blob: 3aa1c3b8edf1d05fa71f3b5f6324452dc0a2e361 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.2">
<meta name="author" content="Luke Daley, Marcin Erdmann, Erik Pragt">
<title>The Book Of Geb</title>
<link rel="stylesheet" href="./css/style.css">
<link rel="stylesheet" href="../../css/font-awesome.min.css">
<link rel="stylesheet" href="../../css/prettify.min.css">
<script src="../../js/prettify.min.js"></script>
<script>document.addEventListener('DOMContentLoaded', prettyPrint)</script><!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setDoNotTrack", true]);
_paq.push(["disableCookies"]);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://analytics.apache.org/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '73']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<!-- End Matomo Code --></head>
<body class="book toc2 toc-left">
<div id="header">
<h1>The Book Of Geb</h1>
<div class="details">
<span id="author" class="author">Luke Daley, Marcin Erdmann, Erik Pragt</span><br>
<span id="revnumber">version 2.0</span>
<br><span id="revremark">Very groovy browser automation… web testing, screen scraping and more</span>
</div>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#introduction">1. Introduction</a>
<ul class="sectlevel2">
<li><a href="#the-browser-automation-technology">1.1. The browser automation technology</a></li>
<li><a href="#the-page-object-pattern">1.2. The Page Object pattern</a></li>
<li><a href="#the-jquery-ish-navigator-api">1.3. The jQuery-ish navigator API</a></li>
<li><a href="#full-examples">1.4. Full examples</a>
<ul class="sectlevel3">
<li><a href="#inline-scripting">1.4.1. Inline scripting</a></li>
<li><a href="#scripting-with-page-objects">1.4.2. Scripting with Page Objects</a></li>
<li><a href="#testing-intro">1.4.3. Testing</a></li>
</ul>
</li>
<li><a href="#installation-usage">1.5. Installation &amp; usage</a>
<ul class="sectlevel3">
<li><a href="#snapshot-repository">Snapshot repository</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#browser">2. The <code>Browser</code></a>
<ul class="sectlevel2">
<li><a href="#drive">2.1. The <code>drive()</code> method</a></li>
<li><a href="#making-requests">2.2. Making requests</a>
<ul class="sectlevel3">
<li><a href="#base-url">2.2.1. The base URL</a></li>
<li><a href="#using-pages">2.2.2. Using pages</a></li>
<li><a href="#direct">2.2.3. Direct</a></li>
</ul>
</li>
<li><a href="#the-page">2.3. The <code>Page</code></a>
<ul class="sectlevel3">
<li><a href="#changing-pages">Changing the page</a></li>
</ul>
</li>
<li><a href="#at-checking">2.4. At checking</a></li>
<li><a href="#page-change-listening">2.5. Page change listening</a></li>
<li><a href="#working-with-multiple-tabs-and-windows">2.6. Working with multiple tabs and windows</a>
<ul class="sectlevel3">
<li><a href="#already-opened-windows">2.6.1. Already opened windows</a>
<ul class="sectlevel4">
<li><a href="#code-withwindow-code-options"><code>withWindow()</code> options</a>
<ul class="sectlevel5">
<li><a href="#code-close-code"><code>close</code></a></li>
<li><a href="#code-page-code"><code>page</code></a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#newly-opened-windows">2.6.2. Newly opened windows</a>
<ul class="sectlevel4">
<li><a href="#code-withnewwindow-code-options"><code>withNewWindow()</code> options</a>
<ul class="sectlevel5">
<li><a href="#code-close-code-2"><code>close</code></a></li>
<li><a href="#code-page-code-2"><code>page</code></a></li>
<li><a href="#code-wait-code"><code>wait</code></a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><a href="#quitting-the-browser">2.7. Quitting the browser</a></li>
</ul>
</li>
<li><a href="#driver">3. The WebDriver implementation</a>
<ul class="sectlevel2">
<li><a href="#driver-management">3.1. Explicit driver management</a></li>
<li><a href="#implicit-driver-management">3.2. Implicit driver management</a></li>
<li><a href="#driver-quirks">3.3. Driver quirks</a></li>
</ul>
</li>
<li><a href="#navigator">4. Interacting with content</a>
<ul class="sectlevel2">
<li><a href="#the-code-code-function">4.1. The <code>$()</code> function</a>
<ul class="sectlevel3">
<li><a href="#css-selectors">4.1.1. CSS Selectors</a></li>
<li><a href="#using-webdriver-s-code-by-code-class-selectors">4.1.2. Using WebDriver&#8217;s <code>By</code> class selectors</a></li>
<li><a href="#indexes-and-ranges">4.1.3. Indexes and ranges</a></li>
<li><a href="#attribute-and-text-matching">4.1.4. Attribute and text matching</a>
<ul class="sectlevel4">
<li><a href="#using-patterns">Using patterns</a></li>
</ul>
</li>
<li><a href="#navigators-are-iterable">4.1.5. Navigators are iterable</a></li>
<li><a href="#code-equals-code-and-code-hashcode-code">4.1.6. <code>equals()</code> and <code>hashCode()</code></a></li>
</ul>
</li>
<li><a href="#finding-filtering">4.2. Finding &amp; filtering</a></li>
<li><a href="#composition">4.3. Composition</a></li>
<li><a href="#traversing">4.4. Traversing</a></li>
<li><a href="#clicking">4.5. Clicking</a></li>
<li><a href="#determining-visibility">4.6. Determining visibility</a></li>
<li><a href="#focus">4.7. Focus</a></li>
<li><a href="#size-and-location">4.8. Size and location</a></li>
<li><a href="#accessing-tag-name-attributes-text-and-classes">4.9. Accessing tag name, attributes, text and classes</a></li>
<li><a href="#css-properties">4.10. CSS properties</a></li>
<li><a href="#sending-keystrokes">4.11. Sending keystrokes</a>
<ul class="sectlevel3">
<li><a href="#non-characters-e-g-delete-key-key-chords-etc">Non characters (e.g. delete key, key chords, etc.)</a></li>
</ul>
</li>
<li><a href="#accessing-input-values">4.12. Accessing input values</a></li>
<li><a href="#form-control-shortcuts">4.13. Form control shortcuts</a></li>
<li><a href="#setting-form-control-values">4.14. Setting form control values</a>
<ul class="sectlevel3">
<li><a href="#select">4.14.1. Select</a></li>
<li><a href="#multiple-select">4.14.2. Multiple select</a></li>
<li><a href="#checkbox">4.14.3. Checkbox</a></li>
<li><a href="#multiple-checkboxes">4.14.4. Multiple checkboxes</a></li>
<li><a href="#radio">4.14.5. Radio</a></li>
<li><a href="#text-inputs-and-textareas">4.14.6. Text inputs and textareas</a></li>
<li><a href="#file-upload">4.14.7. File upload</a></li>
</ul>
</li>
<li><a href="#complex-interactions">4.15. Complex interactions</a>
<ul class="sectlevel3">
<li><a href="#using-actions">4.15.1. Using the WebDriver <code>Actions</code> API directly</a></li>
<li><a href="#interact-closures">4.15.2. Using <code>interact()</code></a></li>
<li><a href="#interact-examples">4.15.3. Interact examples</a>
<ul class="sectlevel4">
<li><a href="#drag-and-drop">Drag and drop</a></li>
<li><a href="#control-clicking">Control-clicking</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><a href="#pages">5. Pages</a>
<ul class="sectlevel2">
<li><a href="#the-page-object-pattern-2">5.1. The Page Object pattern</a></li>
<li><a href="#the-code-page-code-superclass">5.2. The <code>Page</code> superclass</a></li>
<li><a href="#content-dsl">5.3. The content DSL</a>
<ul class="sectlevel3">
<li><a href="#template-options">5.3.1. Template options</a>
<ul class="sectlevel4">
<li><a href="#code-required-code"><code>required</code></a></li>
<li><a href="#content-dsl-min"><code>min</code></a></li>
<li><a href="#content-dsl-max"><code>max</code></a></li>
<li><a href="#content-dsl-times"><code>times</code></a></li>
<li><a href="#code-cache-code"><code>cache</code></a></li>
<li><a href="#content-dsl-to"><code>to</code></a></li>
<li><a href="#content-dsl-wait"><code>wait</code></a></li>
<li><a href="#content-dsl-to-wait"><code>toWait</code></a></li>
<li><a href="#content-dsl-page"><code>page</code></a></li>
</ul>
</li>
<li><a href="#aliasing">5.3.2. Aliasing</a></li>
</ul>
</li>
<li><a href="#at-checker">5.4. “At” verification</a>
<ul class="sectlevel3">
<li><a href="#unexpected-pages">Unexpected pages</a></li>
</ul>
</li>
<li><a href="#page-urls">5.5. Page URLs</a>
<ul class="sectlevel3">
<li><a href="#url-fragments">5.5.1. URL fragments</a></li>
<li><a href="#page-level-at-check-waiting">5.5.2. Page level <code>atCheckWaiting</code> configuration</a></li>
</ul>
</li>
<li><a href="#advanced-page-navigation">5.6. Advanced page navigation</a>
<ul class="sectlevel3">
<li><a href="#named-params">5.6.1. Named params</a></li>
<li><a href="#advanced-page-navigation-url-fragments">5.6.2. URL fragments</a></li>
</ul>
</li>
<li><a href="#parameterized-pages">5.7. Parametrized pages</a></li>
<li><a href="#pages-inheritance">5.8. Inheritance</a></li>
<li><a href="#lifecycle-hooks">5.9. Lifecycle hooks</a>
<ul class="sectlevel3">
<li><a href="#code-onload-page-previouspage-code">5.9.1. <code>onLoad(Page previousPage)</code></a></li>
<li><a href="#code-onunload-page-newpage-code">5.9.2. <code>onUnload(Page newPage)</code></a></li>
</ul>
</li>
<li><a href="#dealing-with-frames">5.10. Dealing with frames</a>
<ul class="sectlevel3">
<li><a href="#executing-code-in-the-context-of-a-frame">5.10.1. Executing code in the context of a frame</a></li>
<li><a href="#switching-pages-and-frames-at-once">5.10.2. Switching pages and frames at once</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#modules">6. Modules</a>
<ul class="sectlevel2">
<li><a href="#base-and-context">6.1. Base and context</a></li>
<li><a href="#code-module-code-is-a-code-navigator-code">6.2. <code>Module</code> is-a <code>Navigator</code></a></li>
<li><a href="#reusing-modules-across-pages">6.3. Reusing modules across pages</a></li>
<li><a href="#repeating-content">6.4. Using modules for repeating content</a></li>
<li><a href="#the-content-dsl">6.5. The content DSL</a></li>
<li><a href="#inheritance">6.6. Inheritance</a></li>
<li><a href="#form-control-modules">6.7. Form control modules</a>
<ul class="sectlevel3">
<li><a href="#form-element">6.7.1. <code>FormElement</code></a></li>
<li><a href="#checkbox-module">6.7.2. <code>Checkbox</code></a></li>
<li><a href="#code-select-code">6.7.3. <code>Select</code></a></li>
<li><a href="#code-multipleselect-code">6.7.4. <code>MultipleSelect</code></a></li>
<li><a href="#code-textinput-code">6.7.5. <code>TextInput</code></a></li>
<li><a href="#code-textarea-code">6.7.6. <code>Textarea</code></a></li>
<li><a href="#code-fileinput-code">6.7.7. <code>FileInput</code></a></li>
<li><a href="#radio-buttons-module">6.7.8. <code>RadioButtons</code></a></li>
</ul>
</li>
<li><a href="#unwrapping-modules-returned-from-the-code-content-code-dsl">6.8. Unwrapping modules returned from the <code>content</code> DSL</a></li>
</ul>
</li>
<li><a href="#configuration">7. Configuration</a>
<ul class="sectlevel2">
<li><a href="#mechanisms">7.1. Mechanisms</a>
<ul class="sectlevel3">
<li><a href="#the-config-script">7.1.1. The config script</a>
<ul class="sectlevel4">
<li><a href="#environment-sensitivity">Environment sensitivity</a></li>
</ul>
</li>
<li><a href="#system-properties">7.1.2. System properties</a></li>
<li><a href="#build-adapter-config">7.1.3. Build adapter</a></li>
</ul>
</li>
<li><a href="#config-options">7.2. Config options</a>
<ul class="sectlevel3">
<li><a href="#driver-implementation-configuration">7.2.1. Driver implementation</a>
<ul class="sectlevel4">
<li><a href="#factory-closure">Factory closure</a></li>
<li><a href="#driver-class-name">Driver class name</a></li>
</ul>
</li>
<li><a href="#navigator-factory">7.2.2. Navigator factory</a></li>
<li><a href="#driver-caching-configuration">7.2.3. Driver caching</a></li>
<li><a href="#base-url">7.2.4. Base URL</a></li>
<li><a href="#waiting-configuration">7.2.5. Waiting</a>
<ul class="sectlevel4">
<li><a href="#default-waiting-configuration">Defaults</a></li>
<li><a href="#waiting-presets">Presets</a></li>
<li><a href="#failure-causes">Failure causes</a></li>
</ul>
</li>
<li><a href="#at-check-waiting">7.2.6. Waiting in “at” checkers</a></li>
<li><a href="#waiting-for-base-navigator">7.2.7. Waiting for base navigator</a></li>
<li><a href="#unexpected-pages-config">7.2.8. Unexpected pages</a></li>
<li><a href="#reporter-configuration">7.2.9. Reporter</a></li>
<li><a href="#reports-directory-configuration">7.2.10. Reports directory</a></li>
<li><a href="#report-test-failures-configuration">7.2.11. Report test failures only</a></li>
<li><a href="#reporting-listener">7.2.12. Reporting listener</a></li>
<li><a href="#auto-clearing-cookies-configuration">7.2.13. Auto clearing cookies</a></li>
</ul>
</li>
<li><a href="#runtime-overrides">7.3. Runtime overrides</a></li>
</ul>
</li>
<li><a href="#implicit-assertions">8. Implicit assertions</a>
<ul class="sectlevel2">
<li><a href="#at-verification">8.1. At verification</a></li>
<li><a href="#implicit-assertions-waiting">8.2. Waiting</a>
<ul class="sectlevel3">
<li><a href="#waiting-content">Waiting content</a></li>
</ul>
</li>
<li><a href="#implicit-assertions-mechanics">8.3. How it works</a></li>
</ul>
</li>
<li><a href="#javascript-ajax-and-dynamic-pages">9. Javascript, AJAX and dynamic pages</a>
<ul class="sectlevel2">
<li><a href="#js-object">9.1. The "js" object</a>
<ul class="sectlevel3">
<li><a href="#accessing-variables">9.1.1. Accessing variables</a></li>
<li><a href="#calling-methods">9.1.2. Calling methods</a></li>
<li><a href="#executing-arbitrary-code">9.1.3. Executing arbitrary code</a></li>
</ul>
</li>
<li><a href="#waiting">9.2. Waiting</a>
<ul class="sectlevel3">
<li><a href="#examples">9.2.1. Examples</a></li>
<li><a href="#custom-message">9.2.2. Custom message</a></li>
</ul>
</li>
<li><a href="#alert-and-confirm-dialogs">9.3. Alert and confirm dialogs</a>
<ul class="sectlevel3">
<li><a href="#alert">9.3.1. <code>alert()</code></a></li>
<li><a href="#code-confirm-code">9.3.2. <code>confirm()</code></a></li>
<li><a href="#code-prompt-code">9.3.3. <code>prompt()</code></a></li>
</ul>
</li>
<li><a href="#jquery-integration">9.4. jQuery integration</a>
<ul class="sectlevel3">
<li><a href="#why">Why?</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#direct-downloading">10. Direct downloading</a>
<ul class="sectlevel2">
<li><a href="#downloading-example">10.1. Downloading example</a></li>
<li><a href="#fine-grained-request">10.2. Fine grained request</a></li>
<li><a href="#dealing-with-untrusted-certificates">10.3. Dealing with untrusted certificates</a></li>
<li><a href="#default-configuration">10.4. Default configuration</a></li>
<li><a href="#errors">10.5. Errors</a></li>
</ul>
</li>
<li><a href="#binding">11. Scripts and binding</a>
<ul class="sectlevel2">
<li><a href="#setup">11.1. Setup</a></li>
<li><a href="#the-binding-environment">11.2. The binding environment</a>
<ul class="sectlevel3">
<li><a href="#browser-methods-and-properties">11.2.1. Browser methods and properties</a></li>
<li><a href="#the-current-page">11.2.2. The current page</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#reporting">12. Reporting</a>
<ul class="sectlevel2">
<li><a href="#report-group">12.1. The report group</a></li>
<li><a href="#listening-to-reporting">12.2. Listening to reporting</a></li>
<li><a href="#cleaning">12.3. Cleaning</a></li>
</ul>
</li>
<li><a href="#testing">13. Testing</a>
<ul class="sectlevel2">
<li><a href="#spock-junit-testng">13.1. Spock, JUnit &amp; TestNG</a>
<ul class="sectlevel3">
<li><a href="#configuration-2">13.1.1. Configuration</a></li>
<li><a href="#testing-reporting">13.1.2. Reporting</a></li>
<li><a href="#cookie-management-in-tests">13.1.3. Cookie management</a></li>
<li><a href="#jar-and-class-names">13.1.4. JAR and class names</a></li>
<li><a href="#example-projects">13.1.5. Example projects</a></li>
</ul>
</li>
<li><a href="#cucumber-jvm">13.2. Cucumber (Cucumber-JVM)</a>
<ul class="sectlevel3">
<li><a href="#writing-your-own-steps">13.2.1. Writing your own steps</a></li>
<li><a href="#using-pre-built-steps">13.2.2. Using pre-built steps</a></li>
<li><a href="#example-project">13.2.3. Example project</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#cloud-browser-testing">14. Cloud browser testing</a>
<ul class="sectlevel2">
<li><a href="#creating-a-driver">14.1. Creating a driver</a>
<ul class="sectlevel3">
<li><a href="#sauce-labs-driver-factory">14.1.1. <code>SauceLabsDriverFactory</code></a></li>
<li><a href="#browser-stack-driver-factory">14.1.2. <code>BrowserStackDriverFactory</code></a></li>
</ul>
</li>
<li><a href="#gradle-plugins">14.2. Gradle plugins</a>
<ul class="sectlevel3">
<li><a href="#geb-saucelabs-plugin">14.2.1. geb-saucelabs plugin</a></li>
<li><a href="#geb-browserstack-plugin">14.2.2. geb-browserstack plugin</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#build-integrations">15. Build system &amp; framework integrations</a>
<ul class="sectlevel2">
<li><a href="#gradle">15.1. Gradle</a></li>
<li><a href="#maven">15.2. Maven</a></li>
</ul>
</li>
<li><a href="#ide-support">16. IDE support</a>
<ul class="sectlevel2">
<li><a href="#execution">16.1. Execution</a></li>
<li><a href="#authoring-assistance-autocomplete-and-navigation">16.2. Authoring assistance (autocomplete and navigation)</a>
<ul class="sectlevel3">
<li><a href="#dynamism-and-conciseness-vs-tooling-support">16.2.1. Dynamism and conciseness vs tooling support</a></li>
<li><a href="#strong-typing">16.2.2. Strong typing</a>
<ul class="sectlevel4">
<li><a href="#intellij-idea-support">IntelliJ IDEA support</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><a href="#about-the-project">17. About the project</a>
<ul class="sectlevel2">
<li><a href="#api-reference">17.1. API reference</a></li>
<li><a href="#support-development">17.2. Support &amp; development</a></li>
<li><a href="#credits">17.3. Credits</a>
<ul class="sectlevel3">
<li><a href="#committers">17.3.1. Committers</a></li>
<li><a href="#contributors">17.3.2. Contributors</a></li>
</ul>
</li>
<li><a href="#history">17.4. History</a>
<ul class="sectlevel3">
<li><a href="#2-0">2.0</a>
<ul class="sectlevel4">
<li><a href="#new-features">New features</a></li>
<li><a href="#fixes">Fixes</a></li>
<li><a href="#improvements">Improvements</a></li>
<li><a href="#breaking-changes">Breaking changes</a></li>
</ul>
</li>
<li><a href="#1-1-1">1.1.1</a>
<ul class="sectlevel4">
<li><a href="#fixes-2">Fixes</a></li>
</ul>
</li>
<li><a href="#1-1">1.1</a>
<ul class="sectlevel4">
<li><a href="#fixes-3">Fixes</a></li>
<li><a href="#improvements-2">Improvements</a></li>
<li><a href="#deprecations">Deprecations</a></li>
</ul>
</li>
<li><a href="#1-0">1.0</a>
<ul class="sectlevel4">
<li><a href="#fixes-4">Fixes</a></li>
<li><a href="#improvements-3">Improvements</a></li>
<li><a href="#breaking-changes-2">Breaking changes</a></li>
</ul>
</li>
<li><a href="#0-13-1">0.13.1</a>
<ul class="sectlevel4">
<li><a href="#fixes-5">Fixes</a></li>
</ul>
</li>
<li><a href="#0-13-0">0.13.0</a>
<ul class="sectlevel4">
<li><a href="#new-features-2">New features</a></li>
<li><a href="#fixes-6">Fixes</a></li>
<li><a href="#improvements-4">Improvements</a></li>
<li><a href="#deprecations-2">Deprecations</a></li>
<li><a href="#breaking-changes-3">Breaking changes</a></li>
</ul>
</li>
<li><a href="#0-12-2">0.12.2</a>
<ul class="sectlevel4">
<li><a href="#fixes-7">Fixes</a></li>
</ul>
</li>
<li><a href="#0-12-1">0.12.1</a>
<ul class="sectlevel4">
<li><a href="#fixes-8">Fixes</a></li>
</ul>
</li>
<li><a href="#0-12-0">0.12.0</a>
<ul class="sectlevel4">
<li><a href="#new-features-3">New features</a></li>
<li><a href="#improvements-5">Improvements</a></li>
<li><a href="#fixes-9">Fixes</a></li>
<li><a href="#breaking-changes-4">Breaking changes</a></li>
<li><a href="#deprecations-3">Deprecations</a></li>
<li><a href="#project-related-changes">Project related changes</a></li>
</ul>
</li>
<li><a href="#0-10-0">0.10.0</a>
<ul class="sectlevel4">
<li><a href="#new-features-4">New features</a></li>
<li><a href="#fixes-10">Fixes</a></li>
<li><a href="#project-related-changes-2">Project related changes</a></li>
<li><a href="#breaking-changes-5">Breaking changes</a></li>
</ul>
</li>
<li><a href="#0-9-3">0.9.3</a>
<ul class="sectlevel4">
<li><a href="#new-features-5">New features</a></li>
<li><a href="#fixes-11">Fixes</a></li>
<li><a href="#breaking-changes-6">Breaking changes</a></li>
<li><a href="#project-related-changes-3">Project related changes</a></li>
</ul>
</li>
<li><a href="#0-9-2">0.9.2</a>
<ul class="sectlevel4">
<li><a href="#new-features-6">New features</a></li>
<li><a href="#fixes-12">Fixes</a></li>
<li><a href="#breaking-changes-7">Breaking changes</a></li>
</ul>
</li>
<li><a href="#0-9-1">0.9.1</a>
<ul class="sectlevel4">
<li><a href="#breaking-changes-8">Breaking changes</a></li>
<li><a href="#new-features-7">New features</a></li>
<li><a href="#fixes-13">Fixes</a></li>
</ul>
</li>
<li><a href="#0-9-0">0.9.0</a>
<ul class="sectlevel4">
<li><a href="#new-features-8">New features</a></li>
<li><a href="#fixes-14">Fixes</a></li>
<li><a href="#breaking-changes-9">Breaking changes</a></li>
</ul>
</li>
<li><a href="#0-7-2">0.7.2</a>
<ul class="sectlevel4">
<li><a href="#fixes-15">Fixes</a></li>
</ul>
</li>
<li><a href="#0-7-1">0.7.1</a>
<ul class="sectlevel4">
<li><a href="#new-features-9">New features</a></li>
<li><a href="#fixes-16">Fixes</a></li>
</ul>
</li>
<li><a href="#0-7-0">0.7.0</a>
<ul class="sectlevel4">
<li><a href="#new-features-10">New features</a></li>
<li><a href="#breaking-changes-10">Breaking changes</a></li>
</ul>
</li>
<li><a href="#0-6-3">0.6.3</a>
<ul class="sectlevel4">
<li><a href="#new-features-11">New features</a></li>
</ul>
</li>
<li><a href="#0-6-2">0.6.2</a>
<ul class="sectlevel4">
<li><a href="#new-features-12">New features</a></li>
<li><a href="#breaking-changes-11">Breaking changes</a></li>
</ul>
</li>
<li><a href="#0-6-1">0.6.1</a>
<ul class="sectlevel4">
<li><a href="#new-features-13">New features</a></li>
<li><a href="#breaking-changes-12">Breaking changes</a></li>
</ul>
</li>
<li><a href="#0-6">0.6</a>
<ul class="sectlevel4">
<li><a href="#new-features-14">New features</a></li>
<li><a href="#breaking-changes-13">Breaking changes</a></li>
</ul>
</li>
<li><a href="#0-5-1">0.5.1</a></li>
<li><a href="#0-5">0.5</a>
<ul class="sectlevel4">
<li><a href="#new-features-15">New features</a></li>
<li><a href="#breaking-changes-14">Breaking changes</a></li>
</ul>
</li>
<li><a href="#0-4">0.4</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="imageblock" style="text-align: center">
<div class="content">
<img src="images/logo.png" alt="Geb">
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="introduction"><a class="link" href="#introduction">1. Introduction</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Geb is a developer focused tool for automating the interaction between web browsers and web content.
It uses the dynamic language features of <a href="http://groovy-lang.org/">Groovy</a> to provide a powerful content definition DSL (for modelling content for reuse) and key concepts from <a href="http://jquery.com/">jQuery</a> to provide a powerful content
inspection and traversal API (for finding and interacting with content).</p>
</div>
<div class="paragraph">
<p>Geb was born out of a desire to make browser automation (originally for web testing) easier and more productive.
It aims to be a <strong>developer tool</strong> in that it allows and encourages the using of programming and language constructs instead of creating a restricted environment.
It uses Groovy&#8217;s dynamism to remove the noise and boiler plate code in order to focus on what&#8217;s important — the content and interaction.</p>
</div>
<div class="sect2">
<h3 id="the-browser-automation-technology"><a class="link" href="#the-browser-automation-technology">1.1. The browser automation technology</a></h3>
<div class="paragraph">
<p>Geb builds on the <a href="http://code.google.com/p/selenium/">WebDriver</a> browser automation library, which means that Geb can work with
<a href="http://docs.seleniumhq.org/docs/01_introducing_selenium.jsp#supported-browsers-and-platforms">any browser that WebDriver can</a>.
While Geb provides an extra layer of convenience and productivity, it is always possible to “drop down” to the WebDriver level to do something directly should you need to.</p>
</div>
<div class="paragraph">
<p>For more information see the manual section on <a href="#driver">using a driver implementation</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="the-page-object-pattern"><a class="link" href="#the-page-object-pattern">1.2. The Page Object pattern</a></h3>
<div class="paragraph">
<p>The Page Object Pattern gives us a common sense way to model content in a reusable and maintainable way.
From the <a href="http://code.google.com/p/selenium/wiki/PageObjects">WebDriver wiki page on the Page Object Pattern</a>:</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>Within your web app&#8217;s UI there are areas that your tests interact with.
A Page Object simply models these as objects within the test code.
This reduces the amount of duplicated code and means that if the UI changes, the fix need only be applied in one place.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>Furthermore (from the same document):</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>PageObjects can be thought of as facing in two directions simultaneously.
Facing towards the developer of a test, they represent the services offered by a particular page.
Facing away from the developer, they should be the only thing that has a deep knowledge of the structure of the HTML of a page (or part of a page) It&#8217;s simplest to think of the methods on a
Page Object as offering the "services" that a page offers rather than exposing the details and mechanics of the page.
As an example, think of the inbox of any web-based email system.
Amongst the services that it offers are typically the ability to compose a new email, to choose to read a single email, and to list the subject lines of the emails in the inbox.
How these are implemented shouldn&#8217;t matter to the test.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>The Page Object Pattern is an important technique, and Geb provides first class support via its <a href="#pages">page</a> and <a href="#modules">module</a> constructs.</p>
</div>
</div>
<div class="sect2">
<h3 id="the-jquery-ish-navigator-api"><a class="link" href="#the-jquery-ish-navigator-api">1.3. The jQuery-ish navigator API</a></h3>
<div class="paragraph">
<p>The <a href="http://jquery.com/">jQuery</a> JavaScript library provides an excellent API for (among other things) selecting or targeting content on a page and traversing through and around content. Geb takes a lot of inspiration from this.</p>
</div>
<div class="paragraph">
<p>In Geb, content is selected through the <code>$</code> function, which returns a <code><a href="api/geb/navigator/Navigator.html">Navigator</a></code> object.
A <code>Navigator</code> object is in someways analogous to the <code>jQuery</code> data type in jQuery in that it represents one or more targeted elements on the page.</p>
</div>
<div class="paragraph">
<p>Let&#8217;s see some examples:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("div") <i class="conum" data-value="1"></i><b>(1)</b>
$("div", 0) <i class="conum" data-value="2"></i><b>(2)</b>
$("div", title: "section") <i class="conum" data-value="3"></i><b>(3)</b>
$("div", 0, title: "section") <i class="conum" data-value="4"></i><b>(4)</b>
$("div.main") <i class="conum" data-value="5"></i><b>(5)</b>
$("div.main", 0) <i class="conum" data-value="6"></i><b>(6)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Match all <code>"div"</code> elements on the page.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Match the first <code>"div"</code> element on the page.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Match all <code>"div"</code> elements with a title attribute value of <code>"section"</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Match the first "div" element with a title attribute value of <code>"section"</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Match all <code>"div"</code> elements who have the class <code>"main"</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Match the first <code>"div"</code> element with the class <code>"main"</code>.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>These methods return <code>Navigator</code> objects that can be used to further refine the content.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("p", 0).parent() <i class="conum" data-value="1"></i><b>(1)</b>
$("p").find("table", cellspacing: '0') <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The parent of the first paragraph.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>All tables with a cellspacing attribute value of 0 that are nested in a paragraph.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This is just the beginning of what is possible with the Navigator API. See the <a href="#navigator">chapter on the navigator</a> for more details.</p>
</div>
</div>
<div class="sect2">
<h3 id="full-examples"><a class="link" href="#full-examples">1.4. Full examples</a></h3>
<div class="paragraph">
<p>Let&#8217;s have a look at a simple case of going to Geb&#8217;s home page and navigating to the latest version of this manual.</p>
</div>
<div class="sect3">
<h4 id="inline-scripting"><a class="link" href="#inline-scripting">1.4.1. Inline scripting</a></h4>
<div class="paragraph">
<p>Here&#8217;s an example of using Geb in an inline (i.e. no page objects or predefined content) scripting style…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.Browser
Browser.drive {
go "http://gebish.org"
assert title == "Geb - Very Groovy Browser Automation" <i class="conum" data-value="1"></i><b>(1)</b>
$("div.menu a.manuals").click() <i class="conum" data-value="2"></i><b>(2)</b>
waitFor { !$("#manuals-menu").hasClass("animating") } <i class="conum" data-value="3"></i><b>(3)</b>
$("#manuals-menu a")[0].click() <i class="conum" data-value="4"></i><b>(4)</b>
assert title.startsWith("The Book Of Geb") <i class="conum" data-value="5"></i><b>(5)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Check that we are at Geb&#8217;s homepage.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Click on the manual menu entry to open it.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Wait for the menu open animation to finish.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Click on the first link to a manual.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Check that we are at The Book of Geb.</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="scripting-with-page-objects"><a class="link" href="#scripting-with-page-objects">1.4.2. Scripting with Page Objects</a></h4>
<div class="paragraph">
<p>This time let us define our content up front using the Page Object pattern…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.Module
import geb.Page
class ManualsMenuModule extends Module { <i class="conum" data-value="1"></i><b>(1)</b>
static content = { <i class="conum" data-value="2"></i><b>(2)</b>
toggle { $("div.menu a.manuals") }
linksContainer { $("#manuals-menu") }
links { linksContainer.find("a") } <i class="conum" data-value="3"></i><b>(3)</b>
}
void open() { <i class="conum" data-value="4"></i><b>(4)</b>
toggle.click()
waitFor { !linksContainer.hasClass("animating") }
}
}
class GebHomePage extends Page {
static url = "http://gebish.org" <i class="conum" data-value="5"></i><b>(5)</b>
static at = { title == "Geb - Very Groovy Browser Automation" } <i class="conum" data-value="6"></i><b>(6)</b>
static content = {
manualsMenu { module(ManualsMenuModule) } <i class="conum" data-value="7"></i><b>(7)</b>
}
}
class TheBookOfGebPage extends Page {
static at = { title.startsWith("The Book Of Geb") }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Modules are reusable fragments that can be used across pages. Here we are using a module to model a dropdown menu.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Content DSL.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Content definitions can use other content definitions to define relative content elements.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Modules can contain methods that allow to hide document structure details or interaction complexity.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Pages can define their location, either absolute or relative to a base.</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>“at checkers” allow verifying that the browser is at the expected page.</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Include the previously defined module.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Now our script again, using the above defined content…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.Browser
Browser.drive {
to GebHomePage <i class="conum" data-value="1"></i><b>(1)</b>
manualsMenu.open()
manualsMenu.links[0].click()
at TheBookOfGebPage
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Go to the url defined by <code>GebHomePage</code> and also verify it&#8217;s “at checker”.</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="testing-intro"><a class="link" href="#testing-intro">1.4.3. Testing</a></h4>
<div class="paragraph">
<p>Geb itself does not include any kind of testing or execution framework.
Rather, it works with existing popular tools like <a href="http://spockframework.org/">Spock</a>, <a href="http://www.junit.org/">JUnit</a>, <a href="http://testng.org/">TestNg</a> and <a href="https://github.com/cucumber/cucumber-jvm">Cucumber-JVM</a>. While Geb works well with all of these test tools, we encourage the use of <a href="http://spockframework.org/">Spock</a> as it&#8217;s a great match for Geb with its focus and style.</p>
</div>
<div class="paragraph">
<p>Here is our Geb homepage case again, this time using Geb&#8217;s <a href="http://spockframework.org/">Spock</a> integration…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.spock.GebSpec
class GebHomepageSpec extends GebSpec {
def "can access The Book of Geb via homepage"() {
given:
to GebHomePage
when:
manualsMenu.open()
manualsMenu.links[0].click()
then:
at TheBookOfGebPage
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>For more information on using Geb for web and functional testing, see the <a href="#testing">testing chapter</a>.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="installation-usage"><a class="link" href="#installation-usage">1.5. Installation &amp; usage</a></h3>
<div class="paragraph">
<p>Geb itself is a available as a single <a href="http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.gebish%22%20AND%20a%3A%22geb-core%22"><code>geb-core</code> jar from the central Maven repository</a>. To get up and running you simply need this jar, a WebDriver driver implementation and the <code>selenium-support</code> jar.</p>
</div>
<div class="paragraph">
<p>Via <code>@Grab</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">@Grapes([
@Grab("org.gebish:geb-core:2.0"),
@Grab("org.seleniumhq.selenium:selenium-firefox-driver:3.6.0"),
@Grab("org.seleniumhq.selenium:selenium-support:3.6.0")
])
import geb.Browser</code></pre>
</div>
</div>
<div class="paragraph">
<p>Via Maven…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-xml" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.gebish&lt;/groupId&gt;
&lt;artifactId&gt;geb-core&lt;/artifactId&gt;
&lt;version&gt;2.0&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.seleniumhq.selenium&lt;/groupId&gt;
&lt;artifactId&gt;selenium-firefox-driver&lt;/artifactId&gt;
&lt;version&gt;3.6.0&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.seleniumhq.selenium&lt;/groupId&gt;
&lt;artifactId&gt;selenium-support&lt;/artifactId&gt;
&lt;version&gt;3.6.0&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Via Gradle…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">compile "org.gebish:geb-core:2.0"
compile "org.seleniumhq.selenium:selenium-firefox-driver:3.6.0"
compile "org.seleniumhq.selenium:selenium-support:3.6.0"</code></pre>
</div>
</div>
<div class="paragraph">
<p>Alternatively, if using an integration such as <code>geb-spock</code> or <code>geb-junit4</code> you can depend on that instead of <code>geb-core</code>. You can check out <a href="http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.gebish%22">the listing of all artifacts in <code>org.gebish</code> group</a> to see what&#8217;s available.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Be sure to check the chapter on <a href="#build-integrations">build integrations</a> for information on using Geb with particular environments.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="snapshot-repository"><a class="link" href="#snapshot-repository">Snapshot repository</a></h4>
<div class="paragraph">
<p>If you fancy living on the bleeding edge then you can try out Geb&#8217;s snapshot artifacts located in <a href="https://oss.sonatype.org/content/repositories/snapshots/org/gebish">the Maven repository at https://oss.sonatype.org/content/repositories/snapshots</a>.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="browser"><a class="link" href="#browser">2. The <code>Browser</code></a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>The entry point to Geb is the <code><a href="api/geb/Browser.html">Browser</a></code> object.
A browser object marries a <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/WebDriver.html">WebDriver</a></code> instance (which drives the actual web browser being automated) with the concept of a “current page”.</p>
</div>
<div class="paragraph">
<p>Browser objects are created with a <code><a href="api/geb/Configuration.html">Configuration</a></code> that specifies which driver implementation to use, the base URL to resolve relative links against and other bits of config.
The configuration mechanism allows you to externalise how Geb should operate, which means you can use the same suite of Geb code or tests with different browsers or site instances.
The <a href="#configuration">chapter on configuration</a> contains more details on how to manage the configuration parameters and what they are.</p>
</div>
<div class="paragraph">
<p>The default constructor of <code><a href="api/geb/Browser.html">Browser</a></code> simply loads its settings from the config mechanism.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.Browser
def browser = new Browser()</code></pre>
</div>
</div>
<div class="paragraph">
<p>However, if you prefer to specify the driver implementation (or any other settable property on the <code><a href="api/geb/Browser.html">Browser</a></code>) you can by using Groovy&#8217;s map constructor syntax.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.Browser
import org.openqa.selenium.htmlunit.HtmlUnitDriver
def browser = new Browser(driver: new HtmlUnitDriver())</code></pre>
</div>
</div>
<div class="paragraph">
<p>Which is the same as…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def browser = new Browser()
browser.driver = new HtmlUnitDriver()</code></pre>
</div>
</div>
<div class="paragraph">
<p>Any property set this way will <strong>override</strong> any settings coming from the config mechanism.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The behaviour is undefined if a browser&#8217;s driver is changed after its first use, so you should avoid setting the driver this way and prefer the configuration mechanism.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>For drastically custom configuration requirements, you can create your own <code><a href="api/geb/Configuration.html">Configuration</a></code> object and construct the browser with it,
likely using the <a href="api/geb/ConfigurationLoader.html">configuration loader</a>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.Browser
import geb.ConfigurationLoader
def loader = new ConfigurationLoader("a-custom-environment")
def browser = new Browser(loader.conf)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Wherever possible, you should strive to use the no-arg constructor and manage Geb through the inbuilt <a href="#configuration">configuration mechanism</a> as it offers a great deal of flexibility
and separates your configuration from your code.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Geb integrations typically remove the need to construct a browser object and do this for you, leaving you to just manage the configuration.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="drive"><a class="link" href="#drive">2.1. The <code>drive()</code> method</a></h3>
<div class="paragraph">
<p>The Browser class features a static method, <code><a href="api/geb/Browser.html#drive(groovy.lang.Closure)">drive()</a></code>, that makes Geb scripting a little more convenient.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go "signup"
assert $("h1").text() == "Signup Page"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Which is equivalent to:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def browser = new Browser()
browser.go "signup"
assert browser.$("h1").text() == "Signup Page"</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>drive()</code> method takes all of the arguments that the <code><a href="api/geb/Browser.html">Browser</a></code> constructor takes (i.e. none, a <code><a href="api/geb/Configuration.html">Configuration</a></code> and/or property overrides) or an existing browser instance,
and a closure.
The closure is evaluated against created browser instance (i.e. the browser is made the <em>delegate</em> of the closure).
The net result is that all top level method calls and property accesses are implied to be against the browser.</p>
</div>
<div class="paragraph">
<p>The <code>drive()</code> method always returns the browser object that was used, so if you need to quit the browser after the drive session you can do something like…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
//…
}.quit()</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>For more on when/why you need to manually quit the browser, see the section on the <a href="#driver">driver</a>.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="making-requests"><a class="link" href="#making-requests">2.2. Making requests</a></h3>
<div class="sect3">
<h4 id="base-url"><a class="link" href="#base-url">2.2.1. The base URL</a></h4>
<div class="paragraph">
<p>Browser instances maintain a <a href="api/geb/Browser.html#getBaseUrl()"><code>baseUrl</code></a> property that is used to resolve all relative URLs.
This value can come from <a href="#base-url">configuration</a> or can be <a href="api/geb/Browser.html#setBaseUrl(java.lang.String)">explicitly set</a> on the browser.</p>
</div>
<div class="paragraph">
<p>Care must be taken with slashes when specifying both the base URL and the relative URL as trailing and leading slashes have significant meaning.
The following table illustrates the resolution of different types of URLs.</p>
</div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 33%;">
<col style="width: 33%;">
<col style="width: 33%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Base</th>
<th class="tableblock halign-left valign-top">Navigating To</th>
<th class="tableblock halign-left valign-top">Result</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">abc</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/abc</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">abc</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.comabc</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">/abc</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/abc</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/abc/</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">def</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/abc/def</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/abc</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">def</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/def</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/abc/</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">/def</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/def</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/abc/def/</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">jkl</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/abc/def/jkl</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/abc/def</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">jkl</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/abc/jkl</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/abc/def</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">/jkl</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">http://myapp.com/jkl</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>It is usually most desirable to define your base urls with trailing slashes and not to use leading slashes on relative URLs.</p>
</div>
</div>
<div class="sect3">
<h4 id="using-pages"><a class="link" href="#using-pages">2.2.2. Using pages</a></h4>
<div class="paragraph">
<p>Page objects (<a href="#the-page">discussed further shortly</a>) can define a url that will be used when explicitly navigating to that page.
This is done with the <a href="api/geb/Browser.html#to(java.util.Map,%20Class%3CT%3E,%20java.lang.Object)"><code>to()</code></a> and <a href="api/geb/Browser.html#via(java.util.Map,%20Class%3CT%3E,%20java.lang.Object)"><code>via()</code></a> methods.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class SignupPage extends Page {
static url = "signup"
}
Browser.drive {
to SignupPage
assert $("h1").text() == "Signup Page"
assert page instanceof SignupPage
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>to()</code> and <code>via()</code> method makes a request to the resolved URL and sets the browser&#8217;s page instance to an instance of the given class. Most Geb scripts and tests start with a <code>to()</code> or <code>via()</code> call.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>See the section on <a href="#advanced-page-navigation">advanced page navigation</a> for more information on how to use more complicated URL resolution for pages.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="direct"><a class="link" href="#direct">2.2.3. Direct</a></h4>
<div class="paragraph">
<p>You can also make a new request to a URL without setting or changing the page using the <a href="api/geb/Browser.html#go(java.lang.String)"><code>go()</code></a> methods.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.Page
import geb.spock.GebSpec
class GoogleHomePage extends Page {
static url = "http://www.google.com"
}
class GoogleSpec extends GebSpec {
def "go method does NOT set the page"() {
given:
Page oldPage = page
when:
go "http://www.google.com"
then:
oldPage == page
currentUrl.contains "google"
}
def "to method does set the page and change the current url"() {
given:
Page oldPage = page
when:
to GoogleHomePage
then:
oldPage != page
currentUrl.contains "google"
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following examples use a baseUrl of “<code>http://localhost/</code>”.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go() <i class="conum" data-value="1"></i><b>(1)</b>
go "signup" <i class="conum" data-value="2"></i><b>(2)</b>
go "signup", param1: "value1", param2: "value2" <i class="conum" data-value="3"></i><b>(3)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Go to the base URL.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Go to a URL relative to base URL.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Go to a URL with request params i.e <code>http://localhost/signup?param1=value1&amp;param2=value2</code></td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="the-page"><a class="link" href="#the-page">2.3. The <code>Page</code></a></h3>
<div class="paragraph">
<p>Browser instances hold a reference to a <em>page</em>. This page instance is retrievable via the <a href="api/geb/Browser.html#getPage()"><code>page</code></a> property.
Initially, all browser instances have a page of type <a href="api/geb/Page.html"><code>Page</code></a> which provides the basic navigation functions and is the superclass for all page objects.</p>
</div>
<div class="paragraph">
<p>However, the page property is rarely accessed directly.
The browser object will <em>forward</em> any method calls or property read/writes that it can&#8217;t handle to the current page instance.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go "signup"
assert $("h1").text() == "Signup Page" <i class="conum" data-value="1"></i><b>(1)</b>
assert page.$("h1").text() == "Signup Page" <i class="conum" data-value="1"></i><b>(1)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>These two calls are equivalent.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The <em>page</em> is providing the <code>$()</code> function, not the browser. This forwarding facilitates very concise code, void of unnecessary noise.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>For more information on the <code>$()</code> function which is used to interact with page content, see the section on the <a href="#navigator">Navigator API</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>When using the Page Object pattern, you create subclasses of <a href="api/geb/Page.html"><code>Page</code></a> that define content via a powerful DSL that allows you to refer to content by meaningful names instead of
tag names or CSS expressions.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class SignupPage extends Page {
static url = "signup"
static content = {
heading { $("h1").text() }
}
}
Browser.drive {
to SignupPage
assert heading == "Signup Page"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Page objects are discussed in depth in the <a href="#pages">Pages</a> chapter, which also explores the Content DSL.</p>
</div>
<div class="sect3">
<h4 id="changing-pages"><a class="link" href="#changing-pages">Changing the page</a></h4>
<div class="paragraph">
<p>We have already seen that that <code>to()</code> methods change the browser&#8217;s page instance.
It is also possible to change the page instance without initiating a new request with the <code>page()</code> methods.</p>
</div>
<div class="paragraph">
<p>The <code><a href="api/geb/Browser.html#page(Class&lt;T&gt;)">&lt;T extends Page&gt; T page(Class&lt;T&gt; pageType)</a></code> method allows you to change the page to a new instance of <strong>the given class</strong>.
The class must be <code><a href="api/geb/Page.html">Page</a></code> or a subclass thereof.
This method <strong>does not</strong> verify that the given page actually matches the content (at checking is discussed shortly).</p>
</div>
<div class="paragraph">
<p>The <code><a href="api/geb/Browser.html#page(T)">&lt;T extends Page&gt; T page(T pageInstance)</a></code> method allows you to change the page to <strong>the given instance</strong>.
Similarly to the method taking a page class it <strong>does not</strong> verify that the given page actually matches the content.</p>
</div>
<div class="paragraph">
<p>The <code><a href="api/geb/Browser.html#page(Class&lt;?%20extends%20Page&gt;)">Page page(Class&lt;? extends Page&gt;[] potentialPageTypes)</a></code> method allows you to specify a number of <em>potential</em> page types.
Each of the potential pages is instantiated and checked to see if it matches the content the browser is actually currently at by running each page&#8217;s at checker.
All of the page classes passed in must have an “at” checker defined otherwise an <code>UndefinedAtCheckerException</code> will be thrown.</p>
</div>
<div class="paragraph">
<p>The <code><a href="api/geb/Browser.html#page(geb.Page)">Page page(Page[] potentialPageInstances)</a></code> method allows you to specify a number of <em>potential</em> page instances.
Each of the potential page instances is initialized and checked to see if it matches the content the browser is actually currently at by running each pages at checker.
All of the page instances passed in must have an “at” checker defined otherwise an <code>UndefinedAtCheckerException</code> will be thrown.</p>
</div>
<div class="paragraph">
<p>These two methods taking arrays as arguments are not typically used explicitly but are used by the <code>to()</code> method and content definitions that specify the page that the content navigates to when
clicked (see the section on the <a href="#content-dsl-to"><code>to</code> attribute of the Content DSL</a> for more information about this).
However, should you need to manually change the page type, they are there.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="at-checking"><a class="link" href="#at-checking">2.4. At checking</a></h3>
<div class="paragraph">
<p>Pages define an <a href="#at-checker">“at checker”</a> that the browser uses for checking if it is pointing at a given page.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class SignupPage extends Page {
static url = "signup"
static at = {
$("h1").text() == "Signup Page"
}
}
Browser.drive {
to SignupPage
}</code></pre>
</div>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Not using explicit <code>return</code> statements in “at checkers” is preferred.
Geb transforms all “at checkers” so that each statement in them is asserted (just like for <code>then:</code> blocks in Spock specifications).
Thanks to that you can immediately see evaluated values of your “at checker” if it fails.
See the <a href="#at-checker">“at checker”</a> section for more details.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The <code>to()</code> method that takes a single page type or instance <strong>verifies</strong> that the the browser ends up at the given page.
If the request may initiate a redirect and take the browser to a different page you should use <code><a href="api/geb/Browser.html#via(java.util.Map,%20Class&lt;T&gt;,%20java.lang.Object)">via()</a></code> method:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
via SecurePage
at AccessDeniedPage
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Browser objects have <code><a href="api/geb/Browser.html#at(Class&lt;T&gt;)">&lt;T extends Page&gt; T at(Class&lt;T&gt; pageType)</a></code> and <code><a href="api/geb/Browser.html#at(T)">&lt;T extends Page&gt; T at(T page)</a></code> methods that test whether or not the browser is currently at the page modeled by the given page
class or instance.</p>
</div>
<div class="paragraph">
<p>The <code>at AccessDeniedPage</code> method call return a page instance if the “at checker” is fulfilled.
If on the other hand it is not then an <code>AssertionError</code> will be thrown even if there are no explicit assertions in the “at checker” (the default, see the section on <a href="#implicit-assertions">Implicit assertions</a> for
details) or return null if implicit assertions are disabled.</p>
</div>
<div class="paragraph">
<p>It&#8217;s always a good idea to either use the <code>to()</code> method which implicitly verifies the “at checker” or the <code>via()</code> method followed by an <code>at()</code> check whenever the page changes in order to <em>fail fast</em>.
Otherwise, subsequent steps may fail in harder to diagnose ways due to the content not matching what is expected and content look-ups returning strange results.</p>
</div>
<div class="paragraph">
<p>If you pass a page class or instance that doesn&#8217;t define an “at checker” to <code>at()</code> you will get an <code>UndefinedAtCheckerException</code> - “at checkers” are mandatory when doing explicit “at checks”.
This is not the case when implicit “at checks” are being performed, like when using <code>to()</code>.
This behaviour is intended to make you aware that you probably want to define an “at checker” when explicitly verifying if you&#8217;re at a given page but not forcing you to do so when using implicit
“at checking”.</p>
</div>
<div class="paragraph">
<p>The <code>at()</code> method will also update the browser&#8217;s page instance if the “at checker” is successful.
This means that you don&#8217;t have to manually set browser&#8217;s page to the new one after “at checking”.</p>
</div>
<div class="paragraph">
<p>Pages can also define content that declares what the browser&#8217;s page type should change to when that content is clicked.
After clicking on such content the declared page is automatically ”at checked” if it defines an “at checker” (see the content DSL reference for the <a href="#content-dsl-to"><code>to</code></a> parameter).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class LoginPage extends Page {
static url = "/login"
static content = {
loginButton(to: AdminPage) { $("input", type: "submit") }
username { $("input", name: "username") }
password { $("input", name: "password") }
}
}
class AdminPage extends Page {
static at = {
$("h1").text() == "Admin Page"
}
}
Browser.drive {
to LoginPage
username.value("admin")
password.value("p4sw0rd")
loginButton.click()
assert page instanceof AdminPage
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="page-change-listening"><a class="link" href="#page-change-listening">2.5. Page change listening</a></h3>
<div class="paragraph">
<p>It is possible to be notified when a browser&#8217;s page <em>instance</em> changes (note that this is not necessarily when the browser makes a request to a new URL) using the
<a href="api/geb/PageChangeListener.html"><code>PageChangeListener</code></a> interface.</p>
</div>
<div class="paragraph">
<p>As soon as a listener is registered, its <code>pageWillChange()</code> method will be called with <code>newPage</code> as the current page and <code>oldPage</code> as <code>null</code>.
Subsequently, each time the page changes <code>oldPage</code> will be the page that the browser currently has, and <code>newPage</code> will be the page that will soon be the browser&#8217;s page.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class RecordingPageChangeListener implements PageChangeListener {
Page oldPage
Page newPage
@Override
void pageWillChange(Browser browser, Page oldPage, Page newPage) {
this.oldPage = oldPage
this.newPage = newPage
}
}
class FirstPage extends Page {
}
class SecondPage extends Page {
}
def listener = new RecordingPageChangeListener()
def browser = new Browser()
browser.page(FirstPage)
browser.registerPageChangeListener(listener)
assert listener.oldPage == null
assert listener.newPage instanceof FirstPage
browser.page(SecondPage)
assert listener.oldPage instanceof FirstPage
assert listener.newPage instanceof SecondPage</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can remove a listener at any time…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">browser.removePageChangeListener(listener)</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <a href="api/geb/Browser.html#removePageChangeListener(geb.PageChangeListener)"><code>removePageChangeListener(PageChangeListener listener)</code></a> returns <code>true</code> if <code>listener</code> was registered and has now been
removed, otherwise it returns <code>false</code>.</p>
</div>
<div class="paragraph">
<p>Listeners cannot be registered twice.
If an attempt is made to register a listener that is already registered (i.e. there is another listener that is <em>equal</em> to the listener being registered, based on their <code>equals()</code> implementation)
then a <a href="api/geb/error/PageChangeListenerAlreadyRegisteredException.html"><code>PageChangeListenerAlreadyRegisteredException</code></a> will be raised.</p>
</div>
</div>
<div class="sect2">
<h3 id="working-with-multiple-tabs-and-windows"><a class="link" href="#working-with-multiple-tabs-and-windows">2.6. Working with multiple tabs and windows</a></h3>
<div class="paragraph">
<p>When you&#8217;re working with an application that opens new windows or tabs, for example when clicking on a link with a target attribute set, you can use <code>withWindow()</code> and <code>withNewWindow()</code> methods to
execute code in the context of other windows.</p>
</div>
<div class="paragraph">
<p>If you really need to know the name of the current window or all the names of open windows use <a href="api/geb/Browser.html#getCurrentWindow()"><code>Browser.getCurrentWindow()</code></a> and
<a href="api/geb/Browser.html#getAvailableWindows()"><code>Browser.getAvailableWindows()</code></a> methods but <code>withWindow()</code> and <code>withNewWindow()</code> are the preferred methods when it comes to dealing with multiple
windows.</p>
</div>
<div class="sect3">
<h4 id="already-opened-windows"><a class="link" href="#already-opened-windows">2.6.1. Already opened windows</a></h4>
<div class="paragraph">
<p>If you know the name of the window in context of which you want to execute the code you can use
<code><a href="api/geb/Browser.html#withWindow(java.lang.String,%20groovy.lang.Closure)">withWindow(String windowName, Closure block)</a></code>.</p>
</div>
<div class="paragraph">
<p>Given that this HTML is rendered for the <code>baseUrl</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;a href="http://www.gebish.org" target="myWindow"&gt;Geb&lt;/a&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>This code passes:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go()
$("a").click()
withWindow("myWindow") {
assert title == "Geb - Very Groovy Browser Automation"
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you don&#8217;t know the name of the window but you know something about the content of the window you can use the
<code><a href="api/geb/Browser.html#withWindow(groovy.lang.Closure,%20groovy.lang.Closure)">withWindow(Closure specification, Closure block)</a></code> method.
The first closure passed should return true for the window, or windows, you want to use as context for execution of the second closure.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If there is no window for which the window specification closure returns <code>true</code> then
<a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/NoSuchWindowException.html"><code>NoSuchWindowException</code></a> is thrown.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>So given:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;a href="http://www.gebish.org" target="_blank"&gt;Geb&lt;/a&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>This code passes:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go()
$("a").click()
withWindow({ title == "Geb - Very Groovy Browser Automation" }) {
assert $(".slogan").text().startsWith("Very Groovy browser automation.")
}
}</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If code of the closure passed as the last argument changes browser&#8217;s current page instance
(e.g. by using <a href="api/geb/Browser.html#page(Class&lt;T&gt;)"><code>page(Page)</code></a> or <a href="api/geb/Browser.html#at(Class&lt;T&gt;)"><code>at(Page)</code></a>) then it will be reverted to its original value after returning from
<code>withWindow()</code>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect4">
<h5 id="code-withwindow-code-options"><a class="link" href="#code-withwindow-code-options"><code>withWindow()</code> options</a></h5>
<div class="paragraph">
<p>There are some additional options that can be passed to a <a href="api/geb/Browser.html#withWindow(java.util.Map,%20groovy.lang.Closure,%20groovy.lang.Closure)"><code>withWindow()</code></a> call which make working
with already opened windows even simpler. The general syntax is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">withWindow(«window specification», «option name»: «option value», ...) {
«action executed within the context of the window»
}</code></pre>
</div>
</div>
<div class="sect5">
<h6 id="code-close-code"><a class="link" href="#code-close-code"><code>close</code></a></h6>
<div class="paragraph">
<p>Default value: <code>false</code></p>
</div>
<div class="paragraph">
<p>If you pass any <em>truthy</em> value as the <code>close</code> option then all matching windows will be closed after the execution of the closure passed as the last argument to a <code>withWindow()</code> call.</p>
</div>
</div>
<div class="sect5">
<h6 id="code-page-code"><a class="link" href="#code-page-code"><code>page</code></a></h6>
<div class="paragraph">
<p>Default value: <code>null</code></p>
</div>
<div class="paragraph">
<p>If you pass a class or an instance of a class that extends <code>Page</code> as <code>page</code> option, then browser&#8217;s page will be set to that value before executing the closure passed as the last argument and will be
reverted to its original value afterwards. If the page class defines an “at checker” then it will be verified when the page is set on the browser.</p>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="newly-opened-windows"><a class="link" href="#newly-opened-windows">2.6.2. Newly opened windows</a></h4>
<div class="paragraph">
<p>If you wish to execute code in a window that is newly opened by some of your actions, use the
<code><a href="api/geb/Browser.html#withNewWindow(groovy.lang.Closure,%20groovy.lang.Closure)">withNewWindow(Closure windowOpeningBlock, Closure block)</a></code> method.
Given that this HTML is rendered for the <code>baseUrl</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;a href="http://www.gebish.org" target="_blank"&gt;Geb&lt;/a&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following will pass:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go()
withNewWindow({ $('a').click() }) {
assert title == 'Geb - Very Groovy Browser Automation'
}
}</code></pre>
</div>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If the first parameter opens none or more than one window, then <a href="api/geb/error/NoNewWindowException.html"><code>NoNewWindowException</code></a> is thrown.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If code of the closure passed as the last argument changes browser&#8217;s current page instance (e.g. by using <a href="api/geb/Browser.html#page(Class&lt;T&gt;)"><code>page(Page)</code></a> or
<a href="api/geb/Browser.html#page(Class&lt;T&gt;)"><code>at(Page)</code></a>) then it will be reverted to its original value after returning from <code>withNewWindow()</code>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect4">
<h5 id="code-withnewwindow-code-options"><a class="link" href="#code-withnewwindow-code-options"><code>withNewWindow()</code> options</a></h5>
<div class="paragraph">
<p>There are several options that can be passed to a <a href="api/geb/Browser.html#withNewWindow(java.util.Map,%20groovy.lang.Closure,%20groovy.lang.Closure)"><code>withNewWindow()</code></a> call which make working
with newly opened windows even simpler. The general syntax is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">withNewWindow({ «window opening action» }, «option name»: «option value», ...) {
«action executed within the context of the window»
}</code></pre>
</div>
</div>
<div class="sect5">
<h6 id="code-close-code-2"><a class="link" href="#code-close-code-2"><code>close</code></a></h6>
<div class="paragraph">
<p>Default value: <code>true</code></p>
</div>
<div class="paragraph">
<p>If you pass any <em>truthy</em> value as <code>close</code> option then the newly opened window will be closed after the execution of the closure passed as the last argument to the <code>withNewWindow()</code> call.</p>
</div>
</div>
<div class="sect5">
<h6 id="code-page-code-2"><a class="link" href="#code-page-code-2"><code>page</code></a></h6>
<div class="paragraph">
<p>Default value: <code>null</code></p>
</div>
<div class="paragraph">
<p>If you pass a class or an instance of a class that extends <code>Page</code> as <code>page</code> option then browser&#8217;s page will be set to that value before executing the closure passed as the last argument and will be
reverted to its original value afterwards.</p>
</div>
</div>
<div class="sect5">
<h6 id="code-wait-code"><a class="link" href="#code-wait-code"><code>wait</code></a></h6>
<div class="paragraph">
<p>Default value: <code>null</code></p>
</div>
<div class="paragraph">
<p>You can specify <code>wait</code> option if the action defined in the window opening closure passed as the first argument is asynchronous and you need to wait for the new window to be opened.
The possible values for the <code>wait</code> option are consistent with the <a href="#content-dsl-wait">ones for <code>wait</code> option of content definitions</a>.</p>
</div>
<div class="paragraph">
<p>Given that the following HTML is rendered for the <code>baseUrl</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;a href="http://google.com" target="_blank" id="new-window-link"&gt;Google&lt;/a&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>together with the following javascript:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-javascript" data-lang="javascript">function openNewWindow() {
setTimeout(function() {
document.getElementById('new-window-link').click();
}, 200);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>then the following will pass:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go()
withNewWindow({ js.openNewWindow() }, wait: true) {
assert title == 'Google'
}
}</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="quitting-the-browser"><a class="link" href="#quitting-the-browser">2.7. Quitting the browser</a></h3>
<div class="paragraph">
<p>The browser object has <a href="api/geb/Browser.html#quit()"><code>quit()</code></a> and <a href="api/geb/Browser.html#close()"><code>close()</code></a> methods (that simply delegate to the underlying driver).
See the section on <a href="#driver-management">driver management</a> for more information on when and why you need to quit the browser.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="driver"><a class="link" href="#driver">3. The WebDriver implementation</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>A <code><a href="api/geb/Browser.html">Browser</a></code> instance interacts with an actual browser via an instance of <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/WebDriver.html">WebDriver</a></code>.
The browser&#8217;s driver can always be retrieved via the <code><a href="api/geb/Browser.html#getDriver()">getDriver()</a></code> method.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>One of the key design principles that WebDriver embraces is that tests/scripts should be written to the <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/WebDriver.html">WebDriver</a></code> API making them agnostic to the actual browser being driven,
and therefore portable.
Geb always supports this goal.
However, the reality is that there are still quirks and behavioural differences between driver implementations.
Each release of WebDriver has historically worked to minimise these issues so expect the situation to improve over time as WebDriver matures.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="driver-management"><a class="link" href="#driver-management">3.1. Explicit driver management</a></h3>
<div class="paragraph">
<p>One option for specifying the driver implementation is to construct the driver instance and pass it to the <code>Browser</code> to be used <a href="#browser">when it is constructed</a>.</p>
</div>
<div class="paragraph">
<p>However, where possible prefer implicit driver management which is discussed later in this chapter.</p>
</div>
<h4 id="explicit-driver-lifecycle" class="discrete">Explicit lifecycle</h4>
<div class="paragraph">
<p>When the driver is constructed by the user, the user is responsible for quitting the driver at the appropriate time.
This can be done via the methods on the <code>WebDriver</code> instance (obtainable via <code><a href="api/geb/Browser.html#getDriver()">getDriver()</a></code>) or by calling the <a href="#quitting-the-browser">delegating methods on the browser object</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="implicit-driver-management"><a class="link" href="#implicit-driver-management">3.2. Implicit driver management</a></h3>
<div class="paragraph">
<p>If a driver is not given when a <code>Browser</code> object is constructed, one will be created and managed implicitly by Geb by the the <a href="#driver-implementation-configuration">configuration mechanism</a>.</p>
</div>
<h4 id="implicit-driver-lifecycle" class="discrete">Implicit lifecycle</h4>
<div class="paragraph">
<p>By default, Geb internally caches and reuses the first driver created, meaning that all subsequent browser instances created without an explicit driver will reuse the cached driver.
This avoids the overhead of creating a new driver each time, which can be significant when working with a real browser.</p>
</div>
<div class="paragraph">
<p>This means that you may need to call the <code><a href="api/geb/Browser.html#clearCookies()">clearCookies()</a></code> or <code><a href="api/geb/Browser.html#clearCookies(java.lang.String)">clearCookies(String&#8230;&#8203; additionalUrls)</a></code> method on the browser in order not to get strange results due to cookies from previous executions.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Some of the integrations (e.g. Spock, JUnit) automatically clear the browser cookies for the current domain at appropriate times such as after each test.
Consult the section on <a href="#cookie-management-in-tests">testing</a> for specifics.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The shared driver will be closed and quited when the JVM shuts down.</p>
</div>
<div class="paragraph">
<p>A new driver can be forced at anytime by calling either of <code><a href="api/geb/driver/CachingDriverFactory.html#clearCache()">CachingDriverFactory.clearCache()</a></code> or <code><a href="api/geb/driver/CachingDriverFactory.html#clearCacheAndQuitDriver()">CachingDriverFactory.clearCacheAndQuitDriver()</a></code> both of which are <code>static</code>.
After calling any of these methods, the next request for a default driver will result in a new driver instance being created.</p>
</div>
<div class="paragraph">
<p>This caching behavior is <a href="#driver-caching-configuration">configurable</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="driver-quirks"><a class="link" href="#driver-quirks">3.3. Driver quirks</a></h3>
<div class="paragraph">
<p>This section details various quirks or issues that have been encountered with different driver implementations.</p>
</div>
<h4 id="code-htmlunitdriver-code" class="discrete"><code>HTMLUnitDriver</code></h4>
<h5 id="dealing-with-pages-that-use-html-refreshes" class="discrete">Dealing with pages that use HTML refreshes</h5>
<div class="paragraph">
<p>The default behaviour of the HTMLUnit driver is to immediately refresh the page as soon as it encounters a <code>&lt;meta http-equiv="refresh" content="5"&gt;</code> regardless of the specified time.
The solution is to use a refresh handler that handles the refresh asynchronously.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import com.gargoylesoftware.htmlunit.ThreadedRefreshHandler
Browser.drive {
driver.webClient.refreshHandler = new ThreadedRefreshHandler()
<i class="conum" data-value="1"></i><b>(1)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>From here on refresh meta tag value will be respected.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>See <a href="https://web.archive.org/web/20120211213811/http://markmail.org/thread/bu3g56oxz2uqzq43">this mailing list thread</a> for details.</p>
</div>
<h5 id="configuring-logging" class="discrete">Configuring logging</h5>
<div class="paragraph">
<p>HTMLUnit can be very noisy, and it&#8217;s not clear how to make it not so noisy.</p>
</div>
<div class="paragraph">
<p>See <a href="https://github.com/geb/issues/issues/98">this issue</a> for some tips on how to tune its logging.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="navigator"><a class="link" href="#navigator">4. Interacting with content</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Geb provides a concise and Groovy interface to the content and controls in your browser.
This is implemented through the <code>Navigator</code> API which is a jQuery inspired mechanism for finding, filtering and interacting with DOM elements.</p>
</div>
<div class="sect2">
<h3 id="the-code-code-function"><a class="link" href="#the-code-code-function">4.1. The <code>$()</code> function</a></h3>
<div class="paragraph">
<p>The <code>$()</code> function is the access point to the browser&#8217;s page content.
It returns a <code><a href="api/geb/navigator/Navigator.html">Navigator</a></code> object that is roughly analogous to a jQuery object.
It is analogous in that it represents one or more elements on the page and can be used to refine the matched content or query the matched content.
When a <code>$()</code> function is called that does not match any content, an “empty” navigator object is returned that represents no content.
Operations on “empty” navigators return <code>null</code> or another “empty” navigator or other values that make sense (e.g. the <code>size()</code> method returns <code>0</code>).</p>
</div>
<div class="paragraph">
<p>The signature of the <code>$()</code> function is as follows…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$(«css selector», «index or range», «attribute / text matchers»)</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following is a concrete example…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("h1", 2, class: "heading")</code></pre>
</div>
</div>
<div class="paragraph">
<p>This would find the 3rd (elements are 0 indexed) <code>h1</code> element whose <code>class</code> <em>attribute</em> is exactly “<code>heading</code>”.</p>
</div>
<div class="paragraph">
<p>All arguments are optional, meaning the following calls are all valid:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("div p", 0)
$("div p", title: "something")
$(0)
$(title: "something")</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>There is an alias for the dollar function named “find” if a method named “$” is not to your taste.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="css-selectors"><a class="link" href="#css-selectors">4.1.1. CSS Selectors</a></h4>
<div class="paragraph">
<p>You can use any CSS selector that the underlying <code>WebDriver</code> supports…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$('div.some-class p:first-child[title^="someth"]')</code></pre>
</div>
</div>
<div class="paragraph">
<p>HTMLUnit driver only partially supports CSS 3.
A future version of the HTMLUnit driver may gain better CSS 3 support.</p>
</div>
</div>
<div class="sect3">
<h4 id="using-webdriver-s-code-by-code-class-selectors"><a class="link" href="#using-webdriver-s-code-by-code-class-selectors">4.1.2. Using WebDriver&#8217;s <code>By</code> class selectors</a></h4>
<div class="paragraph">
<p>For all signatures of <code>$()</code> function that accept a css selector there is an equivalent signature where an instance of WebDriver&#8217;s <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/By.html">By</a></code> class can be used instead of a <code>String</code>.</p>
</div>
<div class="paragraph">
<p>Using CSS selectors is the idiomatic way of using Geb and should be preferred to using <code>By</code> selectors.
It is always possible to select the same elements using a css selector as when using a certain <code>By</code> selector apart from certain XPath selectors which is why this convenience mechanism is provided.</p>
</div>
<div class="paragraph">
<p>Following are some examples of using <code>By</code> selectors…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$(By.id("some-id"))
$(By.className("some-class"))
$(By.xpath('//p[@class="xpath"]'))</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="indexes-and-ranges"><a class="link" href="#indexes-and-ranges">4.1.3. Indexes and ranges</a></h4>
<div class="paragraph">
<p>When matching, a single positive integer or integer range can be given to restrict by index.</p>
</div>
<div class="paragraph">
<p>Consider the following html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;p&gt;a&lt;/p&gt;
&lt;p&gt;b&lt;/p&gt;
&lt;p&gt;c&lt;/p&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can use indexes to match content like so…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p", 0).text() == "a"
assert $("p", 2).text() == "c"
assert $("p", 0..1)*.text() == ["a", "b"]
assert $("p", 1..2)*.text() == ["b", "c"]</code></pre>
</div>
</div>
<div class="paragraph">
<p>See below for an explanation of the <code>text()</code> method and the use of the spread operator.</p>
</div>
</div>
<div class="sect3">
<h4 id="attribute-and-text-matching"><a class="link" href="#attribute-and-text-matching">4.1.4. Attribute and text matching</a></h4>
<div class="paragraph">
<p>Matches can be made on attributes and node text values via Groovy&#8217;s named parameter syntax.
The value <code>text</code> is treated specially as a match against the node&#8217;s text.
All other values are matched against their corresponding attribute values.</p>
</div>
<div class="paragraph">
<p>Consider the following html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;p attr1="a" attr2="b"&gt;p1&lt;/p&gt;
&lt;p attr1="a" attr2="c"&gt;p2&lt;/p&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can use attribute matchers like so…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p", attr1: "a").size() == 2
assert $("p", attr2: "c").size() == 1</code></pre>
</div>
</div>
<div class="paragraph">
<p>Attribute values are `and`ed together…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p", attr1: "a", attr2: "b").size() == 1</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can use text matchers like so…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p", text: "p1").size() == 1</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can mix attribute and text matchers…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p", text: "p1", attr1: "a").size() == 1</code></pre>
</div>
</div>
<div class="sect4">
<h5 id="using-patterns"><a class="link" href="#using-patterns">Using patterns</a></h5>
<div class="paragraph">
<p>To match the entire value of an attribute or the text you use a <code>String</code> value. It is also possible to use a <code>Pattern</code> to do regexp matching…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p", text: ~/p./).size() == 2</code></pre>
</div>
</div>
<div class="paragraph">
<p>Geb also ships with a bunch of shortcut pattern methods…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p", text: startsWith("p")).size() == 2
assert $("p", text: endsWith("2")).size() == 1</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following is the complete listing:</p>
</div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 33%;">
<col style="width: 33%;">
<col style="width: 33%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Case Sensitive</th>
<th class="tableblock halign-left valign-top">Case Insensitive</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>startsWith</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>iStartsWith</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Matches values that start with the given value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>contains</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>iContains</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Matches values that contain the given value anywhere</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>endsWith</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>iEndsWith</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Matches values that end with the given value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>containsWord</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>iContainsWord</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Matches values that contain the given value surrounded by either whitespace or the beginning or end of the value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>notStartsWith</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>iNotStartsWith</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Matches values that DO NOT start with the given value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>notContains</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>iNotContains</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Matches values that DO NOT contain the given value anywhere</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>notEndsWith</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>iNotEndsWith</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Matches values that DO NOT end with the given value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>notContainsWord</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>iNotContainsWord</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Matches values that DO NOT contain the given value surrounded by either whitespace or the beginning or end of the value</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>All of these methods themselves can take a <code>String</code> or a <code>Pattern</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p", text: contains(~/\d/)).size() == 2</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>You might be wondering how this magic works, i.e. where these methods come from and where they can be used.
They are methods that are available on <code>geb.Page</code> and other <em>places</em> where you can use the $ function.
They simply just return patterns.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect3">
<h4 id="navigators-are-iterable"><a class="link" href="#navigators-are-iterable">4.1.5. Navigators are iterable</a></h4>
<div class="paragraph">
<p>The navigator objects implement the Java <code>Iterable</code> interface, which allows you to do lots of Groovy stuff like use the <code>max()</code> function…</p>
</div>
<div class="paragraph">
<p>Consider the following html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;p&gt;1&lt;/p&gt;
&lt;p&gt;2&lt;/p&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can use the <code>max()</code> function on <code>Navigator</code> instances…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p").max { it.text() }.text() == "2"</code></pre>
</div>
</div>
<div class="paragraph">
<p>This also means that navigator objects work with the Groovy spread operator…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p")*.text().max() == "2"</code></pre>
</div>
</div>
<div class="paragraph">
<p>When treating a navigator as <code>Iterable</code>, the iterated over content is always the exact matched elements (as opposed to including children).</p>
</div>
</div>
<div class="sect3">
<h4 id="code-equals-code-and-code-hashcode-code"><a class="link" href="#code-equals-code-and-code-hashcode-code">4.1.6. <code>equals()</code> and <code>hashCode()</code></a></h4>
<div class="paragraph">
<p>It&#8217;s possible to check <code>Navigator</code> instances for equality.
The rules are simple - two empty navigators are always equal and two non empty navigators are only equal if they contain the exact same elements in the same order.</p>
</div>
<div class="paragraph">
<p>Consider the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;p class="a"&gt;&lt;/p&gt;
&lt;p class="b"&gt;&lt;/p&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here are some examples of equal Navigator instances…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("div") == $(".foo") <i class="conum" data-value="1"></i><b>(1)</b>
assert $(".a") == $(".a") <i class="conum" data-value="2"></i><b>(2)</b>
assert $(".a") == $("p").not(".b") <i class="conum" data-value="3"></i><b>(3)</b>
assert $("p") == $("p") <i class="conum" data-value="4"></i><b>(4)</b>
assert $("p") == $(".a").add(".b") <i class="conum" data-value="5"></i><b>(5)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Two empty navigators</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Two single element navigators containing the same element</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Two single element navigators containing the same element created using different methods</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Two multi element navigators containing the same elements</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Two multi element navigators containing the same elements created using different methods</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>And some that are not equal…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("div") != $("p") <i class="conum" data-value="1"></i><b>(1)</b>
assert $(".a") != $(".b") <i class="conum" data-value="2"></i><b>(2)</b>
assert $(".a").add(".b") != $(".b").add(".a") <i class="conum" data-value="3"></i><b>(3)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Empty and not empty navigators</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Single element navigators containing different elements</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Multi element navigators containing the same elements but in a different order</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="finding-filtering"><a class="link" href="#finding-filtering">4.2. Finding &amp; filtering</a></h3>
<div class="paragraph">
<p>Navigator objects have <code>find()</code> and <code>$()</code> methods for finding descendants, and <code>filter()</code> and <code>not()</code> methods for reducing the matched content.</p>
</div>
<div class="paragraph">
<p>Consider the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;div class="a"&gt;
&lt;p class="b"&gt;geb&lt;/p&gt;
&lt;/div&gt;
&lt;div class="b"&gt;
&lt;input type="text"/&gt;
&lt;/div&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can select <code>p.b</code> by…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("div").find(".b")
$("div").$(".b")</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can select <code>div.b</code> by…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("div").filter(".b")</code></pre>
</div>
</div>
<div class="paragraph">
<p>or…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$(".b").not("p")</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can select the <code>div</code> containing the <code>p</code> with…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("div").has("p")</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or select the <code>div</code> containing the <code>input</code> with a type attribute of "text" like so…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("div").has("input", type: "text")</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can select the <code>div</code> that does not contain the <code>p</code> with…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("div").hasNot("p")</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or select the <code>div</code> that does not contain the <code>input</code> with a type attribute of "text" like so…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("div").hasNot("input", type: "text")</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or select the two <code>div</code> that do not contain <code>input</code> with a type attribute of "submit" like so…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("div").hasNot("input", type: "submit")</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>find()</code> and <code>$()</code> methods support the <strong>exact same argument types as the <code>$()</code> function</strong>.</p>
</div>
<div class="paragraph">
<p>The <code>filter()</code>, <code>not()</code>, <code>has()</code> and <code>hasNot()</code> methods have the same signatures - they accept: a selector string, a predicates map or both.</p>
</div>
<div class="paragraph">
<p>These methods return a new navigator object that represents the new content.</p>
</div>
</div>
<div class="sect2">
<h3 id="composition"><a class="link" href="#composition">4.3. Composition</a></h3>
<div class="paragraph">
<p>It is also possible to compose navigator objects from other navigator objects, for situations where you can&#8217;t express a content set in one query.
To do this, simply call the <code>$()</code> function and pass in the navigators to compose.</p>
</div>
<div class="paragraph">
<p>Consider the following markup…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;p class="a"&gt;1&lt;/p&gt;
&lt;p class="b"&gt;2&lt;/p&gt;
&lt;p class="c"&gt;3&lt;/p&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can then create a new navigator object that represents both the <code>a</code> and <code>b</code> paragraphs the following way:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $($("p.a"), $("p.b"))*.text() == ["1", "2"]</code></pre>
</div>
</div>
<div class="paragraph">
<p>An alternative way is to use the <code>add()</code> method of <code>Navigator</code> that takes either a <code>String</code> or a Webdriver&#8217;s <code>By</code> selector:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p.a").add("p.b").add(By.className("c"))*.text() == ["1", "2", "3"]</code></pre>
</div>
</div>
<div class="paragraph">
<p>Finally, you can compose navigator objects from content. So given a page content definition:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">static content = {
pElement { pClass -&gt; $('p', class: pClass) }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can compose content elements into a navigator in the following way:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $(pElement("a"), pElement("b"))*.text() == ["1", "2"]</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="traversing"><a class="link" href="#traversing">4.4. Traversing</a></h3>
<div class="paragraph">
<p>Navigators also have methods for selecting content <em>around</em> the matched content.</p>
</div>
<div class="paragraph">
<p>Consider the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;div class="a"&gt;
&lt;div class="b"&gt;
&lt;p class="c"&gt;&lt;/p&gt;
&lt;p class="d"&gt;&lt;/p&gt;
&lt;p class="e"&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="f"&gt;&lt;/div&gt;
&lt;/div&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can select content <em>around</em> <code>p.d</code> by…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p.d").previous() == $("p.c")
assert $("p.e").prevAll() == $("p.c").add("p.d")
assert $("p.d").next() == $("p.e")
assert $("p.c").nextAll() == $("p.d").add("p.e")
assert $("p.d").parent() == $("div.b")
assert $("p.c").siblings() == $("p.d").add("p.e")
assert $("div.a").children() == $("div.b").add("div.f")</code></pre>
</div>
</div>
<div class="paragraph">
<p>Consider the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;div class="a"&gt;
&lt;p class="a"&gt;&lt;/p&gt;
&lt;p class="b"&gt;&lt;/p&gt;
&lt;p class="c"&gt;&lt;/p&gt;
&lt;/div&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following code will select <code>p.b</code> &amp; <code>p.c</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p").next() == $("p.b").add("p.c")</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>previous()</code>, <code>prevAll()</code>, <code>next()</code>, <code>nextAll()</code>, <code>parent()</code>, <code>parents()</code>, <code>closest()</code>, <code>siblings()</code> and <code>children()</code> methods can also take CSS selectors and attribute matchers.</p>
</div>
<div class="paragraph">
<p>Using the same html, the following examples will select <code>p.c</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p").next(".c") == $("p.c").add("p.c")
assert $("p").next(class: "c") == $("p.c").add("p.c")
assert $("p").next("p", class: "c") == $("p.c").add("p.c")</code></pre>
</div>
</div>
<div class="paragraph">
<p>Likewise, consider the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;div class="a"&gt;
&lt;div class="b"&gt;
&lt;p&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following examples will select <code>div.b</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p").parent(".b") == $("div.b")
assert $("p").parent(class: "b") == $("div.b")
assert $("p").parent("div", class: "b") == $("div.b")</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>closest()</code> method is a special case in that it will select the first ancestor of the current elements that matches a selector.
There is no no-argument version of the <code>closest()</code> method.
For example, these will select <code>div.a</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p").closest(".a") == $("div.a")
assert $("p").closest(class: "a") == $("div.a")
assert $("p").closest("div", class: "a") == $("div.a")</code></pre>
</div>
</div>
<div class="paragraph">
<p>These methods do not take indexes as they automatically select the first matching content.
To select multiple elements you can use <code>prevAll()</code>, <code>nextAll()</code> and <code>parents()</code> all of which have no-argument versions and versions that filter by a selector.</p>
</div>
<div class="paragraph">
<p>The <code>nextUntil()</code>, <code>prevUntil()</code> and <code>parentsUntil()</code> methods return all nodes along the relevant axis <em>until</em> the first one that matches a selector or attributes.
Consider the following markup:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;div class="a"&gt;&lt;/div&gt;
&lt;div class="b"&gt;&lt;/div&gt;
&lt;div class="c"&gt;&lt;/div&gt;
&lt;div class="d"&gt;&lt;/div&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following examples will select <code>div.b</code> and <code>div.c</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $(".a").nextUntil(".d") == $("div.b").add("div.c")
assert $(".a").nextUntil(class: "d") == $("div.b").add("div.c")
assert $(".a").nextUntil("div", class: "d") == $("div.b").add("div.c")</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="clicking"><a class="link" href="#clicking">4.5. Clicking</a></h3>
<div class="paragraph">
<p>Navigator objects implement the <code>click()</code> method, which will instruct the browser to click the sole element the navigator has matched.
If that method is called on a multi element <code>Navigator</code> then a <code>SingleElementNavigatorOnlyMethodException</code> is thrown.</p>
</div>
<div class="paragraph">
<p>There are also <code>click(Class)</code> , <code>click(Page)</code> and <code>click(List)</code> methods that are analogous to the browser object&#8217;s
<a href="#changing-pages"><code>page(Class&lt;? extends Page&gt;)</code>, <code>page(Page)</code>, and <code>page(Class&lt;? extends Page&gt;[])</code>, <code>page(Page[])</code> methods respectively</a>.
This allow page changes to be specified at the same time as click actions.</p>
</div>
<div class="paragraph">
<p>For example…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("a.login").click(LoginPage)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Would click the <code>a.login</code> element, then effectively call <code>browser.page(LoginPage)</code> and verify that the browser is at the expected page.</p>
</div>
<div class="paragraph">
<p>All of the page classes passed in when using the list variant have to have an “at” checker defined, otherwise an <code>UndefinedAtCheckerException</code> will be thrown.</p>
</div>
</div>
<div class="sect2">
<h3 id="determining-visibility"><a class="link" href="#determining-visibility">4.6. Determining visibility</a></h3>
<div class="paragraph">
<p><code>Navigator</code> objects have a <code>displayed</code> property that indicates whether the element is visible to the user or not.
The <code>displayed</code> property of a navigator object that doesn&#8217;t match anything is always <code>false</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="focus"><a class="link" href="#focus">4.7. Focus</a></h3>
<div class="paragraph">
<p>It is possible to verify if a given <code>Navigator</code> object holds the currently focused element using its <code>focused</code> property.
<code>Navigator</code> objects that don&#8217;t match any elements will return <code>false</code> as the value of <code>focused</code> property and ones that match multiple elements will throw a <code>SingleElementNavigatorOnlyMethodException</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="size-and-location"><a class="link" href="#size-and-location">4.8. Size and location</a></h3>
<div class="paragraph">
<p>You can obtain the size and location of content on the page.
All units are in pixels.
The size is available via the <code>height</code> and <code>width</code> properties, while the location is available as the <code>x</code> and <code>y</code> properties which represent the distance from the top left of the page
(or parent frame) to the top left point of the content.</p>
</div>
<div class="paragraph">
<p>All of these properties operate on the <strong>sole</strong> matched element only and a <code>SingleElementNavigatorOnlyMethodException</code> is thrown if they are accessed on a multi element <code>Navigator</code>.</p>
</div>
<div class="paragraph">
<p>Consider the following html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;div style="height: 20px; width: 40px; position: absolute; left: 20px; top: 10px"&gt;&lt;/div&gt;
&lt;div style="height: 40px; width: 100px; position: absolute; left: 30px; top: 150px"&gt;&lt;/div&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following conditions are satisfied for it…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("div", 0).height == 20
assert $("div", 0).width == 40
assert $("div", 0).x == 20
assert $("div", 0).y == 10</code></pre>
</div>
</div>
<div class="paragraph">
<p>To obtain any of the properties for all matched elements, you can use the Groovy spread operator.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("div")*.height == [20, 40]
assert $("div")*.width == [40, 100]
assert $("div")*.x == [20, 30]
assert $("div")*.y == [10, 150]</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="accessing-tag-name-attributes-text-and-classes"><a class="link" href="#accessing-tag-name-attributes-text-and-classes">4.9. Accessing tag name, attributes, text and classes</a></h3>
<div class="paragraph">
<p>The <code>tag()</code>, <code>text()</code> and <code>classes()</code> methods as well as accessing attributes via the <code>@</code> notation or <code>attr()</code> method return the requested content on the <strong>sole</strong> matched element.
If these methods are called on a multi element <code>Navigator</code> then a <code>SingleElementNavigatorOnlyMethodException</code> is thrown.
The <code>classes()</code> method returns a <code>java.util.List</code> of class names sorted alphabetically.</p>
</div>
<div class="paragraph">
<p>Consider the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;p title="a" class="a para"&gt;a&lt;/p&gt;
&lt;p title="b" class="b para"&gt;b&lt;/p&gt;
&lt;p title="c" class="c para"&gt;c&lt;/p&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following assertions are valid…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $(".a").text() == "a"
assert $(".a").tag() == "p"
assert $(".a").@title == "a"
assert $(".a").classes() == ["a", "para"]</code></pre>
</div>
</div>
<div class="paragraph">
<p>To obtain information about all matched content, you use the Groovy <em>spread operator</em></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("p")*.text() == ["a", "b", "c"]
assert $("p")*.tag() == ["p", "p", "p"]
assert $("p")*.@title == ["a", "b", "c"]
assert $("p")*.classes() == [["a", "para"], ["b", "para"], ["c", "para"]]</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="css-properties"><a class="link" href="#css-properties">4.10. CSS properties</a></h3>
<div class="paragraph">
<p>Css properties of a single element navigator can be accessed using the <code>css()</code> method.
If that method is called on a multi element <code>Navigator</code> then a <code>SingleElementNavigatorOnlyMethodException</code> is thrown.</p>
</div>
<div class="paragraph">
<p>Consider the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;div style="float: left"&gt;text&lt;/div&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can obtain the value of <code>float</code> css property in the following way…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-css" data-lang="css">assert $("div").css("float") == "left"</code></pre>
</div>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>There are some limitations when it comes to retrieving css properties of <code>Navigator</code> objects.
Color values should be returned as rgba strings, so, for example if the <code>background-color</code> property is set as <code>green</code> in the HTML source, the returned value will be <code>rgba(0, 255, 0, 1)</code>.
Note that shorthand CSS properties (e.g. <code>background</code>, <code>font</code>, <code>border</code>, <code>border-top</code>, <code>margin</code>, <code>margin-top</code>, <code>padding</code>, <code>padding-top</code>, <code>list-style</code>, <code>outline</code>, <code>pause</code>, <code>cue</code>) are not returned,
in accordance with the DOM CSS2 specification - you should directly access the longhand properties (e.g. <code>background-color</code>) to access the desired values.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="sending-keystrokes"><a class="link" href="#sending-keystrokes">4.11. Sending keystrokes</a></h3>
<div class="paragraph">
<p>Given the following html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;input type="text"/&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can send keystrokes to the input (and any other content) via the leftShift operator, which is a shortcut for the <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/WebElement.html#sendKeys(java.lang.CharSequence%2E%2E%2E)">sendKeys()</a></code> method of WebDriver.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("input") &lt;&lt; "foo"
assert $("input").value() == "foo"</code></pre>
</div>
</div>
<div class="paragraph">
<p>How content responds to the keystrokes depends on what the content is.</p>
</div>
<div class="sect3">
<h4 id="non-characters-e-g-delete-key-key-chords-etc"><a class="link" href="#non-characters-e-g-delete-key-key-chords-etc">Non characters (e.g. delete key, key chords, etc.)</a></h4>
<div class="paragraph">
<p>It is possible to send non-textual characters to content by using the WebDriver <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/Keys.html">Keys</a></code> enumeration…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import org.openqa.selenium.Keys
$("input") &lt;&lt; Keys.chord(Keys.CONTROL, "c")</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here we are sending a “control-c” to an input.</p>
</div>
<div class="paragraph">
<p>See the documentation for <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/Keys.html">Keys</a></code> enumeration for more information on the possible keys.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="accessing-input-values"><a class="link" href="#accessing-input-values">4.12. Accessing input values</a></h3>
<div class="paragraph">
<p>The value of <code>input</code>, <code>select</code> and <code>textarea</code> elements can be retrieved and set with the <code>value</code> method.
Calling <code>value()</code> with no arguments will return the String value of <em>the sole</em> element in the Navigator.
If that method is called on a multi element <code>Navigator</code> then a <code>SingleElementNavigatorOnlyMethodException</code> is thrown.
Calling <code>value(value)</code> will set the current value of <em>all</em> elements in the Navigator.
The argument can be of any type and will be coerced to a <code>String</code> if necessary.
The exceptions are that when setting a <code>checkbox</code> value the method expects a <code>boolean</code> (or an existing checkbox value or label) and when setting a multiple <code>select</code> the method expects an array or
<code>Collection</code> of values.</p>
</div>
</div>
<div class="sect2">
<h3 id="form-control-shortcuts"><a class="link" href="#form-control-shortcuts">4.13. Form control shortcuts</a></h3>
<div class="paragraph">
<p>Interacting with form controls (<code>input</code>, <code>select</code> etc.) is such a common task in web functional testing that Geb provides convenient shortcuts for common functions.</p>
</div>
<div class="paragraph">
<p>Geb supports the following shortcuts for dealing with form controls.</p>
</div>
<div class="paragraph">
<p>Consider the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;form&gt;
&lt;input type="text" name="geb" value="testing" /&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The value can be read and written via property notation…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("form").geb == "testing"
$("form").geb = "goodness"
assert $("form").geb == "goodness"</code></pre>
</div>
</div>
<div class="paragraph">
<p>These are literally shortcuts for…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("form").find("input", name: "geb").value() == "testing"
$("form").find("input", name: "geb").value("goodness")
assert $("form").find("input", name: "geb").value() == "goodness"</code></pre>
</div>
</div>
<div class="paragraph">
<p>There is also a shortcut for obtaining a navigator based on a control name…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("form").geb() == $("form").find("input", name: "geb")</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>In the above and below examples with form controls we are using code like <code>$(&quot;form&quot;).someInput</code> where we could be using just <code>someInput</code> as long as there is only one control with the
<em>name</em> <code>someInput</code> on the page.
In the examples we are using <code>$(&quot;form&quot;).someInput</code> to hopefully be clearer.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>If your content definition (either a page or a module) describes content which is an <code>input</code>, <code>select</code> or <code>textarea</code>, you can access and set its value the same way as described above for forms.
Given a page and module definitions for the above mentioned HTML:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class ShortcutModule extends Module {
static content = {
geb { $('form').geb() }
}
}
class ShortcutPage extends Page {
static content = {
geb { $('form').geb() }
shortcutModule { module ShortcutModule }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following will pass:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">page ShortcutPage
assert geb == "testing"
geb = "goodness"
assert geb == "goodness"</code></pre>
</div>
</div>
<div class="paragraph">
<p>As well as:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">page ShortcutPage
assert shortcutModule.geb == "testing"
shortcutModule.geb = "goodness"
assert shortcutModule.geb == "goodness"</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="setting-form-control-values"><a class="link" href="#setting-form-control-values">4.14. Setting form control values</a></h3>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The following examples describe usage of form controls only using code like <code>$(&quot;form&quot;).someInput</code>.
Given a content definition <code>myContent { $(&quot;form&quot;).someInput }</code>, you can substitute <code>$(&quot;form&quot;).someInput</code> in the examples with <code>myContent</code>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Trying to set a value on an element which is not one of <code>input</code>, <code>select</code> or <code>textarea</code> will cause an <code>UnableToSetElementException</code> to be thrown.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="select"><a class="link" href="#select">4.14.1. Select</a></h4>
<div class="paragraph">
<p>Select values are set by assigning the value or text of the required option. Assigned values are automatically coerced to String. For example…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;form&gt;
&lt;select name="artist"&gt;
&lt;option value="1"&gt;Ima Robot&lt;/option&gt;
&lt;option value="2"&gt;Edward Sharpe and the Magnetic Zeros&lt;/option&gt;
&lt;option value="3"&gt;Alexander&lt;/option&gt;
&lt;/select&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can select options with…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("form").artist = "1" <i class="conum" data-value="1"></i><b>(1)</b>
$("form").artist = 2 <i class="conum" data-value="2"></i><b>(2)</b>
$("form").artist = "Alexander" <i class="conum" data-value="3"></i><b>(3)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>First option selected by its value attribute.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Second option selected by its value attribute with argument coercion.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Third option selected by its text.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>If you attempt to set a select to a value that does not match the value or text of any options, an <code>IllegalArgumentException</code> will be thrown.</p>
</div>
</div>
<div class="sect3">
<h4 id="multiple-select"><a class="link" href="#multiple-select">4.14.2. Multiple select</a></h4>
<div class="paragraph">
<p>If the select has the <code>multiple</code> attribute it is set with a array or <code>Collection</code> of values.
Any options not in the values are un-selected.
For example…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;form&gt;
&lt;select name="genres" multiple&gt;
&lt;option value="1"&gt;Alt folk&lt;/option&gt;
&lt;option value="2"&gt;Chiptunes&lt;/option&gt;
&lt;option value="3"&gt;Electroclash&lt;/option&gt;
&lt;option value="4"&gt;G-Funk&lt;/option&gt;
&lt;option value="5"&gt;Hair metal&lt;/option&gt;
&lt;/select&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can select options with…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("form").genres = ["2", "3"] <i class="conum" data-value="1"></i><b>(1)</b>
$("form").genres = [1, 4, 5] <i class="conum" data-value="2"></i><b>(2)</b>
$("form").genres = ["Alt folk", "Hair metal"] <i class="conum" data-value="3"></i><b>(3)</b>
$("form").genres = [] <i class="conum" data-value="4"></i><b>(4)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Second and third options selected by their value attributes.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>First, fourth and fifth options selected by their value attributes with argument coercion.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>First and last options selected by their text.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>All options un-selected.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>If the collection being assigned contains a value that does not match the value or text of any options, an <code>IllegalArgumentException</code> will be thrown.</p>
</div>
</div>
<div class="sect3">
<h4 id="checkbox"><a class="link" href="#checkbox">4.14.3. Checkbox</a></h4>
<div class="paragraph">
<p>Checkboxes are generally checked/unchecked by setting their value to <code>true</code> or <code>false</code>.</p>
</div>
<div class="paragraph">
<p>Consider the following html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;form&gt;
&lt;input type="checkbox" name="pet" value="dog" /&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can set a single checkbox in the following manner…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("form").pet = true</code></pre>
</div>
</div>
<div class="paragraph">
<p>Calling <code>value()</code> on a checked checkbox will return the value of its <code>value</code> attribute, i.e:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;form&gt;
&lt;input type="checkbox" name="pet" value="dog" checked/&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("input", name: "pet").value() == "dog"
assert $("form").pet == "dog"</code></pre>
</div>
</div>
<div class="paragraph">
<p>Calling <code>value()</code> on an unchecked checkbox will return <code>false</code>, i.e:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;form&gt;
&lt;input type="checkbox" name="pet" value="dog"/&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert !$("input", name: 'pet').value()
assert !$("form").pet</code></pre>
</div>
</div>
<div class="paragraph">
<p>In general you should use <a href="http://mrhaki.blogspot.com/2009/08/groovy-goodness-tell-groovy-truth.html">Groovy Truth</a> when checking if a checkbox is checked:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;form&gt;
&lt;input type="checkbox" name="checkedByDefault" value="checkedByDefaulValue" checked/&gt;
&lt;input type="checkbox" name="uncheckedByDefault" value="uncheckedByDefaulValue"/&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("form").checkedByDefault
assert !$("form").uncheckedByDefault</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="multiple-checkboxes"><a class="link" href="#multiple-checkboxes">4.14.4. Multiple checkboxes</a></h4>
<div class="paragraph">
<p>You can also check a checkbox by explicitly setting its <code>value</code> or using its label. This is useful when you have a number of checkboxes with the same name, i.e.:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;form&gt;
&lt;label for="dog-checkbox"&gt;Canis familiaris&lt;/label&gt;
&lt;input type="checkbox" name="pet" value="dog" id="dog-checkbox"/&gt;
&lt;label for="cat-checkbox"&gt;Felis catus&lt;/label&gt;
&lt;input type="checkbox" name="pet" value="cat" id="cat-checkbox" /&gt;
&lt;label for="lizard-checkbox"&gt;Lacerta&lt;/label&gt;
&lt;input type="checkbox" name="pet" value="lizard" id="lizard-checkbox" /&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can select dog as your pet type as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("form").pet = "dog"
$("form").pet = "Canis familiaris"</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you wish to select multiple checkboxes instead of only one then you can use a collection as the value:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("form").pet = ["dog", "lizard"]
$("form").pet = ["Canis familiaris", "Lacerta"]</code></pre>
</div>
</div>
<div class="paragraph">
<p>When checking if a checkbox is checked and there are multiple checkboxes with the same name make sure that you use a navigator that holds only one of them before calling <code>value()</code> on it:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;form&gt;
&lt;input type="checkbox" name="pet" value="dog" checked/&gt;
&lt;input type="checkbox" name="pet" value="cat" /&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $("input", name: "pet", value: "dog").value()
assert !$("input", name: "pet", value: "cat").value()</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="radio"><a class="link" href="#radio">4.14.5. Radio</a></h4>
<div class="paragraph">
<p>Radio values are set by assigning the value of the radio button that is to be selected or the label text associated with a radio button.</p>
</div>
<div class="paragraph">
<p>For example, with the following radio buttons…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;form&gt;
&lt;label for="site-current"&gt;Search this site&lt;/label&gt;
&lt;input type="radio" id="site-current" name="site" value="current"&gt;
&lt;label&gt;Search Google
&lt;input type="radio" name="site" value="google"&gt;
&lt;/label&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can select the radios by value…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("form").site = "current"
assert $("form").site == "current"
$("form").site = "google"
assert $("form").site == "google"</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or by label text…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("form").site = "Search this site"
assert $("form").site == "current"
$("form").site = "Search Google"
assert $("form").site == "google"</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="text-inputs-and-textareas"><a class="link" href="#text-inputs-and-textareas">4.14.6. Text inputs and textareas</a></h4>
<div class="paragraph">
<p>In the case of a text <code>input</code> or a <code>textarea</code>, the assigned value becomes the element&#8217;s <em>value</em> attribute.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;form&gt;
&lt;input type="text" name="language"/&gt;
&lt;input type="text" name="description"/&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("form").language = "gro"
$("form").description = "Optionally statically typed dynamic lang"
assert $("form").language == "gro"
assert $("form").description == "Optionally statically typed dynamic lang"</code></pre>
</div>
</div>
<div class="paragraph">
<p>It is also possible to append text by using the send keys shorthand…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("form").language() &lt;&lt; "ovy"
$("form").description() &lt;&lt; "uage"
assert $("form").language == "groovy"
assert $("form").description == "Optionally statically typed dynamic language"</code></pre>
</div>
</div>
<div class="paragraph">
<p>Which an also be used for non-character keys…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import org.openqa.selenium.Keys
$("form").language() &lt;&lt; Keys.BACK_SPACE
assert $("form").language == "groov"</code></pre>
</div>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>WebDriver has some issues with textareas and surrounding whitespace.
Namely, some drivers implicitly trim whitespace from the beginning and end of the value.
You can track this issue <a href="http://code.google.com/p/selenium/issues/detail?id=2131">here</a>.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="file-upload"><a class="link" href="#file-upload">4.14.7. File upload</a></h4>
<div class="paragraph">
<p>It&#8217;s currently not possible with WebDriver to simulate the process of a user clicking on a file upload control and choosing a file to upload via the normal file chooser.
However, you can directly set the value of the upload control to the <em>absolute path</em> of a file on the system where the driver is running and on form submission that file will be uploaded.
So if your html looks like…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;input type="file" name="csvFile"/&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>And the <code>uploadedFile</code> variable holds a <code>File</code> instance pointing at the file you want to upload then this is how you can set the value of the upload control…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("form").csvFile = uploadedFile.absolutePath</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="complex-interactions"><a class="link" href="#complex-interactions">4.15. Complex interactions</a></h3>
<div class="paragraph">
<p>WebDriver supports interactions that are more complex than simply clicking or typing into items, such as dragging.
You can use this API directly when using Geb or use the more Geb friendly <a href="#interact-closures"><code>interact</code> DSL</a>.</p>
</div>
<div class="sect3">
<h4 id="using-actions"><a class="link" href="#using-actions">4.15.1. Using the WebDriver <code>Actions</code> API directly</a></h4>
<div class="paragraph">
<p>A Geb navigator object is built on top of a collection of WebDriver <a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/WebElement.html">WebElement</a> objects.
It is possible to access the contained <code>WebElement</code> instances via the following methods on navigator objects:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">WebElement singleElement()
WebElement firstElement()
WebElement lastElement()
Collection&lt;WebElement&gt; allElements()</code></pre>
</div>
</div>
<div class="paragraph">
<p>By using the methods of the WebDriver <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/interactions/Actions.html">Actions</a></code> class with <code>WebElement</code> instances, complex user gestures can be emulated.
First you will need to create an <code>Actions</code> instance after obtaining the WebDriver driver:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def actions = new Actions(driver)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Next, use methods of <code>Actions</code> to compose a series of UI actions, then call <code>build()</code> to create a concrete <code>Action</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">WebElement someItem = $("li.clicky").firstElement()
def shiftClick = actions.keyDown(Keys.SHIFT).click(someItem).keyUp(Keys.SHIFT).build()</code></pre>
</div>
</div>
<div class="paragraph">
<p>Finally, call <code>perform()</code> to actually trigger the desired mouse or keyboard behavior:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">shiftClick.perform()</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="interact-closures"><a class="link" href="#interact-closures">4.15.2. Using <code>interact()</code></a></h4>
<div class="paragraph">
<p>To avoid having to manage building and performing an <code>Action</code> via the lifecycle of an <code>Actions</code> instance as well as having to obtain <code>WebElement</code> instances from <code>Navigator</code> when emulating user
gestures, Geb adds the <code>interact()</code> method.
When using that method, an <code>Actions</code> instance is implicitly created, built into an <code>Action</code>, and performed.
The delegate of the closure passed to <code>interact()</code> is an instance of <code><a href="api/geb/interaction/InteractDelegate.html">InteractDelegate</a></code> which declares the same methods as <code>Actions</code> but takes <code>Navigator</code> as arguments for methods of <code>Actions</code>
which take <code>WebElement</code>.</p>
</div>
<div class="paragraph">
<p>This <code>interact()</code> call performs the same work as the calls in the <a href="#using-actions">Using the WebDriver <code>Actions</code> API directly</a> section:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">interact {
keyDown Keys.SHIFT
click $("li.clicky")
keyUp Keys.SHIFT
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>For the full list of available methods that can be used for emulating user gestures see the documentation for the <code><a href="api/geb/interaction/InteractDelegate.html">InteractDelegate</a></code> class.</p>
</div>
</div>
<div class="sect3">
<h4 id="interact-examples"><a class="link" href="#interact-examples">4.15.3. Interact examples</a></h4>
<div class="paragraph">
<p>Calls to <code>interact()</code> can be used to perform behaviors that are more complicated than clicking buttons and anchors or typing in input fields.</p>
</div>
<div class="sect4">
<h5 id="drag-and-drop"><a class="link" href="#drag-and-drop">Drag and drop</a></h5>
<div class="paragraph">
<p><code>clickAndHold()</code>, <code>moveByOffset()</code>, and then <code>release()</code> will drag and drop an element on the page.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">interact {
clickAndHold($('#draggable'))
moveByOffset(150, 200)
release()
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Drag-and-dropping can also be accomplished using the <code>dragAndDropBy()</code> convenience method from the Actions API:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">interact {
dragAndDropBy($("#draggable"), 150, 200)
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In this particular example, the element will be clicked then dragged 150 pixels to the right and 200 pixels downward before being released.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Moving to arbitrary locations with the mouse is currently not supported by the HTMLUnit driver, but moving directly to elements is.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect4">
<h5 id="control-clicking"><a class="link" href="#control-clicking">Control-clicking</a></h5>
<div class="paragraph">
<p>Control-clicking several elements, such as items in a list, is performed the same way as shift-clicking.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import org.openqa.selenium.Keys
interact {
keyDown(Keys.CONTROL)
click($("ul.multiselect li", text: "Order 1"))
click($("ul.multiselect li", text: "Order 2"))
click($("ul.multiselect li", text: "Order 3"))
keyUp(Keys.CONTROL)
}</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="pages"><a class="link" href="#pages">5. Pages</a></h2>
<div class="sectionbody">
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Before reading this chapter, please make sure you have read the <a href="#drive">section on the <code>Browser.drive()</code> method</a></p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="the-page-object-pattern-2"><a class="link" href="#the-page-object-pattern-2">5.1. The Page Object pattern</a></h3>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go "search"
$("input[name='q']").value "Chuck Norris"
$("input[value='Search']").click()
assert $("li", 0).text().contains("Chuck")
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This is valid Geb code, and it works well for a one off script but there are two big issues with this approach.
Imagine that you have <em>many</em> tests that involve searching and checking results.
The implementation of how to search and how to find the results is going to have to be duplicated in <em>every</em> test, maybe <em>many times</em> per test.
As soon as something as trivial as the name of the search field changes you have to update a lot of code.
The Page Object Pattern allows us to apply the same principles of modularity, reuse and encapsulation that we use in other aspects of programming to avoid such issues in browser automation code.</p>
</div>
<div class="paragraph">
<p>Here is the same script, utilising page objects…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.Page
class SearchPage extends Page {
static url = "search"
static at = { title == "Search engine" }
static content = {
searchField { $("input[name=q]") }
searchButton(to: ResultsPage) { $("input[value='Search']") }
}
void search(String searchTerm) {
searchField.value searchTerm
searchButton.click()
}
}
class ResultsPage extends Page {
static at = { title == "Results" }
static content = {
results { $("li") }
result { index -&gt; results[index] }
}
}
Browser.drive {
to SearchPage
search "Chuck Norris"
assert result(0).text().contains("Chuck")
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>You have now encapsulated, in a reusable fashion, information about each page and how to interact with it.
As anyone who has tried to knows, maintaining a large suite of functional web tests for a changing application can become an expensive and frustrating process.
Geb&#8217;s support for the Page Object pattern addresses this problem.</p>
</div>
</div>
<div class="sect2">
<h3 id="the-code-page-code-superclass"><a class="link" href="#the-code-page-code-superclass">5.2. The <code>Page</code> superclass</a></h3>
<div class="paragraph">
<p>All page objects <strong>must</strong> inherit from <code><a href="api/geb/Page.html">Page</a></code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="content-dsl"><a class="link" href="#content-dsl">5.3. The content DSL</a></h3>
<div class="paragraph">
<p>Geb features a DSL for defining page content in a <em>templated</em> fashion, which allows very concise yet flexible page definitions.
Pages define a <code>static</code> closure property called <code>content</code> that describes the page content.</p>
</div>
<div class="paragraph">
<p>Consider the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;div id="a"&gt;a&lt;/div&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We could define this content as so…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithDiv extends Page {
static content = {
theDiv { $('div', id: 'a') }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The structure to the content DSL is…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code>«name» { «definition» }</code></pre>
</div>
</div>
<div class="paragraph">
<p>Where <code>«definition»</code> is Groovy code that is evaluated against the instance of the page.</p>
</div>
<div class="paragraph">
<p>Here is how it could be used…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to PageWithDiv
assert theDiv.text() == "a"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>So how is this working?
First, remember that the <code>Browser</code> instance delegates any method calls or property accesses that it doesn&#8217;t know about to the current page instance.
So the above code is the same as…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to PageWithDiv
assert page.theDiv.text() == "a"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Secondly, defined content becomes available as properties and methods on instance of the page…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to PageWithDiv
// Following two lines are equivalent
assert theDiv.text() == "a"
assert theDiv().text() == "a"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The Content DSL actually defines content <em>templates</em>.
This is best illustrated by example…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class TemplatedPageWithDiv extends Page {
static content = {
theDiv { id -&gt; $('div', id: id) }
}
}
Browser.drive {
to TemplatedPageWithDiv
assert theDiv("a").text() == "a"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>There are no restrictions on what arguments can be passed to content templates.</p>
</div>
<div class="paragraph">
<p>A content template can return <em>anything</em>.
Typically they will return a <code><a href="api/geb/navigator/Navigator.html">Navigator</a></code> object through the use of the <code>$()</code> function, but it can be anything.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithStringContent extends Page {
static content = {
theDivText { $('div#a').text() }
}
}
Browser.drive {
to PageWithStringContent
assert theDivText == "a"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>It&#8217;s important to realise that <code>«definition»</code> code is evaluated against the page instance.
This allows code like the following…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithContentReuse extends Page {
static content = {
theDiv { $("div#a") }
theDivText { theDiv.text() }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>And this is not restricted to other content…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithContentUsingAField extends Page {
def divId = "a"
static content = {
theDiv { $('div', id: divId) }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithContentUsingAMethod extends Page {
static content = {
theDiv { $('div', id: divId()) }
}
def divId() {
"a"
}
}</code></pre>
</div>
</div>
<div class="sect3">
<h4 id="template-options"><a class="link" href="#template-options">5.3.1. Template options</a></h4>
<div class="paragraph">
<p>Template definitions can take different options. The syntax is…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code>«name»(«options map») { «definition» }</code></pre>
</div>
</div>
<div class="paragraph">
<p>For example…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">theDiv(cache: false, required: false) { $("div", id: "a") }</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following are the available options.</p>
</div>
<div class="sect4">
<h5 id="code-required-code"><a class="link" href="#code-required-code"><code>required</code></a></h5>
<div class="paragraph">
<p>Default value: <code>true</code></p>
</div>
<div class="paragraph">
<p>The <code>required</code> option controls whether or not the content returned by the definition has to exist or not.
This is only relevant when the definition returns a <code>Navigator</code> object (via the <code>$()</code> function) or <code>null</code>, it is ignored if the definition returns anything else.</p>
</div>
<div class="paragraph">
<p>If the <code>required</code> option is set to <code>true</code> and the returned content does not exist, a <code><a href="api/geb/error/RequiredPageContentNotPresent.html">RequiredPageContentNotPresent</a></code> exception will be thrown.</p>
</div>
<div class="paragraph">
<p>Given a completely empty html document the following will pass…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithTemplatesUsingRequiredOption extends Page {
static content = {
requiredDiv { $("div", id: "b") }
notRequiredDiv(required: false) { $("div", id: "b") }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to PageWithTemplatesUsingRequiredOption
assert !notRequiredDiv
def thrown = false
try {
page.requiredDiv
} catch (RequiredPageContentNotPresent e) {
thrown = true
}
assert thrown</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="content-dsl-min"><a class="link" href="#content-dsl-min"><code>min</code></a></h5>
<div class="paragraph">
<p>Default value: <code>1</code></p>
</div>
<div class="paragraph">
<p>The <code>min</code> option allows to specify the minimum number of elements that the <code>Navigator</code> object returned by the definition should contain.
If the number of elements returned is lower than the specified value then <code><a href="api/geb/error/ContentCountOutOfBoundsException.html">ContentCountOutOfBoundsException</a></code> will be thrown.</p>
</div>
<div class="paragraph">
<p>The value should be a non-negative integer.
This option cannot be used together with the <a href="#content-dsl-times"><code>times</code> option</a>.</p>
</div>
<div class="paragraph">
<p>Given the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;p&gt;first paragraph&lt;/p&gt;
&lt;p&gt;second paragraph&lt;/p&gt;
&lt;body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Accessing the following content definition…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">atLeastThreeElementNavigator(min: 3) { $('p') }</code></pre>
</div>
</div>
<div class="paragraph">
<p>Will result in a <code>ContentCountOutOfBoundsException</code> and the following exception message:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">"Page content 'pages.PageWithTemplateUsingMinOption -&gt; atLeastThreeElementNavigator: geb.navigator.NonEmptyNavigator' should return a navigator with at least 3 elements but has returned a navigator with 2 elements"</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="content-dsl-max"><a class="link" href="#content-dsl-max"><code>max</code></a></h5>
<div class="paragraph">
<p>Default value: <code>Integer.MAX_INT</code></p>
</div>
<div class="paragraph">
<p>The <code>max</code> option allows to specify the maximum number of elements that the <code>Navigator</code> object returned by the definition should contain.
If the number of elements returned is higher than the specified value then <code><a href="api/geb/error/ContentCountOutOfBoundsException.html">ContentCountOutOfBoundsException</a></code> will be thrown.</p>
</div>
<div class="paragraph">
<p>The value should be a non-negative integer.
This option cannot be used together with the <a href="#content-dsl-times"><code>times</code> option</a>.</p>
</div>
<div class="paragraph">
<p>Given the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;p&gt;first paragraph&lt;/p&gt;
&lt;p&gt;second paragraph&lt;/p&gt;
&lt;p&gt;third paragraph&lt;/p&gt;
&lt;p&gt;fourth paragraph&lt;/p&gt;
&lt;body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Accessing the following content definition…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">atMostThreeElementNavigator(max: 3) { $('p') }</code></pre>
</div>
</div>
<div class="paragraph">
<p>Will result in a <code>ContentCountOutOfBoundsException</code> and the following exception message:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">"Page content 'pages.PageWithTemplateUsingMaxOption -&gt; atMostThreeElementNavigator: geb.navigator.NonEmptyNavigator' should return a navigator with at most 3 elements but has returned a navigator with 4 elements"</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="content-dsl-times"><a class="link" href="#content-dsl-times"><code>times</code></a></h5>
<div class="paragraph">
<p>Default value: <code>null</code></p>
</div>
<div class="paragraph">
<p>A helper option allowing to specify both <a href="#content-dsl-min"><code>min</code> option</a> and <a href="#content-dsl-max"><code>max</code> option</a> in a single option.
If the number of elements returned is out of bounds specified then <code><a href="api/geb/error/ContentCountOutOfBoundsException.html">ContentCountOutOfBoundsException</a></code> will be thrown.</p>
</div>
<div class="paragraph">
<p>The value should be a non-negative integer (when the minimum and maximum number of elements are desired to be equal) or an integer range.
This option cannot be used together with <code>min</code> and <code>max</code> options.</p>
</div>
<div class="paragraph">
<p>Given the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;p&gt;first paragraph&lt;/p&gt;
&lt;p&gt;second paragraph&lt;/p&gt;
&lt;p&gt;third paragraph&lt;/p&gt;
&lt;p&gt;fourth paragraph&lt;/p&gt;
&lt;body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Accessing the following content definition…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">twoToThreeElementNavigator(times: 2..3) { $('p') }</code></pre>
</div>
</div>
<div class="paragraph">
<p>Will result in a <code>ContentCountOutOfBoundsException</code> and the following exception message:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">"Page content 'pages.PageWithTemplateUsingTimesOption -&gt; twoToThreeElementNavigator: geb.navigator.NonEmptyNavigator' should return a navigator with at most 3 elements but has returned a navigator with 4 elements"</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="code-cache-code"><a class="link" href="#code-cache-code"><code>cache</code></a></h5>
<div class="paragraph">
<p>Default value: <code>false</code></p>
</div>
<div class="paragraph">
<p>The <code>cache</code> option controls whether or not the definition is evaluated each time the content is requested (the content is cached for each unique set of parameters).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithTemplateUsingCacheOption extends Page {
def value = 1
static content = {
notCachedValue { value }
cachedValue(cache: true) { value }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to PageWithTemplateUsingCacheOption
assert notCachedValue == 1
assert cachedValue == 1
value = 2
assert notCachedValue == 2
assert cachedValue == 1</code></pre>
</div>
</div>
<div class="paragraph">
<p>Caching is a performance optimisation and is disabled by default.
You may want to enable if you notice that the a particular content definition is taking a long time to resolve.</p>
</div>
</div>
<div class="sect4">
<h5 id="content-dsl-to"><a class="link" href="#content-dsl-to"><code>to</code></a></h5>
<div class="paragraph">
<p>Default value: <code>null</code></p>
</div>
<div class="paragraph">
<p>The <code>to</code> option allows the definition of which page the browser will be sent to if the content is clicked.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithTemplateUsingToOption extends Page {
static content = {
helpLink(to: HelpPage) { $("a", text: "Help") }
}
}
class HelpPage extends Page { }</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to PageWithTemplateUsingToOption
helpLink.click()
assert page instanceof HelpPage</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>to</code> value will be implicitly used as an argument to the content&#8217;s <code>click()</code> method, effectively setting the new page type and verifying its at checker.
See the section on <a href="#clicking">clicking content</a> for how this changes the browser&#8217;s page object.</p>
</div>
<div class="paragraph">
<p>This option also supports all types that can be passed to any of the <a href="#changing-pages"><code>Browser.page()</code> method variants</a>:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>a page instance</p>
</li>
<li>
<p>a list of page classes</p>
</li>
<li>
<p>a list of page instances</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>When using lists variants (here shown with page classes)…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">static content = {
loginButton(to: [LoginSuccessfulPage, LoginFailedPage]) { $("input.loginButton") }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Then, on click, the browser&#8217;s page is set to the first page in the list whose at checker passes.
This is equivalent to the <a href="api/geb/Browser.html#page(Class%3C%3F%20extends%20Page%3E)"><code>page(Class&lt;? extends Page&gt;[])</code></a> and
<a href="api/geb/Browser.html#page(T)"><code>page(Page[])</code></a> browser methods which are explained in the section on <a href="#changing-pages">changing pages</a>.</p>
</div>
<div class="paragraph">
<p>All of the page classes and classes of the page instances passed in when using any variant of the <code>to</code> option have to have an “at” checker defined otherwise an <code>UndefinedAtCheckerException</code> will be
thrown.</p>
</div>
</div>
<div class="sect4">
<h5 id="content-dsl-wait"><a class="link" href="#content-dsl-wait"><code>wait</code></a></h5>
<div class="paragraph">
<p>Default value: <code>false</code></p>
</div>
<div class="paragraph">
<p>Allowed values:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong><code>true</code></strong> - wait for the content using the <a href="#default-waiting-configuration"><em>default wait</em></a> configuration</p>
</li>
<li>
<p><strong>a string</strong> - wait for the content using the <a href="#waiting-presets"><em>wait preset</em></a> with this name from the configuration</p>
</li>
<li>
<p><strong>a number</strong> - wait for the content for this many seconds, using the <a href="#default-waiting-configuration"><em>default retry interval</em></a> from the configuration</p>
</li>
<li>
<p><strong>a 2 element list of numbers</strong> - wait for the content using element 0 as the timeout seconds value, and element 1 as the retry interval seconds value</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Any other value will be interpreted as <code>false</code>.</p>
</div>
<div class="paragraph">
<p>The <code>wait</code> option allows Geb to wait an amount of time for content to appear on the page, instead of throwing a <code><a href="api/geb/error/RequiredPageContentNotPresent.html">RequiredPageContentNotPresent</a></code> exception if the content is not present when
requested.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class DynamicPageWithWaiting extends Page {
static content = {
dynamicallyAdded(wait: true) { $("p.dynamic") }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to DynamicPageWithWaiting
assert dynamicallyAdded.text() == "I'm here now"</code></pre>
</div>
</div>
<div class="paragraph">
<p>This is equivalent to:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class DynamicPageWithoutWaiting extends Page {
static content = {
dynamicallyAdded { $("p.dynamic") }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to DynamicPageWithoutWaiting
assert waitFor { dynamicallyAdded }.text() == "I'm here now"</code></pre>
</div>
</div>
<div class="paragraph">
<p>See the <a href="#waiting">section on waiting</a> for the semantics of the <code>waitFor()</code> method, that is used here internally.
Like <code>waitFor()</code> a <code><a href="api/geb/waiting/WaitTimeoutException.html">WaitTimeoutException</a></code> will be thrown if the wait timeout expires.</p>
</div>
<div class="paragraph">
<p>It is also possible to use <code>wait</code> when defining non-element content, such as a string or number.
Geb will wait until the content definition returns a value that conforms to the Groovy Truth.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class DynamicPageWithNonNavigatorWaitingContent extends Page {
static content = {
status { $("p.status") }
success(wait: true) { status.text().contains("Success") }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to DynamicPageWithNonNavigatorWaitingContent
assert success</code></pre>
</div>
</div>
<div class="paragraph">
<p>In this case, we are inherently waiting for the <code>status</code> content to be on the page and for it to contain the string “Success”.
If the <code>status</code> element is not present when we request <code>success</code>, the <code><a href="api/geb/error/RequiredPageContentNotPresent.html">RequiredPageContentNotPresent</a></code> exception that would be thrown is swallowed and Geb will try again after the
retry interval has expired.</p>
</div>
<div class="paragraph">
<p>You can modify the behaviour of content with <code>wait</code> option set to true if you use it together with <code>required</code> option set to false. Given a content definition:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">static content = {
dynamicallyAdded(wait: true, required: false) { $("p.dynamic") }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Then if wait timeout expires when retrieving <code>dynamicallyAdded</code>, there will be no <code>WaitTimeoutException</code> thrown, and the last closure evaluation value will be returned.
If there is an exception thrown during closure evaluation, it will be wrapped in an <a href="api/geb/waiting/UnknownWaitForEvaluationResult.html"><code>UnknownWaitForEvaluationResult</code></a> instance and returned.</p>
</div>
<div class="paragraph">
<p>Waiting content blocks are subject to “implicit assertions”.
See the section on <a href="#implicit-assertions">implicit assertions</a> for more information.</p>
</div>
</div>
<div class="sect4">
<h5 id="content-dsl-to-wait"><a class="link" href="#content-dsl-to-wait"><code>toWait</code></a></h5>
<div class="paragraph">
<p>Default value: <code>false</code></p>
</div>
<div class="paragraph">
<p>Allowed values are the same as for the <a href="#content-dsl-wait"><code>wait</code></a> option.</p>
</div>
<div class="paragraph">
<p>Can be used together with the <a href="#content-dsl-to"><code>to</code></a> option to specify that the page changing action performed when the content is clicked is asynchronous.
This essentially means that verification of page transition (“at checking”) should be wrapped in a <code>waitFor()</code> call.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithTemplateUsingToWaitOption extends Page {
static content = {
asyncPageLoadButton(to: AsyncPage, toWait: true) { $("button#load-content") } <i class="conum" data-value="1"></i><b>(1)</b>
}
}
class AsyncPage extends Page {
static at = { $("#async-content") }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Page change is asynchronous, e.g. an ajax call is involved.</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to PageWithTemplateUsingToWaitOption
asyncPageLoadButton.click()
assert page instanceof AsyncPage</code></pre>
</div>
</div>
<div class="paragraph">
<p>See the <a href="#waiting">section on waiting</a> for the semantics of the <code>waitFor()</code> method, that is used here internally.
Like <code>waitFor()</code> a <code><a href="api/geb/waiting/WaitTimeoutException.html">WaitTimeoutException</a></code> will be thrown if the wait timeout expires.</p>
</div>
</div>
<div class="sect4">
<h5 id="content-dsl-page"><a class="link" href="#content-dsl-page"><code>page</code></a></h5>
<div class="paragraph">
<p>Default value: <code>null</code></p>
</div>
<div class="paragraph">
<p>The <code>page</code> option allows the definition of a page the browser will be set to if the content describes a frame and is used in a <code>withFrame()</code> call.</p>
</div>
<div class="paragraph">
<p>Given the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;iframe id="frame-id" src="frame.html"&gt;&lt;/iframe&gt;
&lt;body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>…and the contents of frame.html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;span&gt;frame text&lt;/span&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>…the following will pass…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithFrame extends Page {
static content = {
myFrame(page: FrameDescribingPage) { $('#frame-id') }
}
}
class FrameDescribingPage extends Page {
static content = {
frameContentsText { $('span').text() }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to PageWithFrame
withFrame(myFrame) {
assert frameContentsText == 'frame text'
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="aliasing"><a class="link" href="#aliasing">5.3.2. Aliasing</a></h4>
<div class="paragraph">
<p>If you wish to have the same content definitions available under different names you can create a content definition that specifies <code>aliases</code> parameter:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class AliasingPage extends Page {
static content = {
someDiv { $("div#aliased") }
aliasedDiv(aliases: "someDiv")
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to AliasingPage
assert someDiv.@id == aliasedDiv.@id</code></pre>
</div>
</div>
<div class="paragraph">
<p>Remember that the aliased content has to be defined before the aliasing content, otherwise you will get a <code><a href="api/geb/error/InvalidPageContent.html">InvalidPageContent</a></code> exception.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="at-checker"><a class="link" href="#at-checker">5.4. “At” verification</a></h3>
<div class="paragraph">
<p>Each page can define a way to check whether the underlying browser is at the page that the page class actually represents.
This is done via a <code>static</code> <code>at</code> closure…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithAtChecker extends Page {
static at = { $("h1").text() == "Example" }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This closure can either return a <code>false</code> value or throw an <code>AssertionError</code> (via the <code>assert</code> method).
The <code>verifyAt()</code> method call will either:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>return <code>true</code> if the “at” checker passes</p>
</li>
<li>
<p>if <a href="#implicit-assertions">implicit assertions</a> are <a href="#implicit-assertions-mechanics">enabled</a> and the “at” checker fails it will throw an <code>AssertionError</code></p>
</li>
<li>
<p>return <code>false</code> if implicit assertions are not enabled and the the “at” checker fails</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Considering the example above you could use it like this…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageLeadingToPageWithAtChecker extends Page {
static content = {
link { $("a#to-page-with-at-checker") }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to PageLeadingToPageWithAtChecker
link.click()
page PageWithAtChecker
verifyAt()</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>verifyAt()</code> method is used by the <code>Browser.at()</code> method that takes a page class or instance.
It behaves the same as <code>verifyAt()</code> if the “at” checker fails and returns a page instance if the checker succeeds (which is useful if you wish to write <a href="#strong-typing">strongly typed</a> code with Geb)…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to PageLeadingToPageWithAtChecker
link.click()
at PageWithAtChecker</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>There is no need to perform “at” verification after navigating to a page using <a href="#using-pages"><code>to()</code> method</a> or clicking on content with <a href="#content-dsl-to"><code>to</code> option</a> specified as it is performed
implicitly in these cases.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>At checkers are subject to “implicit assertions”. See the section on <a href="#implicit-assertions">implicit assertions</a> for more information.</p>
</div>
<div class="paragraph">
<p>If you don&#8217;t wish to get an exception when “at” checking fails there are methods that return <code>false</code> in that case: <a href="api/geb/Page.html#verifyAtSafely(boolean)"><code>Page#verifyAtSafely()</code></a> and
<a href="api/geb/Browser.html#isAt(Class%3C%3F%20extends%20Page%3E,%20boolean)"><code>Browser#isAt(Class&lt;? extends Page&gt;)</code></a>.</p>
</div>
<div class="paragraph">
<p>As mentioned previously, when a content template defines a <a href="#content-dsl-to"><code>to</code> option</a> of more than one page the page&#8217;s <code>verifyAt()</code> method is used to determine which one of the pages to use.
In this situation, any <code>AssertionError</code> thrown by “at” checkers are suppressed.</p>
</div>
<div class="paragraph">
<p>The “at” checker is evaluated against the page instance, and can access defined content or any other variables or methods…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithAtCheckerUsingContent extends Page {
static at = { heading == "Example" }
static content = {
heading { $("h1").text() }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>If a page does not have an “at” checker, the <code>verifyAt()</code> and <code>at()</code> methods will throw an <code>UndefinedAtCheckerException</code>.
The same will happen if any of the pages in the list used as <code>to</code> content template option doesn&#8217;t define an “at” checker.</p>
</div>
<div class="paragraph">
<p>It can sometimes prove useful to wrap “at” verification in <code>waitFor()</code> calls by default - some drivers are known to return control after URL change before the page is fully loaded in some
circumstances or before one might consider it to be loaded. This behaviour can be required using <a href="#at-check-waiting"><code>atCheckWaiting</code> configuration entry</a>.</p>
</div>
<div class="sect3">
<h4 id="unexpected-pages"><a class="link" href="#unexpected-pages">Unexpected pages</a></h4>
<div class="paragraph">
<p>A list of unexpected pages can be provided via <a href="#unexpected-pages-config"><code>unexpectedPages</code> configuration entry</a>.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Note that this feature does not operate on HTTP response codes as these are not exposed by WebDriver thus Geb does not have access to them.
To use this feature your application has to render custom error pages that can be modeled as page classes and detected by an “at” checker.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>If configured, the classes from the list specified as <code>unexpectedPages</code> configuration entry will be checked for first when “at” checking is performed for any page, and an <code>UnexpectedPageException</code>
with an appropriate message will be raised if any of them is encountered.</p>
</div>
<div class="paragraph">
<p>Given that your application renders a custom error page when a page is not found with a text like "Sorry but we could not find that page", you can model that page with the following class:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageNotFoundPage extends Page {
static at = { $("#error-message").text() == "Sorry but we could not find that page" }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Then register that page in configuration:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">unexpectedPages = [PageNotFoundPage]</code></pre>
</div>
</div>
<div class="paragraph">
<p>When checking if the browser is at a page while the “at” checker for <code>PageNotFoundPage</code> is fulfilled, an <code>UnexpectedPageException</code> will be raised.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">try {
at ExpectedPage
assert false //should not get here
} catch (UnexpectedPageException e) {
assert e.message.startsWith("An unexpected page ${PageNotFoundPage.name} was encountered")
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Unexpected pages will be checked for whenever “at” checking is performed, even implicitly like when using <code>to</code> content template option or passing one or many page classes to <code>Navigator.click()</code>
method.</p>
</div>
<div class="paragraph">
<p>It is possible to explicitly check if the browser is at an unexpected page.
Following will pass without throwing an <code>UnexpectedPageException</code> if “at” checking for <code>PageNotFoundPage</code> succeeds:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">at PageNotFoundPage</code></pre>
</div>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The <a href="#at-check-waiting">global <code>atCheckWaiting</code> configuration</a> does not apply when checking for unexpected pages.
That is, even if configuration calls for implicitly wrapping “at checks” in a <code>waitFor()</code> call it is not done when verifying “at checkers” of unexpected pages.
This is due to the fact that “at checkers” for unexpected pages are not fulfilled for most of the time and they are checked for every <code>at()</code> and <code>to()</code> call thus wrapping them in an implicit
<code>waitFor()</code> call would make these methods extremely slow.
The <a href="#page-level-at-check-waiting">page level <code>atCheckWaiting</code> configuration</a> on the other hand applies to unexpected pages so you can use it if you actually need to wait around “at checkers”
of such pages.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="page-urls"><a class="link" href="#page-urls">5.5. Page URLs</a></h3>
<div class="paragraph">
<p>Pages can define URLs via the <code>static</code> <code>url</code> property.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithUrl extends Page {
static url = "example"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The url is used when using the browser <code>to()</code> method.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to PageWithUrl
assert currentUrl.endsWith("example")</code></pre>
</div>
</div>
<div class="paragraph">
<p>See the section on <a href="#base-url">the base url</a> for notes about URLs and slashes.</p>
</div>
<div class="sect3">
<h4 id="url-fragments"><a class="link" href="#url-fragments">5.5.1. URL fragments</a></h4>
<div class="paragraph">
<p>Pages can also define URL fragment identifiers (the part after a <code>#</code> character at the end of an url) via the <code>static</code> <code>fragment</code> property.</p>
</div>
<div class="paragraph">
<p>The value assigned can be either a <code>String</code> which will be used as is or a <code>Map</code> which will be translated into an <code>application/x-www-form-urlencoded</code> <code>String</code>.
The latter is particularly useful when dealing with single page applications that store state in the fragment identifier by form encoding it.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>There is no need to escape any of the strings used for url fragments as all the necessary escaping is performed by Geb.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Consider the following page which defines an url fragment in such single page application scenario:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithFragment extends Page {
static fragment = [firstKey: "firstValue", secondKey: "secondValue"]
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The fragment is then used when using the browser <code>to()</code> method.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to PageWithFragment
assert currentUrl.endsWith("#firstKey=firstValue&amp;secondKey=secondValue")</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can also use fragments which are dynamic - you can learn how in <a href="#advanced-page-navigation-url-fragments"><em>URL fragments</em> subsection of <em>Advanced page navigation</em> chapter</a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="page-level-at-check-waiting"><a class="link" href="#page-level-at-check-waiting">5.5.2. Page level <code>atCheckWaiting</code> configuration</a></h4>
<div class="paragraph">
<p>At checkers for a specific page can be configured to be implicitly wrapped with <code>waitFor()</code> calls.
This can be set with the <code>static</code> <code>atCheckWaiting</code> property.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithAtCheckWaiting extends Page {
static atCheckWaiting = true
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The possible values for the <code>atCheckWaiting</code> option are the same as for <a href="#content-dsl-wait">the <code>wait</code> content template option</a>.</p>
</div>
<div class="paragraph">
<p>The <code>atCheckWaiting</code> value configured at page level takes priority over the global value specified in <a href="#at-check-waiting">the configuration</a>.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="advanced-page-navigation"><a class="link" href="#advanced-page-navigation">5.6. Advanced page navigation</a></h3>
<div class="paragraph">
<p>Page classes can customise how they generate URLs when used in conjunction with the browser <code>to()</code> method.</p>
</div>
<div class="paragraph">
<p>Consider the following example…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageObjectsPage extends Page {
static url = "pages"
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive(baseUrl: "http://www.gebish.org/") {
to PageObjectsPage
assert currentUrl == "http://www.gebish.org/pages"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>to()</code> method can also take arguments…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class ManualsPage extends Page {
static url = "manual"
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive(baseUrl: "http://www.gebish.org/") {
to ManualsPage, "0.9.3", "index.html"
assert currentUrl == "http://www.gebish.org/manual/0.9.3/index.html"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Any arguments passed to the <code>to()</code> method after the page class are converted to a URL path by calling <code>toString()</code> on each argument and joining them with “<code>/</code>”.</p>
</div>
<div class="paragraph">
<p>However, this is extensible.
You can specify how a set of arguments is converted to a URL path to be added to the page URL.
This is done by overriding the <code><a href="api/geb/Page.html#convertToPath(java.lang.Object)">convertToPath()</a></code> method.
The <code><a href="api/geb/Page.html">Page</a></code> implementation of this method looks like this…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">String convertToPath(Object[] args) {
args ? '/' + args*.toString().join('/') : ""
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can either overwrite this catchall method to control path conversion for all invocations or provide an overloaded version for a specific type signature.
Consider the following…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class Manual {
String version
}
class ManualsPage extends Page {
static url = "manual"
String convertToPath(Manual manual) {
"/${manual.version}/index.html"
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def someManualVersion = new Manual(version: "0.9.3")
Browser.drive(baseUrl: "http://www.gebish.org/") {
to ManualsPage, someManualVersion
assert currentUrl == "http://www.gebish.org/manual/0.9.3/index.html"
}</code></pre>
</div>
</div>
<div class="sect3">
<h4 id="named-params"><a class="link" href="#named-params">5.6.1. Named params</a></h4>
<div class="paragraph">
<p>Any type of argument can be used with the <code>to()</code> method, <strong>except</strong> named parameters (i.e. a <code>Map</code>).
Named parameters are <strong>always</strong> interpreted as query parameters and they are never sent
Using the classes from the above example…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def someManualVersion = new Manual(version: "0.9.3")
Browser.drive(baseUrl: "http://www.gebish.org/") {
to ManualsPage, someManualVersion, flag: true
assert currentUrl == "http://www.gebish.org/manual/0.9.3/index.html?flag=true"
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="advanced-page-navigation-url-fragments"><a class="link" href="#advanced-page-navigation-url-fragments">5.6.2. URL fragments</a></h4>
<div class="paragraph">
<p>An instance of <code><a href="api/geb/url/UrlFragment.html">UrlFragment</a></code> can be passed as an argument which follows a page class or instance when calling <code>to()</code> in order to dynamically control the fragment identifier part of the url.
The <code>UrlFragment</code> class comes with two static factory methods: <a href="api/geb/url/UrlFragment.html#of(java.lang.String)">one for creating a fragment from an explicit <code>String</code></a> and <a href="api/geb/url/UrlFragment.html#of(Map&lt;?,%20?&gt;)">one for creating a fragment from a <code>Map</code> which is then form encoded</a>.</p>
</div>
<div class="paragraph">
<p>The following shows a usage example utilising the classes from the examples above…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive(baseUrl: "http://www.gebish.org/") {
to ManualsPage, UrlFragment.of("advanced-page-navigation"), "0.9.3", "index.html"
assert currentUrl == "http://www.gebish.org/manual/0.9.3/index.html#advanced-page-navigation"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you are using <a href="#parameterized-pages">parameterized pages</a> and you wish the fragment to be determined dynamically, e.g. based on page properties then you can override the <code><a href="api/geb/Page.html#getPageFragment()">getPageFragment()</a></code> method:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class ParameterizedManualsPage extends Page {
String version
String section
@Override
String convertToPath(Object[] args) {
"manual/$version/index.html"
}
@Override
UrlFragment getPageFragment() {
UrlFragment.of(section)
}
}
Browser.drive(baseUrl: "http://www.gebish.org/") {
to new ParameterizedManualsPage(version: "0.9.3", section: "advanced-page-navigation")
assert currentUrl == "http://www.gebish.org/manual/0.9.3/index.html#advanced-page-navigation"
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="parameterized-pages"><a class="link" href="#parameterized-pages">5.7. Parametrized pages</a></h3>
<div class="paragraph">
<p><code>Browser</code> methods like <code><a href="api/geb/Browser.html#to(java.util.Map,%20T,%20java.lang.Object)">to()</a></code>, <code><a href="api/geb/Browser.html#via(java.util.Map,%20T,%20java.lang.Object)">via()</a></code>, <code><a href="api/geb/Browser.html#at(T)">at()</a></code> and <code><a href="api/geb/Browser.html#page(Class&lt;T&gt;)">page()</a></code> accept not only page classes but page instances as well.
This is useful for example when parameterizing pages to use property values in “at” checkers:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class BooksPage extends Page {
static content = {
book { bookTitle -&gt; $("a", text: bookTitle) }
}
}
class BookPage extends Page {
String forBook
static at = { forBook == bookTitle }
static content = {
bookTitle { $("h1").text() }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to BooksPage
book("The Book of Geb").click()
at(new BookPage(forBook: "The Book of Geb"))
}</code></pre>
</div>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Manually instantiated pages have to be initialized before they can be used.
Initialization is performed as part of the <code>Browser</code> methods mentioned above.
Failing to pass the page instance to one of these methods and calling any method on an uninitialized page instance might result in a <code>PageInstanceNotInitializedException</code>.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="pages-inheritance"><a class="link" href="#pages-inheritance">5.8. Inheritance</a></h3>
<div class="paragraph">
<p>Pages can be arranged in an inheritance hierarchy. The content definitions are merged…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class BasePage extends Page {
static content = {
heading { $("h1") }
}
}
class SpecializedPage extends BasePage {
static content = {
footer { $("div.footer") }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to SpecializedPage
assert heading.text() == "Specialized page"
assert footer.text() == "This is the footer"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>If a subclass defines a content template with the same name as a content template defined in a superclass, the subclass version replaces the version from the superclass.</p>
</div>
</div>
<div class="sect2">
<h3 id="lifecycle-hooks"><a class="link" href="#lifecycle-hooks">5.9. Lifecycle hooks</a></h3>
<div class="paragraph">
<p>Page classes can optionally implement methods that are called when the page is set as the browser&#8217;s current page and when it is swapped out for another page. This can be used to transfer state between pages.</p>
</div>
<div class="sect3">
<h4 id="code-onload-page-previouspage-code"><a class="link" href="#code-onload-page-previouspage-code">5.9.1. <code>onLoad(Page previousPage)</code></a></h4>
<div class="paragraph">
<p>The <code>onLoad()</code> method is called with previous page object instance when the page becomes the new page object for a browser.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class FirstPage extends Page {
}
class SecondPage extends Page {
String previousPageName
void onLoad(Page previousPage) {
previousPageName = previousPage.class.simpleName
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to FirstPage
to SecondPage
assert page.previousPageName == "FirstPage"
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="code-onunload-page-newpage-code"><a class="link" href="#code-onunload-page-newpage-code">5.9.2. <code>onUnload(Page newPage)</code></a></h4>
<div class="paragraph">
<p>The <code>onUnload()</code> method is called with next page object instance when the page is being replaced as the page object for the browser.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class FirstPage extends Page {
String newPageName
void onUnload(Page newPage) {
newPageName = newPage.class.simpleName
}
}
class SecondPage extends Page {
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
def firstPage = to FirstPage
to SecondPage
assert firstPage.newPageName == "SecondPage"
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="dealing-with-frames"><a class="link" href="#dealing-with-frames">5.10. Dealing with frames</a></h3>
<div class="paragraph">
<p>Frames might seem a thing of the past, but if you&#8217;re accessing or testing some legacy application with Geb, you might still need to deal with them.
Thankfully, Geb makes working with them groovier thanks to the <code>withFrame()</code> method which is available on <code>Browser</code>, <code>Page</code> and <code>Module</code> instances.</p>
</div>
<div class="sect3">
<h4 id="executing-code-in-the-context-of-a-frame"><a class="link" href="#executing-code-in-the-context-of-a-frame">5.10.1. Executing code in the context of a frame</a></h4>
<div class="paragraph">
<p>There are multiple flavours of the <code>withFrame()</code> method, but for all of them the last closure parameter is executed in the context of a frame specified by the first parameter, and after the execution
the page is restored to what it was before the call:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>withFrame(String, Closure)</code> - <code>String</code> parameter contains the name or id of a frame element</p>
</li>
<li>
<p><code>withFrame(int, Closure)</code> - <code>int</code> parameter contains the index of the frame element, that is, if a page has three frames, the first frame would be at index <code>0</code>, the second at index <code>1</code> and the third
at index <code>2</code></p>
</li>
<li>
<p><code>withFrame(Navigator, Closure)</code> - <code>Navigator</code> parameter should contain a frame element</p>
</li>
<li>
<p><code>withFrame(SimplePageContent, Closure)</code> - <code>SimplePageContent</code>, which is a type returned by content templates, should contain a frame element</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Given the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;iframe name="header" src="frame.html"&gt;&lt;/iframe&gt;
&lt;iframe id="footer" src="frame.html"&gt;&lt;/iframe&gt;
&lt;iframe id="inline" src="frame.html"&gt;&lt;/iframe&gt;
&lt;span&gt;main&lt;/span&gt;
&lt;body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>…the code for frame.html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;span&gt;frame text&lt;/span&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>…and a page class…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageWithFrames extends Page {
static content = {
footerFrame { $('#footer') }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>…then this code will pass…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to PageWithFrames
withFrame('header') { assert $('span').text() == 'frame text' }
withFrame('footer') { assert $('span').text() == 'frame text' }
withFrame(0) { assert $('span').text() == 'frame text' }
withFrame($('#footer')) { assert $('span').text() == 'frame text' }
withFrame(footerFrame) { assert $('span').text() == 'frame text' }
assert $('span').text() == 'main'</code></pre>
</div>
</div>
<div class="paragraph">
<p>If a frame cannot be found for a given first argument of the <code>withFrame()</code> call, then <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/NoSuchFrameException.html">NoSuchFrameException</a></code> is thrown.</p>
</div>
</div>
<div class="sect3">
<h4 id="switching-pages-and-frames-at-once"><a class="link" href="#switching-pages-and-frames-at-once">5.10.2. Switching pages and frames at once</a></h4>
<div class="paragraph">
<p>All of the aforementioned <code>withFrame()</code> variants also accept an optional second argument (a page class or a page instance) which allows to switch page for the execution of the closure passed as
the last parameter. If the page used specifies an “at” checker it will be verified after switching the context to the frame.</p>
</div>
<div class="paragraph">
<p>Given the html and page class from the previous example the following is an example usage with a page class:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class PageDescribingFrame extends Page {
static content = {
text { $("span").text() }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to PageWithFrames
withFrame('header', PageDescribingFrame) {
assert page instanceof PageDescribingFrame
assert text == "frame text"
}
assert page instanceof PageWithFrames</code></pre>
</div>
</div>
<div class="paragraph">
<p>And this is how an example usage with a page instance looks like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class ParameterizedPageDescribingFrame extends Page {
String expectedFrameText
static at = { text == expectedFrameText }
static content = {
text { $("span").text() }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to PageWithFrames
withFrame('header', new ParameterizedPageDescribingFrame(expectedFrameText: "frame text")) {
assert page instanceof ParameterizedPageDescribingFrame
}
assert page instanceof PageWithFrames</code></pre>
</div>
</div>
<div class="paragraph">
<p>It is also possible to <a href="#content-dsl-page">specify a page to switch to for a page content that describes a frame</a>.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="modules"><a class="link" href="#modules">6. Modules</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Modules are re-usable definitions of content that can be used across multiple pages.
They are useful for modelling things like UI widgets that are used across multiple pages, or even for defining more complex UI elements in a page.</p>
</div>
<div class="paragraph">
<p>They are defined in a manner similar to pages, but extend <code><a href="api/geb/Module.html">Module</a></code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class FormModule extends Module {
static content = {
button { $("input", type: "button") }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Pages can “include” modules using the following syntax…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class ModulePage extends Page {
static content = {
form { module FormModule }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>module</code> method returns an instance of a module class which can then be used in the following way…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to ModulePage
form.button.click()
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Modules can also be parameterised…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class ParameterizedModule extends Module {
String formId
static content = {
button {
$("form", id: formId).find("input", type: "button")
}
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Where the parameters are passed to constructor of the module…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class ParameterizedModulePage extends Page {
static content = {
form { id -&gt; module(new ParameterizedModule(formId: id)) }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to ParameterizedModulePage
form("personal-data").button.click()
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Modules can also include other modules…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class OuterModule extends Module {
static content = {
form { module FormModule }
}
}
class OuterModulePage extends Page {
static content = {
outerModule { module OuterModule }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to OuterModulePage
outerModule.form.button.click()
}</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="base-and-context"><a class="link" href="#base-and-context">6.1. Base and context</a></h3>
<div class="paragraph">
<p>Modules can be localised to a specific section of the page that they are used in, or they can specify an absolute context as part of their definition.
There are two ways that a modules base/context can be defined.</p>
</div>
<div class="paragraph">
<p>Module can be based on a <code>Navigator</code> instance…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class FormModule extends Module {
static content = {
button { $("input", type: "button") }
}
}
class PageDefiningModuleWithBase extends Page {
static content = {
form { $("form").module(FormModule) }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to PageDefiningModuleWithBase
form.button.click()
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>It can also be done outside of a content definition…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go "/"
$("form").module(FormModule).button.click()
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can define a <code>Navigator</code> context when including the module using the above syntax.
This now means that calls to <em>all</em> <code>Navigator</code> (e.g. <code>$()</code>) method calls that occur within the module are against the given context (in this case, the <code>form</code> element).</p>
</div>
<div class="paragraph">
<p>However, module classes can also define their own base…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class FormModuleWithBase extends FormModule {
static base = { $("form") }
}
class PageUsingModuleWithBase extends Page {
static content = {
form { module FormModuleWithBase }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to PageUsingModuleWithBase
form.button.click()
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Basing a module on a <code>Navigator</code> and defining a base in a module can be combined. Consider the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;div class="a"&gt;
&lt;form&gt;
&lt;input name="thing" value="a"/&gt;
&lt;/form&gt;
&lt;/div&gt;
&lt;div class="b"&gt;
&lt;form&gt;
&lt;input name="thing" value="b"/&gt;
&lt;/form&gt;
&lt;/div&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>And the following content definitions…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class ThingModule extends Module {
static base = { $("form") }
static content = {
thingValue { thing().value() }
}
}
class ThingsPage extends Page {
static content = {
formA { $("div.a").module(ThingModule) }
formB { $("div.b").module(ThingModule) }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Then they can be used in the following way…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to ThingsPage
assert formA.thingValue == "a"
assert formB.thingValue == "b"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>If the module declares a base, it is always calculated <em>relative</em> to the <code>Navigator</code> used in the initialization statement.
If the initialization statement does not use a <code>Navigator</code>, the module&#8217;s base is calculated relative to the document root.</p>
</div>
</div>
<div class="sect2">
<h3 id="code-module-code-is-a-code-navigator-code"><a class="link" href="#code-module-code-is-a-code-navigator-code">6.2. <code>Module</code> is-a <code>Navigator</code></a></h3>
<div class="paragraph">
<p>Modules always have a base navigator associated with them (if you don&#8217;t specify a base for a module at all then it will be assigned the root element of the document as the base) so it is natural to
think of them as navigators.
Keeping in mind that <code>Module</code> implements <code>Navigator</code> and considering the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;form method="post" action="login"&gt;
&lt;input name="login" type="text"&gt;&lt;/input&gt;
&lt;input name="password" type="password"&gt;&lt;/input&gt;
&lt;input type="submit" value="Login"&gt;&lt;/input&gt;
&lt;/from&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>As well as these content definitions…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class LoginFormModule extends Module {
static base = { $("form") }
}
class LoginPage extends Page {
static content = {
form { module LoginFormModule }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following will pass…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to LoginPage
assert form.@method == "post"
assert form.displayed
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>It&#8217;s also possible to use <code>Navigator</code> methods inside of a module implementation…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class LoginFormModule extends Module {
String getAction() {
getAttribute("action")
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to LoginPage
assert form.action == "login"
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="reusing-modules-across-pages"><a class="link" href="#reusing-modules-across-pages">6.3. Reusing modules across pages</a></h3>
<div class="paragraph">
<p>As previously stated, modules can be used to model page fragments that are reused across multiple pages.
For example, many different types of pages in your application may show information about the user&#8217;s shopping cart.
You could handle this with modules…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class CartInfoModule extends Module {
static content = {
section { $("div.cart-info") }
itemCount { section.find("span.item-count").text().toInteger() }
totalCost { section.find("span.total-cost").text().toBigDecimal() }
}
}
class HomePage extends Page {
static content = {
cartInfo { module CartInfoModule }
}
}
class OtherPage extends Page {
static content = {
cartInfo { module CartInfoModule }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Modules work well for this.</p>
</div>
</div>
<div class="sect2">
<h3 id="repeating-content"><a class="link" href="#repeating-content">6.4. Using modules for repeating content</a></h3>
<div class="paragraph">
<p>Other than content that is repeated on different pages (like the shopping cart mentioned above), pages also can have content that is repeated on the page itself.
On a checkout page, the contents of the shopping cart could be summarized with the product name, the quantity and price for each product contained.
For this kind of page, a list of modules can be collected using the <code>moduleList()</code> methods of <code>Navigator</code>.</p>
</div>
<div class="paragraph">
<p>Consider the following HTML for our cart contents:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;Product&lt;/th&gt;&lt;th&gt;Quantity&lt;/th&gt;&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The Book Of Geb&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;5.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geb Single-User License&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;99.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geb Multi-User License&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;199.99&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can model one line of the table like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class CartRow extends Module {
static content = {
cell { $("td", it) }
productName { cell(0).text() }
quantity { cell(1).text().toInteger() }
price { cell(2).text().toBigDecimal() }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>And define a list of CartRows in our Page:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class CheckoutPage extends Page {
static content = {
cartItems {
$("table tr").tail().moduleList(CartRow) // tailing to skip the header row
}
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Because the return value of <code>cartItems</code> is a list of CartRow instances, we can use any of the usual collection methods:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert cartItems.every { it.price &gt; 0.0 }</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can also access the cart items using subscript operator together with an index or a range of indexes:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert cartItems[0].productName == "The Book Of Geb"
assert cartItems[1..2]*.productName == ["Geb Single-User License", "Geb Multi-User License"]</code></pre>
</div>
</div>
<div class="paragraph">
<p>Keep in mind that you can use parametrized module instances to create lists of modules for repeating content:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class ParameterizedCartRow extends Module {
def nameIndex
def quantityIndex
def priceIndex
static content = {
cell { $("td", it) }
productName { cell(nameIndex).text() }
quantity { cell(quantityIndex).text().toInteger() }
price { cell(priceIndex).text().toBigDecimal() }
}
}
class CheckoutPageWithParametrizedCart extends Page {
static content = {
cartItems {
$("table tr").tail().moduleList {
new ParameterizedCartRow(nameIndex: 0, quantityIndex: 1, priceIndex: 2)
}
}
}
}</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>You might be wondering why the <code>moduleList()</code> method flavour that allows the use parameterized module instances takes a closure instead of a module instance, which is what the <code>module()</code> method for
creating parameterized modules does.
If it took a single instance then it could only initialize it multiple times and return a list which would contain the same instance but many times!
To be able to return a list of different instances it&#8217;s needs a closure which acts as a factory of module instances.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="the-content-dsl"><a class="link" href="#the-content-dsl">6.5. The content DSL</a></h3>
<div class="paragraph">
<p>The Content DSL used for modules is <em>exactly</em> the same as the <a href="#content-dsl">one used for pages</a>, so all of the same options and techniques can be used.</p>
</div>
</div>
<div class="sect2">
<h3 id="inheritance"><a class="link" href="#inheritance">6.6. Inheritance</a></h3>
<div class="paragraph">
<p>Modules can use inheritance in the <a href="#pages-inheritance">same way that pages can</a>.
That is, their content definitions are merged with any content redefined in the subclass taking precedence of the superclass.</p>
</div>
</div>
<div class="sect2">
<h3 id="form-control-modules"><a class="link" href="#form-control-modules">6.7. Form control modules</a></h3>
<div class="paragraph">
<p>If you are using Geb in <a href="#strong-typing">a strongly typed manner</a> you might consider using the provided <a href="api/geb/module/package-summary.html">modules modelling form controls</a> instead of
<a href="#setting-form-control-values">manipulating them directly using the <code>Navigator</code> API</a>.
This will result in longer content definitions but using them will be easier because you won&#8217;t have to remember what is the meaning of <code>value()</code> calls for different types of controls.
This is for example the case when manipulating checkboxes for which checking and unchecking is achieved by passing booleans to <code>value()</code> when interacting with them via <code>Navigator</code> API.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>All of these modules (apart from <a href="#radio-buttons-module"><code>RadioButtons</code></a>) are expecting to <a href="#base-and-context">be based</a> on single element navigators and will throw <code>InvalidModuleBaseException</code> with an
appropriate message if that is not the case.
They also verify that the base contains an element of the expected type providing better error reporting in case of mistakenly selecting an incorrect element as the base.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="form-element"><a class="link" href="#form-element">6.7.1. <code>FormElement</code></a></h4>
<div class="paragraph">
<p><code><a href="api/geb/module/FormElement.html">FormElement</a></code> is a base class for all modules modelling form controls (apart from <a href="#radio-buttons-module"><code>RadioButtons</code></a>) and provides shortcut property methods for checking if a control is disabled or read
only.
You will usually call these methods on the module classes for specific control types and rarely use this module directly.</p>
</div>
<div class="paragraph">
<p>Given the html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;input disabled="disabled" name="disabled"/&gt;
&lt;input readonly="readonly" name="readonly"/&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Following is an example of using the shortcut property methods provided…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert $(name: "disabled").module(FormElement).disabled
assert !$(name: "disabled").module(FormElement).enabled
assert $(name: "readonly").module(FormElement).readOnly
assert !$(name: "readonly").module(FormElement).editable</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="checkbox-module"><a class="link" href="#checkbox-module">6.7.2. <code>Checkbox</code></a></h4>
<div class="paragraph">
<p>The <code><a href="api/geb/module/Checkbox.html">Checkbox</a></code> module provides utility methods for checking and unchecking checkboxes as well as property methods for retrieving their state.</p>
</div>
<div class="paragraph">
<p>Given the html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;input type="checkbox" name="flag"/&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>It can be used this way…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def checkbox = $(name: "flag").module(Checkbox)
assert !checkbox.checked
assert checkbox.unchecked
checkbox.check()
assert checkbox.checked
checkbox.uncheck()
assert checkbox.unchecked</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="code-select-code"><a class="link" href="#code-select-code">6.7.3. <code>Select</code></a></h4>
<div class="paragraph">
<p>The <code><a href="api/geb/module/Select.html">Select</a></code> module provides property methods for selecting options as well as retrieving selected option&#8217;s value and text of a single choice select element.</p>
</div>
<div class="paragraph">
<p>Given the html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;select name="artist"&gt;
&lt;option value="1"&gt;Ima Robot&lt;/option&gt;
&lt;option value="2"&gt;Edward Sharpe and the Magnetic Zeros&lt;/option&gt;
&lt;option value="3"&gt;Alexander&lt;/option&gt;
&lt;/select&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>It can be used this way…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def select = $(name: "artist").module(Select)
select.selected = "2"
assert select.selected == "2"
assert select.selectedText == "Edward Sharpe and the Magnetic Zeros"
select.selected = "Alexander"
assert select.selected == "3"
assert select.selectedText == "Alexander"</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="code-multipleselect-code"><a class="link" href="#code-multipleselect-code">6.7.4. <code>MultipleSelect</code></a></h4>
<div class="paragraph">
<p>The <code><a href="api/geb/module/MultipleSelect.html">MultipleSelect</a></code> module provides property methods for selecting options as well as retrieving selected option&#8217;s value and text of a multiple choice select element.
These methods take and return lists of strings.</p>
</div>
<div class="paragraph">
<p>Given the html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;select name="genres" multiple&gt;
&lt;option value="1"&gt;Alt folk&lt;/option&gt;
&lt;option value="2"&gt;Chiptunes&lt;/option&gt;
&lt;option value="3"&gt;Electroclash&lt;/option&gt;
&lt;option value="4"&gt;G-Funk&lt;/option&gt;
&lt;option value="5"&gt;Hair metal&lt;/option&gt;
&lt;/select&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>It can be used this way…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def multipleSelect = $(name: "genres").module(MultipleSelect)
multipleSelect.selected = ["2", "3"]
assert multipleSelect.selected == ["2", "3"]
assert multipleSelect.selectedText == ["Chiptunes", "Electroclash"]
multipleSelect.selected = ["G-Funk", "Hair metal"]
assert multipleSelect.selected == ["4", "5"]
assert multipleSelect.selectedText == ["G-Funk", "Hair metal"]</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="code-textinput-code"><a class="link" href="#code-textinput-code">6.7.5. <code>TextInput</code></a></h4>
<div class="paragraph">
<p>The <code><a href="api/geb/module/TextInput.html">TextInput</a></code> module provides property methods for setting and retrieving text of an input text element.</p>
</div>
<div class="paragraph">
<p>Given the html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;input type="text" name="language"/&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>It can be used this way…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def input = $(name: "language").module(TextInput)
input.text = "Groovy"
assert input.text == "Groovy"</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="code-textarea-code"><a class="link" href="#code-textarea-code">6.7.6. <code>Textarea</code></a></h4>
<div class="paragraph">
<p>The <code><a href="api/geb/module/Textarea.html">Textarea</a></code> module provides property methods for setting and retrieving text of a textarea element.</p>
</div>
<div class="paragraph">
<p>Given the html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;textarea name="language"/&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>It can be used this way…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def textarea = $(name: "language").module(Textarea)
textarea.text = "Groovy"
assert textarea.text == "Groovy"</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="code-fileinput-code"><a class="link" href="#code-fileinput-code">6.7.7. <code>FileInput</code></a></h4>
<div class="paragraph">
<p>The <code><a href="api/geb/module/FileInput.html">FileInput</a></code> module provides a setter for the file location of a file input element.
The method takes a <code>File</code> instance.</p>
</div>
<div class="paragraph">
<p>Given the html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;input type="file" name="csv"/&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>It can be used this way…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def csvFile = new File("data.csv")
def input = $(name: "csv").module(FileInput)
input.file = csvFile</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="radio-buttons-module"><a class="link" href="#radio-buttons-module">6.7.8. <code>RadioButtons</code></a></h4>
<div class="paragraph">
<p>The <code><a href="api/geb/module/RadioButtons.html">RadioButtons</a></code> module provides property methods for checking radio buttons as well as retrieving selected button&#8217;s value and text of the label associated with it.</p>
</div>
<div class="paragraph">
<p>Given the html…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;body&gt;
&lt;label for="site-current"&gt;Search this site&lt;/label&gt;
&lt;input type="radio" id="site-current" name="site" value="current"&gt;
&lt;label for="site-google"&gt;Search Google
&lt;input type="radio" id="site-google" name="site" value="google"&gt;
&lt;/label&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>It can be used this way…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def radios = $(name: "site").module(RadioButtons)
radios.checked = "current"
assert radios.checked == "current"
assert radios.checkedLabel == "Search this site"
radios.checked = "Search Google"
assert radios.checked == "google"
assert radios.checkedLabel == "Search Google"</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="unwrapping-modules-returned-from-the-code-content-code-dsl"><a class="link" href="#unwrapping-modules-returned-from-the-code-content-code-dsl">6.8. Unwrapping modules returned from the <code>content</code> DSL</a></h3>
<div class="paragraph">
<p>For the sake of better error reporting, current implementation wraps any module declared within <code>content</code> block into
<code>geb.content.TemplateDerivedPageContent</code> instance.</p>
</div>
<div class="paragraph">
<p>Given a page defined as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class ModuleUnwrappingPage extends Page {
static content = {
theModule { module(UnwrappedModule) }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>And a custom module:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class UnwrappedModule extends Module {
static content = {
theContent { $(".the-content") }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>A module assignment to a variable of its declared type will fail with <code>GroovyCastException</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to ModuleUnwrappingPage
UnwrappedModule foo = theModule <i class="conum" data-value="1"></i><b>(1)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td><code>GroovyCastException</code> is thrown</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>An invocation of a method which takes a module as argument with its declared type will fail with <code>MissingMethodException</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">String getContentText(UnwrappedModule module) {
module.theContent.text()
}
Browser.drive {
to ModuleUnwrappingPage
getContentText(theModule) <i class="conum" data-value="1"></i><b>(1)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td><code>MissingMethodException</code> is thrown</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>As you may like or need to use strong typing for modules there is a way to do that. Module can be cast to its declared type with the Groovy <code>as</code> operator:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to ModuleUnwrappingPage
UnwrappedModule unwrapped = theModule as UnwrappedModule
getContentText(theModule as UnwrappedModule)
}</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Bear in mind that casting of a module to its declared type means the module gets unwrapped. By doing so the convenient error messages for such module are gone.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>What&#8217;s the trade-off? Calling <code>toString()</code> on any content element, including module, gives a meaningful path like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code>modules.ModuleUnwrappingPage -&gt; theModule: modules.UnwrappedModule -&gt; theContent: geb.navigator.NonEmptyNavigator</code></pre>
</div>
</div>
<div class="paragraph">
<p>Such paths can be seen in error messages and this is exactly what you are going to give away for unwrapped modules.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="configuration"><a class="link" href="#configuration">7. Configuration</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Geb provides a configuration mechanism that allows you to control various aspects of Geb in a flexible way.
At the heart of this is the <code><a href="api/geb/Configuration.html">Configuration</a></code> object, which the <code><a href="api/geb/Browser.html">Browser</a></code> and other objects query at runtime.</p>
</div>
<div class="paragraph">
<p>There are three general mechanisms for influencing configuration; <em>system properties</em>, <em>config script</em> and the <em>build adapter</em>.</p>
</div>
<div class="sect2">
<h3 id="mechanisms"><a class="link" href="#mechanisms">7.1. Mechanisms</a></h3>
<div class="sect3">
<h4 id="the-config-script"><a class="link" href="#the-config-script">7.1.1. The config script</a></h4>
<div class="paragraph">
<p>Geb attempts to load a <code><a href="http://docs.groovy-lang.org/docs/groovy-2.4.7/html/gapi/groovy/util/ConfigSlurper.html">ConfigSlurper</a></code> script named <code>GebConfig.groovy</code> from the <em>default package</em> (in other words, in the root of a directory that is on the classpath).
If it is not found, Geb will try to load a <code><a href="http://docs.groovy-lang.org/docs/groovy-2.4.7/html/gapi/groovy/util/ConfigSlurper.html">ConfigSlurper</a></code> class named <code>GebConfig</code> from the <em>default package</em> - this is useful if you run tests that use Geb from an IDE because you won&#8217;t have
to specify <code>GebConfig.groovy</code> as a resource, Geb will simply fall back to the compiled version of the script.
If both script and class are not found Geb will continue using all defaults.</p>
</div>
<div class="paragraph">
<p>First, the script is looked for with the <strong>executing thread&#8217;s context class loader</strong> and if it is not found, then it is looked for with the class loader that loaded Geb.
This covers 99% of scenarios out of the box perfectly well without any intervention.
If however you do need to configure the context class loader to load the config script, you <strong>must</strong> make sure that it is either the same as the class loader that loaded Geb or a child of it.
If the script is not found by both of those class loaders the procedure will be repeated but this time the class will be searched for - first using <strong>executing thread&#8217;s context class loader</strong> and then
using the class loader that loaded Geb.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If you are using a build tool such as <a href="http://gradle.org/">Gradle</a> or <a href="http://maven.apache.org/">Maven</a> that has the concept of test “resources”, then that directory is a suitable place.
You can also put your script together with your compilation source and then the compiled version of the script will be used.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect4">
<h5 id="environment-sensitivity"><a class="link" href="#environment-sensitivity">Environment sensitivity</a></h5>
<div class="paragraph">
<p>The Groovy <code><a href="http://docs.groovy-lang.org/docs/groovy-2.4.7/html/gapi/groovy/util/ConfigSlurper.html">ConfigSlurper</a></code> mechanism has built in support for environment sensitive configuration, and Geb leverages this by using the <strong><code>geb.env</code></strong> system property to determine the environment
to use.
An effective use of this mechanism is to configure different drivers based on the designated Geb “environment” (concrete details on how to do this further down).</p>
</div>
<div class="paragraph">
<p>How you set the environment system property is going to be dependent on the build system you are using.
For example, when using Gradle you could control the Geb environment by specifying it in the configuration of the test task running your tests…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">test {
systemProperty 'geb.env', 'windows'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Other build environments will allow you to do this in different ways.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="system-properties"><a class="link" href="#system-properties">7.1.2. System properties</a></h4>
<div class="paragraph">
<p>Some config options can be specified by system properties.
In general, config options specified by system properties will <em>override</em> values set in the config script.
See the config options below for which options are controllable via system properties.</p>
</div>
</div>
<div class="sect3">
<h4 id="build-adapter-config"><a class="link" href="#build-adapter-config">7.1.3. Build adapter</a></h4>
<div class="paragraph">
<p>The build adapter mechanism exists to allow Geb to integrate with development/build environments that logically dictate config options.</p>
</div>
<div class="paragraph">
<p>This mechanism works by loading the name of the class (fully qualified) by the system property <code>geb.build.adapter</code> that must implement the <code><a href="api/geb/BuildAdapter.html">BuildAdapter</a></code> interface.
Currently, the build adapter can only influence the base URL to use, and the location of the reports directory.</p>
</div>
<div class="paragraph">
<p>If the <code>geb.build.adapter</code> system property is not explicitly set, it defaults to `<a href="api/geb/buildadapter/SystemPropertiesBuildAdapter.html">SystemPropertiesBuildAdapter</a>.
As you can probably deduce, this default implementation uses system properties to specify values, so is usable in most circumstances.
See the linked API doc for the details of the specific system properties it looks for.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>While the default build adapter uses system properties, it should not be considered to be the same as system property configuration due to values in the config script taking precedence over the build
adapter which is not true for system properties.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="config-options"><a class="link" href="#config-options">7.2. Config options</a></h3>
<div class="sect3">
<h4 id="driver-implementation-configuration"><a class="link" href="#driver-implementation-configuration">7.2.1. Driver implementation</a></h4>
<div class="paragraph">
<p>The driver to use is specified by the config key <code>driver</code>, or the system property <code>geb.driver</code>.</p>
</div>
<div class="sect4">
<h5 id="factory-closure"><a class="link" href="#factory-closure">Factory closure</a></h5>
<div class="paragraph">
<p>In the config script it can be a closure that when invoked with no arguments returns an instance of <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/WebDriver.html">WebDriver</a></code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import org.openqa.selenium.firefox.FirefoxDriver
driver = { new FirefoxDriver() }</code></pre>
</div>
</div>
<div class="paragraph">
<p>This is the preferred mechanism, as it allows the most control over the drivers creation and configuration.</p>
</div>
<div class="paragraph">
<p>You can use the <code><a href="http://docs.groovy-lang.org/docs/groovy-2.4.7/html/gapi/groovy/util/ConfigSlurper.html">ConfigSlurper</a></code> mechanism&#8217;s environment sensitivity to configure different drivers per environment…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import org.openqa.selenium.htmlunit.HtmlUnitDriver
import org.openqa.selenium.remote.DesiredCapabilities
import org.openqa.selenium.remote.RemoteWebDriver
// default is to use htmlunit
driver = { new HtmlUnitDriver() }
environments {
// when system property 'geb.env' is set to 'remote' use a remote Firefox driver
remote {
driver = {
def remoteWebDriverServerUrl = new URL("http://example.com/webdriverserver")
new RemoteWebDriver(remoteWebDriverServerUrl, DesiredCapabilities.firefox())
}
}
}</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>WebDriver has the ability to drive browsers on a remote host, which is what we are using above.
For more information consult the WebDriver documentation on <code><a href="http://code.google.com/p/selenium/wiki/RemoteWebDriver">RemoteWebDriver</a></code> and <code><a href="http://code.google.com/p/selenium/wiki/RemoteWebDriverServer">RemoteWebDriverServer</a></code>.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect4">
<h5 id="driver-class-name"><a class="link" href="#driver-class-name">Driver class name</a></h5>
<div class="paragraph">
<p>The name of the driver class to use (it will be constructed with no arguments) can be specified as a string with the key <code>driver</code> in the config script or via the <code>geb.driver</code> system property
(the class must implement the <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/WebDriver.html">WebDriver</a></code> interface).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">driver = "org.openqa.selenium.firefox.FirefoxDriver"</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or it can be one of the following short names: <code>ie</code>, <code>htmlunit</code>, <code>firefox</code> or <code>chrome</code>. These will be implicitly expanded to their fully qualified class names…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">driver = "firefox"</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following table gives the possible short names that can be used:</p>
</div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Short Name</th>
<th class="tableblock halign-left valign-top">Driver</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>htmlunit</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-htmlunit-driver/3.6.0/org/openqa/selenium/htmlunit/HtmlUnitDriver.html">org.openqa.selenium.htmlunit.HtmlUnitDriver</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>firefox</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-firefox-driver/3.6.0/org/openqa/selenium/firefox/FirefoxDriver.html">org.openqa.selenium.firefox.FirefoxDriver</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ie</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-ie-driver/3.6.0/org/openqa/selenium/ie/InternetExplorerDriver.html">org.openqa.selenium.ie.InternetExplorerDriver</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>chrome</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-chrome-driver/3.6.0/org/openqa/selenium/chrome/ChromeDriver.html">org.openqa.selenium.chrome.ChromeDriver</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>edge</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/edge/EdgeDriver.html">org.openqa.selenium.edge.EdgeDriver</a></p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>If no explicit driver is specified then Geb will look for the following drivers on the classpath in the order they are listed in the above table.
If none of these classes can be found, a <a href="api/geb/error/UnableToLoadAnyDriversException.html"><code>UnableToLoadAnyDriversException</code></a> will be thrown.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="navigator-factory"><a class="link" href="#navigator-factory">7.2.2. Navigator factory</a></h4>
<div class="paragraph">
<p>It is possible to specify your own implementation of <a href="api/geb/navigator/factory/NavigatorFactory.html"><code>NavigatorFactory</code></a> via configuration.
This is useful if you want to extend the <code><a href="api/geb/navigator/Navigator.html">Navigator</a></code> class to provide your own behaviour extensions.</p>
</div>
<div class="paragraph">
<p>Rather than inject your own <code>NavigatorFactory</code>, it is simpler to inject a custom <a href="api/geb/navigator/factory/InnerNavigatorFactory.html"><code>InnerNavigatorFactory</code></a> which is a much simpler interface.
To do this, you can specify a closure for the config key <code>innerNavigatorFactory</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.Browser
import org.openqa.selenium.WebElement
innerNavigatorFactory = { Browser browser, List&lt;WebElement&gt; elements -&gt;
elements ? new MyCustomNavigator(browser, elements) : new MyCustomEmptyNavigator()
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This is a rather advanced use case.
If you need to do this, check out the source code or get in touch via the mailing list if you need help.</p>
</div>
</div>
<div class="sect3">
<h4 id="driver-caching-configuration"><a class="link" href="#driver-caching-configuration">7.2.3. Driver caching</a></h4>
<div class="paragraph">
<p>Geb&#8217;s ability to cache a driver and re-use it for the lifetime of the JVM (i.e. <a href="#implicit-driver-lifecycle">the implicit driver lifecycle</a>) can be disabled by setting the <code>cacheDriver</code> config option
to <code>false</code>.
However, if you do this you become <a href="#explicit-driver-lifecycle">responsible for quitting</a> every driver that is created at the appropriate time.</p>
</div>
<div class="paragraph">
<p>The default caching behavior is to cache the driver globally across the JVM.
If you are using Geb in multiple threads this may not be what you want, as neither Geb <code>Browser</code> objects nor WebDriver at the core is thread safe.
To remedy this, you can instruct Geb to cache the driver instance per thread by setting the config option <code>cacheDriverPerThread</code> to <code>true</code>.</p>
</div>
<div class="paragraph">
<p>Also, by default Geb will register a shutdown hook to quit any cached browsers when the JVM exits.
You can disable this by setting te config property <code>quitCachedDriverOnShutdown</code> to <code>false</code>.</p>
</div>
</div>
<div class="sect3">
<h4 id="base-url"><a class="link" href="#base-url">7.2.4. Base URL</a></h4>
<div class="paragraph">
<p>The <a href="#base-url">base URL</a> to be used can be specified by setting the <code>baseUrl</code> config property (with a <code>String</code>) value or via the build adapter (the default implementation of which looks at the
<code>geb.build.baseUrl</code> system property).
Any value set in the config script will take precedence over the value provided by the build adapter.</p>
</div>
</div>
<div class="sect3">
<h4 id="waiting-configuration"><a class="link" href="#waiting-configuration">7.2.5. Waiting</a></h4>
<div class="paragraph">
<p>The <a href="#waiting"><code>waitFor()</code></a> methods available on browser, page and module objects can be affected by configuration (this is also true for <a href="#content-dsl-wait">implicitly waiting content</a>).
It is possible to specify default values for the timeout and retry interval, and to define presets of these values to be referred to by name.</p>
</div>
<div class="sect4">
<h5 id="default-waiting-configuration"><a class="link" href="#default-waiting-configuration">Defaults</a></h5>
<div class="paragraph">
<p>Defaults can be specified via:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">waiting {
timeout = 10
retryInterval = 0.5
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Both values are optional and in seconds. If unspecified, the values of <code>5</code> for <code>timeout</code> and <code>0.1</code> for <code>retryInterval</code>.</p>
</div>
</div>
<div class="sect4">
<h5 id="waiting-presets"><a class="link" href="#waiting-presets">Presets</a></h5>
<div class="paragraph">
<p>Presets can be specified via:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">waiting {
presets {
slow {
timeout = 20
retryInterval = 1
}
quick {
timeout = 1
}
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here we have defined two presets, <code>slow</code> and <code>quick</code>.
Notice that the <code>quick</code> preset does not specify a <code>retryInterval</code> value; defaults will be substituted in for any missing values (i.e. giving the <code>quick</code> preset the default <code>retryInterval</code> value of
<code>0.1</code>).</p>
</div>
</div>
<div class="sect4">
<h5 id="failure-causes"><a class="link" href="#failure-causes">Failure causes</a></h5>
<div class="paragraph">
<p>When waiting fails because the condition throws an exception be it an assertion failure or any other exception then that exception is set as the cause of <code>WaitTimeoutException</code> thrown by Geb.
This usually provides fairly good diagnostics of what went wrong.
Unfortunately some runtimes, namely Maven Surefire Plugin, don&#8217;t print full exception stacktraces and exclude the cause from them.
To make diagnostics easier in such situations it&#8217;s possible to configure Geb to include string representation of the cause as part of <code>WaitTimeoutException</code> message:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">waiting {
includeCauseInMessage = true
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="at-check-waiting"><a class="link" href="#at-check-waiting">7.2.6. Waiting in “at” checkers</a></h4>
<div class="paragraph">
<p>At checkers can be configured to be implicitly wrapped with <code>waitFor()</code> calls. This can be set with:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">atCheckWaiting = true</code></pre>
</div>
</div>
<div class="paragraph">
<p>The possible values for the <code>atCheckWaiting</code> property are consistent with the <a href="#content-dsl-wait">ones for <code>wait</code> option of content template definitions</a>.</p>
</div>
<div class="paragraph">
<p>This global setting can also be overridden on a <a href="#page-level-at-check-waiting">per page class basis</a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="waiting-for-base-navigator"><a class="link" href="#waiting-for-base-navigator">7.2.7. Waiting for base navigator</a></h4>
<div class="paragraph">
<p>Sometimes Firefox driver times out when trying to find the root HTML element of the page. This manifests itself in an error similar to:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>org.openqa.selenium.NoSuchElementException: Unable to locate element: {"method":"tag name","selector":"html"}
Command duration or timeout: 576 milliseconds
For documentation on this error, please visit: http://seleniumhq.org/exceptions/no_such_element.html</pre>
</div>
</div>
<div class="paragraph">
<p>You can prevent this error from happening by configuring a wait timeout to use when the driver is locating the root HTML element, using:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">baseNavigatorWaiting = true</code></pre>
</div>
</div>
<div class="paragraph">
<p>The possible values for the <code>baseNavigatorWaiting</code> option are consistent with the <a href="#content-dsl-wait">ones for <code>wait</code> option of content template definitions</a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="unexpected-pages-config"><a class="link" href="#unexpected-pages-config">7.2.8. Unexpected pages</a></h4>
<div class="paragraph">
<p>The <code>unexpectedPages</code> configuration property allows to specify a list of unexpected <code>Page</code> classes that will be checked for when “at” checks are performed.
Given that <code>PageNotFoundPage</code> and <code>InternalServerErrorPage</code> have been defined you can use the following to configure them as unexpected pages:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">unexpectedPages = [PageNotFoundPage, InternalServerErrorPage]</code></pre>
</div>
</div>
<div class="paragraph">
<p>See <a href="#unexpected-pages">this section</a> for more information on unexpected pages.</p>
</div>
</div>
<div class="sect3">
<h4 id="reporter-configuration"><a class="link" href="#reporter-configuration">7.2.9. Reporter</a></h4>
<div class="paragraph">
<p>The <em>reporter</em> is the object responsible for snapshotting the state of the browser (see the <a href="#reporting">Reporting</a> chapter for details).
All reporters are implementations of the <a href="api/geb/report/Reporter.html"><code>Reporter</code></a> interface.
If no reporter is explicitly defined, a <a href="api/geb/report/CompositeReporter.html">composite reporter</a> will be created from a <code>ScreenshotReporter</code> (takes a PNG screenshot) and <code>PageSourceReporter</code>
(dumps the current DOM state as HTML).
This is a sensible default, but should you wish to use a custom reporter you can assign it to the <code>reporter</code> config key.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">reporter = new CustomReporter()</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="reports-directory-configuration"><a class="link" href="#reports-directory-configuration">7.2.10. Reports directory</a></h4>
<div class="paragraph">
<p>The reports dir configuration is used by to control where the browser should write reports (see the <a href="#reporting">Reporting</a> chapter for details).</p>
</div>
<div class="paragraph">
<p>In the config script, you can set the path to the directory to use for reports via the <code>reportsDir</code> key…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">reportsDir = "target/geb-reports"</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The value is interpreted as a path, and if not absolute will be relative to the JVM&#8217;s working directory.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The reports dir can also be specified by the build adapter (the default implementation of which looks at the <code>geb.build.reportsDir</code> system property).
Any value set in the config script will take precedence over the value provided by the build adapter.</p>
</div>
<div class="paragraph">
<p>It is also possible to set the <code>reportsDir</code> config item to a file.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">reportsDir = new File("target/geb-reports")</code></pre>
</div>
</div>
<div class="paragraph">
<p>By default this value is <strong>not set</strong>. The browser&#8217;s <a href="api/geb/Browser.html#report(java.lang.String)"><code>report()</code></a> method requires a value for this config item so if you are using the reporting features
you <strong>must</strong> set a reports dir.</p>
</div>
</div>
<div class="sect3">
<h4 id="report-test-failures-configuration"><a class="link" href="#report-test-failures-configuration">7.2.11. Report test failures only</a></h4>
<div class="paragraph">
<p>By default Geb will take a report at the end of each test method, regardless of whether it ended successfully or not.
The <code>reportOnTestFailureOnly</code> setting allows you to specify that a report should be taken only if a failure occurs.
This might be useful as a way to speed up large test suites.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">reportOnTestFailureOnly = true</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="reporting-listener"><a class="link" href="#reporting-listener">7.2.12. Reporting listener</a></h4>
<div class="paragraph">
<p>It is possible to specify a listener that will be notified when reports are taken. See the section on <a href="#listening-to-reporting">listening to reporting</a> for details.</p>
</div>
</div>
<div class="sect3">
<h4 id="auto-clearing-cookies-configuration"><a class="link" href="#auto-clearing-cookies-configuration">7.2.13. Auto clearing cookies</a></h4>
<div class="paragraph">
<p>Certain integrations will automatically clear the driver&#8217;s cookies for the current domain, which is usually necessary when using an <a href="#implicit-driver-lifecycle">implicit driver</a>.
This configuration flag, which is <code>true</code> by default, can be disabled by setting the <code>autoClearCookies</code> value in the config to <code>false</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">autoClearCookies = false</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="runtime-overrides"><a class="link" href="#runtime-overrides">7.3. Runtime overrides</a></h3>
<div class="paragraph">
<p>The <a href="api/geb/Configuration.html">Configuration</a> object also has setters for all of the config properties it exposes, allowing you to override config properties at runtime in particular circumstances if you need to.</p>
</div>
<div class="paragraph">
<p>For example, you may have one Spock spec that requires the <code>autoClearCookies</code> property to be disabled. You could disable it for just this spec by doing something like…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.spock.GebReportingSpec
class FunctionalSpec extends GebReportingSpec {
def setup() {
browser.config.autoClearCookies = false
}
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="implicit-assertions"><a class="link" href="#implicit-assertions">8. Implicit assertions</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>As of Geb 0.7.0, certain parts of Geb utilise “<strong>implicit assertions</strong>”. This sole goal of this feature is to provide more informative error messages. Put simply, it means that for a given block of code, all <em>expressions</em> are automatically turned into assertions. So the following code:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">1 == 1</code></pre>
</div>
</div>
<div class="paragraph">
<p>Becomes…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert 1 == 1</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If you&#8217;ve used the <a href="http://spockframework.org/">Spock Framework</a> you will be well familiar with the concept of implicit assertions from Spock&#8217;s <code>then:</code> blocks.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>In Geb, waiting expressions and at expressions automatically use implicit assertions. Take the following page object…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class ImplicitAssertionsExamplePage extends Page {
static at = { title == "Implicit Assertions!" }
def waitForHeading() {
waitFor { $("h1") }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This automatically becomes…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class ImplicitAssertionsExamplePage extends Page {
static at = { assert title == "Implicit Assertions!" }
def waitForHeading() {
waitFor { assert $("h1") }
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Because of this, Geb is able to provide much better error messages when the expression fails due to Groovy&#8217;s
<a href="http://docs.groovy-lang.org/latest/html/documentation/core-testing-guide.html#_power_assertions">power assertions</a>.</p>
</div>
<div class="paragraph">
<p>A special form of <code>assert</code> is used by Geb that returns the value of the expression, whereas a regular <code>assert</code> returns <code>null</code>.</p>
</div>
<div class="paragraph">
<p>This means that given…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">static content = {
headingText(wait: true) { $("h1").text() }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Accessing <code>headingText</code> here will <em>wait</em> for there to be a <code>h1</code> and for it to have some text (because an <a href="http://mrhaki.blogspot.com/2009/08/groovy-goodness-tell-groovy-truth.html">empty string is <code>false</code> in Groovy</a>), which will then be returned.
This means that even when implicit assertions are used, the value is still returned and it is usable.</p>
</div>
<div class="sect2">
<h3 id="at-verification"><a class="link" href="#at-verification">8.1. At verification</a></h3>
<div class="paragraph">
<p>Let&#8217;s take the “at checker” case.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If you&#8217;re unfamiliar with Geb&#8217;s “at checking”, please read <a href="#at-checker">this section</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Consider the following small Geb script…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to ImplicitAssertionsExamplePage</code></pre>
</div>
</div>
<div class="paragraph">
<p>At checking works by verifying that the page&#8217;s “at check” returns a <em>trueish</em> value.
If it does, the <code>at()</code> method returns <code>true</code>. If not, the <code>at()</code> method will return <code>false</code>.
However, due to implicit assertions, the “at check” will never return <code>false</code>.
Instead, the at checker will throw an <code>AssertionError</code>.
Because the page&#8217;s “at check” is turned into an assertion, you&#8217;ll see the following in the stacktrace:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Assertion failed:
title == "Implicit Assertions!"
| |
| false
Something else</pre>
</div>
</div>
<div class="paragraph">
<p>As you can see, this is much more informative than the <code>at()</code> method simply returning <code>false</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="implicit-assertions-waiting"><a class="link" href="#implicit-assertions-waiting">8.2. Waiting</a></h3>
<div class="paragraph">
<p>Another place where implicit assertions are utilised is for <em>waiting</em>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If you&#8217;re unfamiliar with Geb&#8217;s “waiting” support, please read <a href="#waiting">this section</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Consider the following Geb script:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">waitFor { title == "Page Title" }</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>waitFor</code> method verifies that the given clause returns a <em>trueish</em> value within a certain timeframe.
Because of implicit assertions, when this fails you&#8217;ll see the following in the stacktrace:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Assertion failed:
title == "Page Title"
| |
| false
Something else</pre>
</div>
</div>
<div class="paragraph">
<p>The failed assertion is carried as the cause of the <code>geb.waiting.WaitTimeoutException</code> and gives you an informative message as to why the waiting failed.</p>
</div>
<div class="sect3">
<h4 id="waiting-content"><a class="link" href="#waiting-content">Waiting content</a></h4>
<div class="paragraph">
<p>The same implicit assertion semantics apply to content definitions that are waiting.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If you&#8217;re unfamiliar with Geb&#8217;s “waiting content” support, please read <a href="#content-dsl-wait">this section</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Any content definitions that declare a <code>wait</code> parameter have implicit assertions added to each expression just like <code>waitFor()</code> method calls.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="implicit-assertions-mechanics"><a class="link" href="#implicit-assertions-mechanics">8.3. How it works</a></h3>
<div class="paragraph">
<p>The “implicit assertions” feature is implemented as a <a href="http://www.groovy-lang.org/metaprogramming.html#_compile_time_metaprogramming">Groovy compile time transformation</a>, which literally
turns all expressions in a candidate block of code into assertions.</p>
</div>
<div class="paragraph">
<p>This transform is packaged as a separate JAR named <code>geb-implicit-assertions</code>.
This JAR needs to be on the compilation classpath of your Geb test/pages/modules (and any other code that you want to use implicit assertions) in order for this feature to work.</p>
</div>
<div class="paragraph">
<p>If you are obtaining Geb via a dependency management system, this is typically not something you need to be concerned about as it will happen automatically.
Geb is distributed via the Maven Central repository in Apache Maven format (i.e. via POM files).
The main Geb module, <code>geb-core</code> depends on the <code>geb-implicit-assertions</code> module as a <code>compile</code> dependency.</p>
</div>
<div class="paragraph">
<p>If your dependency management system <em>inherits</em> transitive compile dependencies (i.e. also makes compile dependencies of first class compile dependencies first class compile dependencies) then you
will automatically have the <code>geb-implicit-assertions</code> module as a compile dependency and everything will work fine (Maven, Gradle and most configurations of Ivy do this).
If your dependency management system does not do this, or if you are manually managing the <code>geb-core</code> dependency, be sure to include the <code>geb-implicit-assertions</code> dependency as a compile dependency.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="javascript-ajax-and-dynamic-pages"><a class="link" href="#javascript-ajax-and-dynamic-pages">9. Javascript, AJAX and dynamic pages</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>This section discusses how to deal with some of the challenges in testing and/or automating modern web applications.</p>
</div>
<div class="sect2">
<h3 id="js-object"><a class="link" href="#js-object">9.1. The "js" object</a></h3>
<div class="paragraph">
<p>The browser instance exposes a “<a href="api/geb/Browser.html#getJs()"><code>js</code></a>” object that provides support for working with JavaScript over and above what WebDriver provides.
It&#8217;s important to understand how WebDriver does handle JavaScript, which is through a driver&#8217;s implementation of
<code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/JavascriptExecutor.html#executeScript(java.lang.String,%20java.lang.Object&#8230;&#8203;)">JavascriptExecutor.executeScript()</a></code>
method.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Before reading further, it&#8217;s <strong>strongly</strong> recommended to read the description of <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/JavascriptExecutor.html#executeScript(java.lang.String,%20java.lang.Object&#8230;&#8203;)">JavascriptExecutor.executeScript()</a></code> in order to understand how type conversion works between the two worlds.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>You can execute JavaScript like you would with straight WebDriver using the driver instance via the browser…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert browser.driver.executeScript("return arguments[0];", 1) == 1</code></pre>
</div>
</div>
<div class="paragraph">
<p>This is a bit long winded, and as you would expect Geb uses the dynamism of Groovy to make life easier.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/JavascriptExecutor.html">JavascriptExecutor</a></code> interface does not define any contract in regards to the driver&#8217;s responsibility when there is some issue executing JavaScript.
All drivers however throw <em>some kind</em> of exception when this happens.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="accessing-variables"><a class="link" href="#accessing-variables">9.1.1. Accessing variables</a></h4>
<div class="paragraph">
<p>Any <em>global</em> JavaScript variables inside the browser can be read as <em>properties</em> of the <code>js</code> object.</p>
</div>
<div class="paragraph">
<p>Given the following page…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;head&gt;
&lt;script type="text/javascript"&gt;
var aVariable = 1;
&lt;/script&gt;
&lt;/head&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We could access the JavaScript variable “<code>aVariable</code>” with…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go "/"
assert js.aVariable == 1
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or if we wanted to map it to page content…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to JsVariablePage
assert aVar == 1
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can even access <em>nested</em> variables…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert js."document.title" == "Book of Geb"</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="calling-methods"><a class="link" href="#calling-methods">9.1.2. Calling methods</a></h4>
<div class="paragraph">
<p>Any <em>global</em> JavaScript functions can be called as methods on the <code>js</code> object.</p>
</div>
<div class="paragraph">
<p>Given the following page…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;head&gt;
&lt;script type="text/javascript"&gt;
function addThem(a,b) {
return a + b;
}
&lt;/script&gt;
&lt;/head&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can call the <code>addThem()</code> function with…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go "/"
assert js.addThem(1, 2) == 3
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This also works from pages and modules.</p>
</div>
<div class="paragraph">
<p>To call <em>nested</em> methods, given the following page…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;head&gt;
&lt;script type="text/javascript"&gt;
functionContainer = {
addThem: function(a,b) {
return a + b;
}
}
&lt;/script&gt;
&lt;/head&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We use the same syntax as for properties…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
Browser.drive {
go "/"
assert js."functionContainer.addThem"(1, 2) == 3
}
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="executing-arbitrary-code"><a class="link" href="#executing-arbitrary-code">9.1.3. Executing arbitrary code</a></h4>
<div class="paragraph">
<p>The <code>js</code> object also has an <code>exec()</code> method that can be used to run snippets of JavaScript.
It is identical to the <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/JavascriptExecutor.html#executeScript(java.lang.String,%20java.lang.Object&#8230;&#8203;)">JavascriptExecutor.executeScript()</a></code> method, except that it takes its arguments in the other order…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert js.exec(1, 2, "return arguments[0] + arguments[1];") == 3</code></pre>
</div>
</div>
<div class="paragraph">
<p>You might be wondering why the order has been changed (i.e. the arguments go <em>before</em> the script). It makes writing multiline JavaScript more convenient…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">js.exec 1, 2, """
someJsMethod(1, 2);
// lots of javascript
return true;
"""</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="waiting"><a class="link" href="#waiting">9.2. Waiting</a></h3>
<div class="paragraph">
<p>Geb provides some convenient methods for <em>waiting</em> for a certain condition to be true. This is useful for testing pages using AJAX, timers or effects.</p>
</div>
<div class="paragraph">
<p>The <code>waitFor</code> methods are provided by the <code><a href="api/geb/waiting/WaitingSupport.html">WaitingSupport</a></code> mixin which delegates to the <code><a href="api/geb/waiting/Wait.html">Wait</a></code> class (see the documentation of the <code><a href="api/geb/waiting/Wait.html#waitFor(Closure%3CT%3E)">waitFor()</a></code> method of this class for the precise
semantics of <em>waiting</em>).
These methods take various parameters that determine how long to wait for the given closure to return a true object according to the <a href="http://mrhaki.blogspot.com/2009/08/groovy-goodness-tell-groovy-truth.html">Groovy Truth</a>, and how long to wait in between invoking the
closure again.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">waitFor {} <i class="conum" data-value="1"></i><b>(1)</b>
waitFor(10) {} <i class="conum" data-value="2"></i><b>(2)</b>
waitFor(10, 0.5) {} <i class="conum" data-value="3"></i><b>(3)</b>
waitFor("quick") {} <i class="conum" data-value="4"></i><b>(4)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Use default configuration.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Wait for up to 10 seconds, using the default retry interval.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Wait for up to 10 seconds, waiting half a second in between retries. See the section on <a href="#waiting-configuration">wait configuration</a> for how to change the default values and define presets.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Use the preset “quick” as the wait settings</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>It is also possible to declare that content should be implicitly waited on, see <a href="#content-dsl-wait">the <code>wait</code> option for content definition</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="examples"><a class="link" href="#examples">9.2.1. Examples</a></h4>
<div class="paragraph">
<p>Here is an example showing one way of using <code>waitFor()</code> to deal with the situation where clicking a button invokes an AJAX request that creates a new <code>div</code> on its completion.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class DynamicPage extends Page {
static content = {
theButton { $("input", value: "Make Request") }
theResultDiv { $("div#result") }
}
def makeRequest() {
theButton.click()
waitFor { theResultDiv.present }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to DynamicPage
makeRequest()
assert theResultDiv.text() == "The Result"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Recall that the <code>return</code> keyword is optional in Groovy, so in the example above the <code>$(&quot;div#result&quot;).present</code> statement acts as the return value for the closure and is used as the basis on
whether the closure <em>passed</em> or not.
This means that you must ensure that the last statement inside the closure returns a value that is <code>true</code> according to the <a href="http://mrhaki.blogspot.com/2009/08/groovy-goodness-tell-groovy-truth.html">Groovy Truth</a> (if you&#8217;re unfamiliar with the Groovy Truth <strong>do</strong> read that
page).</p>
</div>
<div class="paragraph">
<p>Because the browser delegates method calls to the page object, the above could have been written as…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go "/"
$("input", value: "Make Request").click()
waitFor { $("div#result") }
assert $("div#result").text() == "The Result"
}</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Not using explicit <code>return</code> statements in closure expressions passed to <code>waitFor()</code> is actually preferred.
See the section on <a href="#implicit-assertions">implicit assertions</a> for more information.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The closures given to the <code>waitFor()</code> method(s) do not need to be single statement.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">waitFor {
def result = $("div#result")
result.text() == "The Result"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>That will work fine.</p>
</div>
<div class="paragraph">
<p>If you wish to <em>test</em> multiple conditions as separate statement inside a <code>waitFor</code> closure, you can just put them in separate lines.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">waitFor {
def result = $("div")
result.@id == "result"
result.text() == "The Result"
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="custom-message"><a class="link" href="#custom-message">9.2.2. Custom message</a></h4>
<div class="paragraph">
<p>If you wish to add a custom message to <code>WaitTimeoutException</code> that is being thrown when <code>waitFor</code> call times out you can do so by providing a message parameter to the <code>waitFor</code> call:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">waitFor(message: "My custom message") { $("div#result") }</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="alert-and-confirm-dialogs"><a class="link" href="#alert-and-confirm-dialogs">9.3. Alert and confirm dialogs</a></h3>
<div class="paragraph">
<p>WebDriver currently does not handle the alert() and confirm() dialog windows.
However, we can fake it through some JavaScript magic as <a href="http://code.google.com/p/selenium/issues/detail?id=27#c17">discussed on the WebDriver issue for this</a>.
Geb implements a workaround based on this solution for you.
Note that this feature relies on making changes to the browser&#8217;s <code>window</code> DOM object so may not work on all browsers on all platforms.
At the time when WebDriver adds support for this functionality the underlying implementation of the following methods will change to use that which will presumably be more robust.
Geb adds this functionality through the <code><a href="api/geb/js/AlertAndConfirmSupport.html">AlertAndConfirmSupport</a></code> class that is mixed into <code><a href="api/geb/Page.html">Page</a></code> and <code><a href="api/geb/Module.html">Module</a></code>.</p>
</div>
<div class="paragraph">
<p>The Geb methods <strong>prevent</strong> the browser from actually displaying the dialog, which is a good thing.
This prevents the browser blocking while the dialog is displayed and causing your test to hang indefinitely.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Unexpected <code>alert()</code> and <code>confirm()</code> calls can have strange results.
This is due to the nature of how Geb handles this internally.
If you are seeing strange results, you may want to run your tests/scripts against a real browser and watch what happens to make sure there aren&#8217;t any alert or confirm windows being opened that you
aren&#8217;t expecting.
To do this, you need to disable Geb&#8217;s handling by changing your code to not use the methods below.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="alert"><a class="link" href="#alert">9.3.1. <code>alert()</code></a></h4>
<div class="paragraph">
<p>There are three methods that deal with <code>alert()</code> dialogs:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def withAlert(Closure actions)
def withAlert(Map params, Closure actions)
void withNoAlert(Closure actions)</code></pre>
</div>
</div>
<div class="paragraph">
<p>The first method, <code>withAlert()</code>, is used to verify actions that will produce an alert dialog.
This method returns the alert message.</p>
</div>
<div class="paragraph">
<p>Given the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;input type="button" name="showAlert" onclick="alert('Bang!');" /&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>withAlert()</code> method is used like so…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert withAlert { $("input", name: "showAlert").click() } == "Bang!"</code></pre>
</div>
</div>
<div class="paragraph">
<p>If an alert dialog is not raised by the given “actions” closure, an <code>AssertionError</code> will be thrown.</p>
</div>
<div class="paragraph">
<p>The <code>withAlert()</code> method also accepts a wait option.
It is useful if the code in your “actions” closure is raising a dialog in an asynchronous manner and can be used like that:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert withAlert(wait: true) { $("input", name: "showAlert").click() } == "Bang!"</code></pre>
</div>
</div>
<div class="paragraph">
<p>The possible values for the <code>wait</code> option are consistent with the <a href="#content-dsl-wait">ones for <code>wait</code> option of content definitions</a>.</p>
</div>
<div class="paragraph">
<p>The second method, <code>withNoAlert()</code>, is used to verify actions that will not produce an <code>alert()</code> dialog.
If an alert dialog is raised by the given “actions” closure, an <code>AssertionError</code> will be thrown.</p>
</div>
<div class="paragraph">
<p>Given the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;input type="button" name="dontShowAlert" /&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>withNoAlert()</code> method is used like so…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">withNoAlert { $("input", name: "dontShowAlert").click() }</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>It&#8217;s a good idea to use <code>withNoAlert()</code> when doing something that <em>might</em> raise an alert.
If you don&#8217;t, the browser is going to raise a real alert dialog and sit there waiting for someone to click it which means your test is going to hang.
Using <code>withNoAlert()</code> prevents this.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>A side effect of the way that this is implemented is that we aren&#8217;t able to definitively handle actions that cause the browser&#8217;s actual page to change (e.g. clicking a link in the closure given to
<code>withAlert()</code>/<code>withNoAlert()</code>).
We can detect that the browser page did change, but we can&#8217;t know if <code>alert()</code> did or did not get called before the page change.
If a page change was detected the <code>withAlert()</code> method will return a literal <code>true</code> (whereas it would normally return the alert message), while the <code>withNoAlert()</code> will succeed.</p>
</div>
</div>
<div class="sect3">
<h4 id="code-confirm-code"><a class="link" href="#code-confirm-code">9.3.2. <code>confirm()</code></a></h4>
<div class="paragraph">
<p>There are five methods that deal with <code>confirm()</code> dialogs:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def withConfirm(boolean ok, Closure actions)
def withConfirm(Closure actions)
def withConfirm(Map params, Closure actions)
def withConfirm(Map params, boolean ok, Closure actions)
void withNoConfirm(Closure actions)</code></pre>
</div>
</div>
<div class="paragraph">
<p>The first method, <code>withConfirm()</code> (and its ‘<code>ok</code>’ defaulted relative), is used to verify actions that will produce a confirm dialog.
This method returns the confirmation message.
The <code>ok</code> parameter controls whether the the “OK” or “Cancel” button should be clicked.</p>
</div>
<div class="paragraph">
<p>Given the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;input type="button" name="showConfirm" onclick="confirm('Do you like Geb?');" /&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>withConfirm()</code> method is used like so…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">assert withConfirm(true) { $("input", name: "showConfirm").click() } == "Do you like Geb?"</code></pre>
</div>
</div>
<div class="paragraph">
<p>If a confirmation dialog is not raised by the given “actions” closure, an <code>AssertionError</code> will be thrown.</p>
</div>
<div class="paragraph">
<p>The <code>withConfirm()</code> method also accepts a wait option just like the <code>withAlert()</code> method. See the <a href="#alert">description of <code>withAlert()</code></a> to learn about the possible values and usage.</p>
</div>
<div class="paragraph">
<p>The other method, <code>withNoConfirm()</code>, is used to verify actions that will not produce a confirm dialog.
If a confirmation dialog is raised by the given “actions” closure, an <code>AssertionError</code> will be thrown.</p>
</div>
<div class="paragraph">
<p>Given the following HTML…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;input type="button" name="dontShowConfirm" /&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>withNoConfirm()</code> method is used like so…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">withNoConfirm { $("input", name: "dontShowConfirm").click() }</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>It&#8217;s a good idea to use <code>withNoConfirm()</code> when doing something that <em>might</em> raise a a confirmation.
If you don&#8217;t, the browser is going to raise a real confirmation dialog and sit there waiting for someone to click it, which means your test is going to hang.
Using <code>withNoConfirm()</code> prevents this.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>A side effect of the way that this is implemented is that we aren&#8217;t able to definitively handle actions that cause the browser&#8217;s actual page to change (e.g. clicking a link in the closure given to
<code>withConfirm()</code>/<code>withNoConfirm()</code>).
We can detect that the browser page did change, but we can&#8217;t know if <code>confirm()</code> did or did not get called before the page change.
If a page change was detected, the <code>withConfirm()</code> method will return a literal <code>true</code> (whereas it would normally return the alert message), while the <code>withNoConfirm()</code> will succeed.</p>
</div>
</div>
<div class="sect3">
<h4 id="code-prompt-code"><a class="link" href="#code-prompt-code">9.3.3. <code>prompt()</code></a></h4>
<div class="paragraph">
<p>Geb does not provide any support for <code>prompt()</code> due to its infrequent and generally discouraged use.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="jquery-integration"><a class="link" href="#jquery-integration">9.4. jQuery integration</a></h3>
<div class="paragraph">
<p>Geb has special support for <a href="http://jquery.com/">jQuery</a>.
Navigator objects have a special adapter that makes calling jQuery methods against the underlying DOM elements simple.
This is best explained by example.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The jQuery integration only works when the pages you are working with include jQuery, Geb does not install it in the page for you.
The minimum supported version of jQuery is 1.4.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Consider the following page:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-html" data-lang="html">&lt;html&gt;
&lt;head&gt;
&lt;script type="text/javascript" src="/js/jquery-2.1.4.min.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
$(function() {
$("#a").mouseover(function() {
$("#b").show();
});
});
&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div id="a"&gt;&lt;/div&gt;
&lt;div id="b" style="display:none;"&gt;&lt;a href="http://www.gebish.org"&gt;Geb!&lt;/a&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We want to click the Geb link, but can&#8217;t because it&#8217;s hidden (WebDriver does not let you interact with hidden elements).
The div containing the link (div “a”) is only displayed when the mouse moves over div “a”.</p>
</div>
<div class="paragraph">
<p>The jQuery library provides convenient methods for triggering browser events.
We can use this to simulate the mouse being moved over the div “a”.</p>
</div>
<div class="paragraph">
<p>In straight jQuery JavaScript we would do…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-javascript" data-lang="javascript">jQuery("div#a").mouseover();</code></pre>
</div>
</div>
<div class="paragraph">
<p>Which we could invoke via Geb easy enough…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">js.exec 'jQuery("div#a").mouseover()'</code></pre>
</div>
</div>
<div class="paragraph">
<p>That will work, but can be inconvenient as it duplicates content definitions in our Geb pages.
Geb&#8217;s jQuery integration allows you to use your defined content in Geb with jQuery.
Here is how we could call the <code>mouseover</code> jQuery function on an element from Geb…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("div#a").jquery.mouseover()</code></pre>
</div>
</div>
<div class="paragraph">
<p>To be clear, that is Groovy (not JavaScript code). It can be used with pages…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class JQueryPage extends Page {
static content = {
divA { $("#a") }
divB { $("#b") }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to JQueryPage
divA.jquery.mouseover()
assert divB.displayed</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>jquery</code> property of a navigator is conceptually equivalent to a jQuery object for <em>all</em> of the navigator&#8217;s matched page elements.</p>
</div>
<div class="paragraph">
<p>The methods can also take arguments…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">$("#a").jquery.trigger('mouseover')</code></pre>
</div>
</div>
<div class="paragraph">
<p>The same set of restricted types as allowed by WebDriver&#8217;s <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/JavascriptExecutor.html#executeScript(java.lang.String,%20java.lang.Object&#8230;&#8203;)">JavascriptExecutor.executeScript()</a></code> method are permitted here.</p>
</div>
<div class="paragraph">
<p>The return value of methods called on the <code>jquery</code> property depends on what the corresponding jQuery method returns.
A jQuery object will be converted to a Navigator representing the same set of elements, other values such as objects, strings and numbers are returned as per WebDriver&#8217;s
<code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/JavascriptExecutor.html#executeScript(java.lang.String,%20java.lang.Object&#8230;&#8203;)">JavascriptExecutor.executeScript()</a></code> method.</p>
</div>
<div class="sect3">
<h4 id="why"><a class="link" href="#why">Why?</a></h4>
<div class="paragraph">
<p>This functionality was developed to make triggering mouse related events easier.
Some applications are very sensitive to mouse events, and triggering these events in an automated environment is a challenge.
jQuery provides a good API for faking these events, which makes for a good solution.
An alternative is using <a href="#interact-closures"><code>the interact() method</code></a>.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="direct-downloading"><a class="link" href="#direct-downloading">10. Direct downloading</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Geb features an API that can be used to make direct HTTP requests from the application that is executing the Geb scripts or tests.
This facilitates fine grained requests and downloading content such as PDF, CSVs, images etc. into your scripts or tests to then do something with.</p>
</div>
<div class="paragraph">
<p>The direct download API works by using <a href="https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html"><code>java.net.HttpURLConnection</code></a> to directly connect to a URL from the application executing Geb, bypassing WebDriver.</p>
</div>
<div class="paragraph">
<p>The Direct Download API is provided by the <code><a href="api/geb/download/DownloadSupport.html">DownloadSupport</a></code> class, which is mixed in to pages and modules (which means you can just call these instance methods directly from anywhere where you
would want to, e.g. drive blocks, in tests/specs, methods on page objects, methods on modules).
Consult the <code><a href="api/geb/download/DownloadSupport.html">DownloadSupport</a></code> API reference for the various <code>download*()</code> methods that are available.</p>
</div>
<div class="sect2">
<h3 id="downloading-example"><a class="link" href="#downloading-example">10.1. Downloading example</a></h3>
<div class="paragraph">
<p>For example, let&#8217;s say you are using Geb to exercise a web application that generates PDF documents.
The WebDriver API can only deal with HTML documents.
You want to hit the PDF download link and also do some tests on the downloaded PDF.
The direct download API is there to fill this need.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class LoginPage extends Page {
static content = {
loginButton(to: PageWithPdfLink) { $("input", name: "login") }
}
void login(String user, String pass) {
username = user
password = pass
loginButton.click()
}
}
class PageWithPdfLink extends Page {
static content = {
pdfLink { $("a#pdf-download-link") }
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
to LoginPage
login("me", "secret")
def pdfBytes = downloadBytes(pdfLink.@href)
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Simple enough, but consider what is happening behind the scenes.
Our application required us to log in, which implies some kind of session state.
Geb is using <a href="https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html"><code>HttpURLConnection</code></a> to get the content and before doing so the cookies from the real browser are being transferred onto the connection allowing it to reuse
the same session.
The PDF download link href may also be relative, and Geb handles this by resolving the link passed to the download function against the browser&#8217;s current page URL.</p>
</div>
</div>
<div class="sect2">
<h3 id="fine-grained-request"><a class="link" href="#fine-grained-request">10.2. Fine grained request</a></h3>
<div class="paragraph">
<p>The Direct Download API can also be used for making fine grained requests which can be useful for testing edge cases or abnormal behavior.</p>
</div>
<div class="paragraph">
<p>All of the <code>download*()</code> methods take an optional closure that can configure the <a href="https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html"><code>HttpURLConnection</code></a> that will be used to make the request (after the <code>Cookie</code> header has
been set).</p>
</div>
<div class="paragraph">
<p>For example, we could test what happens when we send <code>application/json</code> in the <code>Accept</code> header.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go "/"
def jsonBytes = downloadBytes { HttpURLConnection connection -&gt;
connection.setRequestProperty("Accept", "application/json")
}
}</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Before doing something like the above, it&#8217;s worth considering whether doing such testing via Geb (a browser automation tool) is the right thing to do.
You may find that it&#8217;s more appropriate to directly use <code>HttpURLConnection</code> or a http client library without Geb.
That said, there are scenarios where such fine grained request control can be useful.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="dealing-with-untrusted-certificates"><a class="link" href="#dealing-with-untrusted-certificates">10.3. Dealing with untrusted certificates</a></h3>
<div class="paragraph">
<p>When facing web applications using untrusted (e.g. self-signed) SSL certificates, you will likely get exceptions when trying to use Geb&#8217;s download API.
By overriding the behavior of the request you can get around this kind of problem.
Using the following code will allow running requests against a server which uses a certificate from the given keystore:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.download.helper.SelfSignedCertificateHelper
def text = downloadText { HttpURLConnection connection -&gt;
if (connection instanceof HttpsURLConnection) {
def keystore = getClass().getResource('/keystore.jks')
def helper = new SelfSignedCertificateHelper(keystore, 'password')
helper.acceptCertificatesFor(connection as HttpsURLConnection)
}
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="default-configuration"><a class="link" href="#default-configuration">10.4. Default configuration</a></h3>
<div class="paragraph">
<p>In the <a href="#configuration">configuration</a>, the default behaviour of the <code>HttpURLConnection</code> object can be specified by providing a closure as the <code>defaultDownloadConfig</code> property.</p>
</div>
<div class="paragraph">
<p>The below example configures all requests executed using direct downloading support to carry a <code>User-Agent</code> header.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">defaultDownloadConfig = { HttpURLConnection connection -&gt;
connection.setRequestProperty("User-Agent", "Geb")
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This config closure will be run first, so anything set here can be overridden using the fine grained request configuration shown above.</p>
</div>
</div>
<div class="sect2">
<h3 id="errors"><a class="link" href="#errors">10.5. Errors</a></h3>
<div class="paragraph">
<p>Any I/O type errors that occur during a download operation (e.g. HTTP 500 responses) will result in a <a href="api/geb/download/DownloadException.html"><code>DownloadException</code></a> being thrown that wraps the
original exception and provides access to the <code>HttpURLConnection</code> used to make the request.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="binding"><a class="link" href="#binding">11. Scripts and binding</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Geb supports being used in scripting environments via both the <code>Browser.drive()</code> method, and by using the <a href="api/geb/binding/BindingUpdater.html"><code>geb.binding.BindingUpdater</code></a> class that populates and updates a
<a href="http://docs.groovy-lang.org/docs/groovy-2.4.7/html/api/groovy/lang/Binding.html"><code>groovy.lang.Binding</code></a> that can be used with scripts.
This is also the same mechanism that can be used with <a href="#cucumber-jvm">Cucumber-JVM</a>.</p>
</div>
<div class="sect2">
<h3 id="setup"><a class="link" href="#setup">11.1. Setup</a></h3>
<div class="paragraph">
<p>To use the binding support, you simply create a <a href="api/geb/binding/BindingUpdater.html"><code>BindingUpdater</code></a> object with a <a href="http://docs.groovy-lang.org/docs/groovy-2.4.7/html/api/groovy/lang/Binding.html"><code>Binding</code></a> and <code><a href="api/geb/Browser.html">Browser</a></code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.Browser
import geb.binding.BindingUpdater
def binding = new Binding()
def browser = new Browser()
def updater = new BindingUpdater(binding, browser)
updater.initialize() <i class="conum" data-value="1"></i><b>(1)</b>
def script = getClass().getResource(resourcePath).text
def result = new GroovyShell(binding).evaluate(script) <i class="conum" data-value="2"></i><b>(2)</b>
updater.remove() <i class="conum" data-value="3"></i><b>(3)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Populate and start updating the browser.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Run a script from a resource loaded from the classpath.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Remove Geb bits from the binding and stop updating it.</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="the-binding-environment"><a class="link" href="#the-binding-environment">11.2. The binding environment</a></h3>
<div class="sect3">
<h4 id="browser-methods-and-properties"><a class="link" href="#browser-methods-and-properties">11.2.1. Browser methods and properties</a></h4>
<div class="paragraph">
<p>The <a href="api/geb/binding/BindingUpdater.html"><code>BindingUpdater</code></a> installs shortcuts into the binding for most of the <code><a href="api/geb/Browser.html">Browser</a></code> public methods.</p>
</div>
<div class="paragraph">
<p>The following is an example script that will work if <code>BindingUpdater</code> is initialized on its binding…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">go "some/page"
at(SomePage)
waitFor { $("p#status").text() == "ready" }
js.someJavaScriptFunction()
downloadText($("a.textFile").@href)</code></pre>
</div>
</div>
<div class="paragraph">
<p>In a managed binding, all of the methods/properties that you can usually call in the <a href="api/geb/Browser.html#drive(groovy.lang.Closure)"><code>Browser.drive()</code></a> method are available.
This includes the <code>$()</code> method.</p>
</div>
<div class="paragraph">
<p>The following methods are available:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>$()</code></p>
</li>
<li>
<p><code>go()</code></p>
</li>
<li>
<p><code>to()</code></p>
</li>
<li>
<p><code>via()</code></p>
</li>
<li>
<p><code>at()</code></p>
</li>
<li>
<p><code>waitFor()</code></p>
</li>
<li>
<p><code>withAlert()</code></p>
</li>
<li>
<p><code>withNoAlert()</code></p>
</li>
<li>
<p><code>withConfirm()</code></p>
</li>
<li>
<p><code>withNoConfirm()</code></p>
</li>
<li>
<p><code>download()</code></p>
</li>
<li>
<p><code>downloadStream()</code></p>
</li>
<li>
<p><code>downloadText()</code></p>
</li>
<li>
<p><code>downloadBytes()</code></p>
</li>
<li>
<p><code>downloadContent()</code></p>
</li>
<li>
<p><code>report()</code></p>
</li>
<li>
<p><code>reportGroup()</code></p>
</li>
<li>
<p><code>cleanReportGroupDir()</code></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The JavaScript interface property <a href="#js-object"><code>js</code></a> is also available. The <code>Browser</code> object itself is available as the <code>browser</code> property.</p>
</div>
</div>
<div class="sect3">
<h4 id="the-current-page"><a class="link" href="#the-current-page">11.2.2. The current page</a></h4>
<div class="paragraph">
<p>The binding updater also updates the <code>page</code> property of the binding to be the browser&#8217;s current page…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.Page
class InitialPage extends Page {
static content = {
button(to: NextPage) { $("input.do-stuff") }
}
}
class NextPage extends Page {
}
to InitialPage
assert page instanceof InitialPage
page.button.click()
assert page instanceof NextPage</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="reporting"><a class="link" href="#reporting">12. Reporting</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Geb includes a simple reporting mechanism which can be used to snapshot the state of the browser at any point in time.
Reporters are implementations of the <code><a href="api/geb/report/Reporter.html">Reporter</a></code> interface.
Geb ships with two implementations: <code><a href="api/geb/report/PageSourceReporter.html">PageSourceReporter</a></code> and <code><a href="api/geb/report/ScreenshotReporter.html">ScreenshotReporter</a></code>.
There are three bits of configuration that pertain to reporting: the <a href="#reporter-configuration">reporter implementation</a>, the <a href="#reports-directory-configuration">reports directory</a> and whether to
<a href="#report-test-failures-configuration">only report test failures</a> or not.</p>
</div>
<div class="paragraph">
<p>If no reporter is explicitly defined, a <a href="api/geb/report/CompositeReporter.html">composite reporter</a> will be created from a <code>ScreenshotReporter</code> (takes a PNG screenshot) and <code>PageSourceReporter</code> (dumps the current
DOM state as HTML).</p>
</div>
<div class="paragraph">
<p>You take a report by calling the <code><a href="api/geb/Browser.html#report(java.lang.String)">report(String label)</a></code> method on the browser object.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
go "http://google.com"
report "google home page"
}</code></pre>
</div>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The <code>report()</code> method will thrown an exception if it is called and there is no configured <code>reportsDir</code>.
If you are going to use reporting you <strong>must</strong> specify a <code>reportsDir</code> via config.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Assuming that we configured a <code>reportsDir</code> of “<code>reports/geb</code>”, after running this script we will find two files in this directory:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>google home page.html</code> - A HTML dump of the page source</p>
</li>
<li>
<p><code>google home page.png</code> - A screenshot of the browser as a PNG file (if the driver implementation supports this)</p>
</li>
</ul>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>To avoid issues with reserved characters in filenames, Geb replaces any character in the report name that is not an alphanumeric, a space or a hyphen with an underscore.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="report-group"><a class="link" href="#report-group">12.1. The report group</a></h3>
<div class="paragraph">
<p>The configuration mechanism allows you to specify the base <code>reportsDir</code> which is where reports are written to by default.
It is also possible to change the <a href="api/geb/Browser.html#reportGroup(java.lang.String)">report group</a> to a relative path inside this directory.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
reportGroup "google"
go "http://google.com"
report "home page"
reportGroup "geb"
go "http://gebish.org"
report "home page"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We have now created the following files inside the <code>reportsDir</code></p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>google/home page.html</code></p>
</li>
<li>
<p><code>google/home page.png</code></p>
</li>
<li>
<p><code>wikipedia/home page.html</code></p>
</li>
<li>
<p><code>wikipedia/home page.png</code></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The browser will create the directory for the report group as needed.
By default, the report group is not set which means that reports are written to the base of the <code>reportsDir</code>.
To go back to this after setting a report group, simply call <code>reportGroup(null)</code>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>It is common for test integrations to manage the report group for you, setting it to the name of the test class.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="listening-to-reporting"><a class="link" href="#listening-to-reporting">12.2. Listening to reporting</a></h3>
<div class="paragraph">
<p>It is possible to register a listener on the reporter that gets notified when a report is taken.
This was added to make it possible to write something to stdout when a report is taken, which is how the
<a href="https://wiki.jenkins-ci.org/display/JENKINS/JUnit+Attachments+Plugin">Jenkins JUnit Attachments Plugin</a> makes it possible to associate arbitrary files to test execution.
Reporting listeners are of type <code><a href="api/geb/report/ReportingListener.html">ReportingListener</a></code> can be specified as part of the config…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.report.*
reportingListener = new ReportingListener() {
void onReport(Reporter reporter, ReportState reportState, List&lt;File&gt; reportFiles) {
reportFiles.each {
println "[[ATTACHMENT|$it.absolutePath]]"
}
}
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="cleaning"><a class="link" href="#cleaning">12.3. Cleaning</a></h3>
<div class="paragraph">
<p>Geb does not automatically clean the reports dir for you. It does however provide a method that you can call to do this.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">Browser.drive {
cleanReportGroupDir()
go "http://google.com"
report "home page"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code><a href="api/geb/Browser.html#cleanReportGroupDir()">cleanReportGroupDir()</a></code> method will remove whatever the reports group dir is set to at the time.
If it cannot do this it will throw an exception.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The Spock, JUnit and TestNG test integrations <strong>do</strong> automatically clean the reports dir for you, see the <a href="#testing-reporting">section in the testing chapter</a> on these integrations.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="testing"><a class="link" href="#testing">13. Testing</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Geb provides first class support for functional web testing via integration with popular testing frameworks such as <a href="http://spockframework.org/">Spock</a>, <a href="http://www.junit.org/">JUnit</a>, <a href="http://testng.org/">TestNg</a> and <a href="https://github.com/cucumber/cucumber-jvm">Cucumber-JVM</a>.</p>
</div>
<div class="sect2">
<h3 id="spock-junit-testng"><a class="link" href="#spock-junit-testng">13.1. Spock, JUnit &amp; TestNG</a></h3>
<div class="paragraph">
<p>The Spock, JUnit and TestNG integrations work fundamentally the same way.
They provide subclasses that setup a <code><a href="api/geb/Browser.html">Browser</a></code> instance that all method calls and property accesses/references resolve against via Groovy&#8217;s <code>methodMissing</code> and <code>propertyMissing</code> mechanism.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Recall that the browser instance also forwards any method calls or property accesses/references that it can&#8217;t handle to its current page object, which helps to remove a lot of noise from the test.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Consider the following Spock spec…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.spock.GebSpec
class FunctionalSpec extends GebSpec {
def "go to login"() {
when:
go "/login"
then:
title == "Login Screen"
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Which is equivalent to…</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.spock.GebSpec
class FunctionalSpec extends GebSpec {
def "verbosely go to login"() {
when:
browser.go "/login"
then:
browser.page.title == "Login Screen"
}
}</code></pre>
</div>
</div>
<div class="sect3">
<h4 id="configuration-2"><a class="link" href="#configuration-2">13.1.1. Configuration</a></h4>
<div class="paragraph">
<p>The browser instance is created by the testing integrations. The <a href="#configuration">configuration mechanism</a> allows you to control aspects such as the driver implementation and base URL.</p>
</div>
</div>
<div class="sect3">
<h4 id="testing-reporting"><a class="link" href="#testing-reporting">13.1.2. Reporting</a></h4>
<div class="paragraph">
<p>The Spock, JUnit and TestNG integrations also ship a superclass (the name of the class for each integration module is provided below) that automatically takes reports at the end of test methods with
the label “end”.
They also set the <a href="#report-group">report group</a> to the name of the test class (substituting “.” with “/”).</p>
</div>
<div class="paragraph">
<p>The <code><a href="api/geb/Browser.html#report(java.lang.String)">report(String label)</a></code> browser method is replaced with a specialised version.
This method works the same as the browser method, but adds counters and the current test method name as prefixes to the given label.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">package my.tests
import geb.spock.GebReportingSpec
class ReportingFunctionalSpec extends GebReportingSpec {
def "login"() {
when:
go "/login"
username = "me"
report "login screen" <i class="conum" data-value="1"></i><b>(1)</b>
login().click()
then:
title == "Logged in!"
}
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Take a report of the login screen.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Assuming a configured <code>reportsDir</code> of <code>reports/geb</code> and the default reporters (i.e. <code><a href="api/geb/report/ScreenshotReporter.html">ScreenshotReporter</a></code> and <code><a href="api/geb/report/PageSourceReporter.html">PageSourceReporter</a></code>), we would find the following files:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>reports/geb/my/tests/ReportingFunctionalSpec/001-001-login-login screen.html</code></p>
</li>
<li>
<p><code>reports/geb/my/tests/ReportingFunctionalSpec/001-001-login-login screen.png</code></p>
</li>
<li>
<p><code>reports/geb/my/tests/ReportingFunctionalSpec/001-002-login-end.html</code></p>
</li>
<li>
<p><code>reports/geb/my/tests/ReportingFunctionalSpec/001-002-login-end.png</code></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The report file name format is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>«test method number»-«report number in test method»-«test method name»-«label».«extension»</pre>
</div>
</div>
<div class="paragraph">
<p>Reporting is an extremely useful feature and can help you diagnose test failures much easier.
Wherever possible, favour the use of the auto-reporting base classes.</p>
</div>
</div>
<div class="sect3">
<h4 id="cookie-management-in-tests"><a class="link" href="#cookie-management-in-tests">13.1.3. Cookie management</a></h4>
<div class="paragraph">
<p>The Spock, JUnit and TestNG integrations will automatically clear the browser&#8217;s cookies for the current domain at the end of each test method.
For JUnit 3 this happens in the <code>tearDown()</code> method in <code>geb.junit3.GebTest</code>, for JUnit 4 it happens in an <code>@After</code> method in <code>geb.junit4.GebTest</code> and for TestNG it happens in an <code>@AfterMethod</code> method
in <code>geb.testng.GebTestTrait</code>.</p>
</div>
<div class="paragraph">
<p>The <code>geb.spock.GebSpec</code> class will clear the cookies in the <code>cleanup()</code> method unless the spec is <code>@Stepwise</code>, in which case they are cleared in <code>cleanupSpec()</code> (meaning that all feature methods in a
stepwise spec share the same browser state).</p>
</div>
<div class="paragraph">
<p>This auto-clearing of cookies can be <a href="#auto-clearing-cookies-configuration">disabled via configuration</a>.</p>
</div>
<div class="paragraph">
<p>If you need to clear cookies in multiple domains you will need to manually track the urls and call <code><a href="api/geb/Browser.html#clearCookies(java.lang.String)">clearCookies(String&#8230;&#8203; additionalUrls)</a></code>.</p>
</div>
</div>
<div class="sect3">
<h4 id="jar-and-class-names"><a class="link" href="#jar-and-class-names">13.1.4. JAR and class names</a></h4>
<div class="paragraph">
<p>The following table illustrates the specific JARs and class names for various test frameworks that Geb integrates with.</p>
</div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Framework</th>
<th class="tableblock halign-left valign-top">JAR</th>
<th class="tableblock halign-left valign-top">Base Class / Trait</th>
<th class="tableblock halign-left valign-top">Reporting Base Class / Trait</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Spock</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://mvnrepository.com/artifact/org.gebish/geb-spock">geb-spock</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="api/geb/spock/GebSpec.html"><code>geb.spock.GebSpec</code></a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="api/geb/spock/GebReportingSpec.html"><code>geb.spock.GebReportingSpec</code></a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">JUnit 4</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://mvnrepository.com/artifact/org.gebish/geb-junit4">geb-junit4</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="api/geb/junit4/GebTest.html"><code>geb.junit4.GebTest</code></a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="api/geb/junit4/GebReportingTest.html"><code>geb.junit4.GebReportingTest</code></a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">JUnit 3</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://mvnrepository.com/artifact/org.gebish/geb-junit3">geb-junit3</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="api/geb/junit3/GebTest.html"><code>geb.junit3.GebTest</code></a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="api/geb/junit3/GebReportingTest.html"><code>geb.junit3.GebReportingTest</code></a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">TestNG</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://mvnrepository.com/artifact/org.gebish/geb-testng">geb-testng</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="api/geb/testng/GebTestTrait.html"><code>geb.testng.GebTestTrait</code></a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="api/geb/testng/GebReportingTestTrait.html"><code>geb.testng.GebReportingTestTrait</code></a></p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect3">
<h4 id="example-projects"><a class="link" href="#example-projects">13.1.5. Example projects</a></h4>
<div class="paragraph">
<p>The following projects can be used as starting references:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/geb/geb-example-gradle">geb-example-gradle</a></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect2">
<h3 id="cucumber-jvm"><a class="link" href="#cucumber-jvm">13.2. Cucumber (Cucumber-JVM)</a></h3>
<div class="paragraph">
<p>It is possible to both:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Write your own <a href="https://github.com/cucumber/cucumber-jvm">Cucumber-JVM</a> steps that manipulate Geb</p>
</li>
<li>
<p>Use a library of pre-built steps that drives Geb to do many common tasks</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="writing-your-own-steps"><a class="link" href="#writing-your-own-steps">13.2.1. Writing your own steps</a></h4>
<div class="paragraph">
<p>Use Geb&#8217;s <a href="#binding">binding management features</a> to bind a browser in before / after hooks, often in a file named <code>env.groovy</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def bindingUpdater
Before() { scenario -&gt;
bindingUpdater = new BindingUpdater(binding, new Browser())
bindingUpdater.initialize()
}
After() { scenario -&gt;
bindingUpdater.remove()
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Then normal Geb commands and objects are available in your Cucumber steps:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import static cucumber.api.groovy.EN.*
Given(~/I am on the DuckDuckGo search page/) { -&gt;
to DuckDuckGoHomePage
waitFor { at(DuckDuckGoHomePage) }
}
When(~/I search for "(.*)"/) { String query -&gt;
page.search.value(query)
page.searchButton.click()
}
Then(~/I can see some results/) { -&gt;
assert at(DuckDuckGoResultsPage)
}
Then(~/the first link should be "(.*)"/) { String text -&gt;
waitFor { page.results }
assert page.resultLink(0).text()?.contains(text)
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="using-pre-built-steps"><a class="link" href="#using-pre-built-steps">13.2.2. Using pre-built steps</a></h4>
<div class="paragraph">
<p>The <a href="https://github.com/tomdcc/geb-cucumber">geb-cucumber</a> project has a set of pre-built cucumber steps that drive Geb. So for example a feature with steps similar to the above would look like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>When I go to the duck duck go home page
And I enter "cucumber-jvm github" into the search field
And I click the search button
Then the results table 1st row link matches /cucumber\/cucumber-jvm · GitHub.*/</pre>
</div>
</div>
<div class="paragraph">
<p>See <a href="https://github.com/tomdcc/geb-cucumber">geb-cucumber</a> for more examples.</p>
</div>
<div class="paragraph">
<p>geb-cucumber also does Geb binding automatically, so if it is picked up you don&#8217;t need to do it yourself as above.</p>
</div>
</div>
<div class="sect3">
<h4 id="example-project"><a class="link" href="#example-project">13.2.3. Example project</a></h4>
<div class="paragraph">
<p>The following project has examples of both writing your own steps and using geb-cucumber:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/geb/geb-example-cucumber-jvm">geb-example-cucumber-jvm</a></p>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="cloud-browser-testing"><a class="link" href="#cloud-browser-testing">14. Cloud browser testing</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>When you want to perform web testing on multiple browsers and operating systems, it can be quite complicated to maintain machines for each of the target environments.
There are a few companies that provide "remote web browsers as a service", making it easy to do this sort of matrix testing without having to maintain the multiple browser installations yourself.
Geb provides easy integration with two such services, <a href="https://saucelabs.com/">SauceLabs</a> and <a href="http://www.browserstack.com/">BrowserStack</a>.
This integration includes two parts: assistance with creating a driver in <code>GebConfig.groovy</code> and a Gradle plugin.</p>
</div>
<div class="sect2">
<h3 id="creating-a-driver"><a class="link" href="#creating-a-driver">14.1. Creating a driver</a></h3>
<div class="paragraph">
<p>For both SauceLabs and BrowserStack, a special driver factory is provided that, given a browser specification as well as an username and access key, creates an instance of <code>RemoteWebDriver</code> configured
to use a browser in the cloud.
Examples of typical usage in <code>GebConfig.groovy</code> are included below.
They will configure Geb to run in SauceLabs/BrowserStack if the appropriate system property is set, and if not it will use whatever driver that is configured.
This is useful if you want to run the code in a local browser for development.
In theory you could use any system property to pass the browser specification but <code>geb.saucelabs.browser</code>/<code>geb.browserstack.browser</code> are also used by the Geb Gradle plugins, so it&#8217;s a good idea to
stick with those property names.</p>
</div>
<div class="paragraph">
<p>The first parameter passed to the <code>create()</code> method is a ”browser specification“ and it should be a list of required browser capabilities in Java properties file format:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>browserName=«browser name as per values of fields in org.openqa.selenium.remote.BrowserType»
platform=«platform as per enum item names in org.openqa.selenium.Platform»
version=«version»</pre>
</div>
</div>
<div class="paragraph">
<p>Assuming you&#8217;re using the following snippet in your <code>GebConfig.groovy</code> to execute your code via SauceLabs with Firefox 19 on Linux, you would set the <code>geb.saucelabs.browser</code> system property to:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>browserName=firefox
platform=LINUX
version=19</pre>
</div>
</div>
<div class="paragraph">
<p>and to execute it with IE 9 on Vista to:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>browserName=internet explorer
platform=VISTA
version=9</pre>
</div>
</div>
<div class="paragraph">
<p>Some browsers like Chrome automatically update to the latest version; for these browsers you don&#8217;t need to specify the version as there&#8217;s only one, and you would use something like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>browserName=chrome
platform=MAC</pre>
</div>
</div>
<div class="paragraph">
<p>as the ”browser specification“. For a full list of available browsers, versions and operating systems refer to your cloud provider&#8217;s documentation:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://saucelabs.com/docs/platforms/webdriver">SauceLabs platform list</a></p>
</li>
<li>
<p><a href="http://www.browserstack.com/list-of-browsers-and-platforms?product=automate">BrowserStack Browsers and Platforms list</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Please note that Geb Gradle plugins can set the <code>geb.saucelabs.browser</code>/<code>geb.browserstack.browser</code> system properties for you using the aforementioned format.</p>
</div>
<div class="paragraph">
<p>Following the browser specification are the username and access key used to identify your account with the cloud provider.
The example uses two environment variables to access this information.
This is usually the easiest way of passing something secret to your build in open CI services like <a href="https://drone.io/">drone.io</a> or <a href="https://travis-ci.org/">Travis CI</a> if your code is public, but you can
use other mechanisms if desired.</p>
</div>
<div class="paragraph">
<p>You can optionally pass additional configuration settings by providing a Map to the <code>create()</code> method as the last parameter.
The configuration options available are described in your cloud provider&#8217;s documentation:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://saucelabs.com/docs/additional-config">SauceLabs additional config</a></p>
</li>
<li>
<p><a href="http://www.browserstack.com/automate/capabilities">BrowserStack Capabilities</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Finally, there is also <a href="api/geb/driver/CloudDriverFactory.html#create(java.lang.String,%20java.lang.String,%20Map%3CString,%20Object%3E)">an overloaded version of <code>create()</code> method</a> available that
doesn&#8217;t take a string specification and allows you to simply specify all the required capabilities using a map.
This method might be useful if you just want to use the factory, but don&#8217;t need the build level parametrization.</p>
</div>
<div class="sect3">
<h4 id="sauce-labs-driver-factory"><a class="link" href="#sauce-labs-driver-factory">14.1.1. <code>SauceLabsDriverFactory</code></a></h4>
<div class="paragraph">
<p>The following is an example of utilizing <code>SauceLabsDriverFactory</code> in <code>GebConfig.groovy</code> to configure a driver that will use a browser provided in the SauceLabs cloud.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def sauceLabsBrowser = System.getProperty("geb.saucelabs.browser")
if (sauceLabsBrowser) {
driver = {
def username = System.getenv("GEB_SAUCE_LABS_USER")
assert username
def accessKey = System.getenv("GEB_SAUCE_LABS_ACCESS_PASSWORD")
assert accessKey
new SauceLabsDriverFactory().create(sauceLabsBrowser, username, accessKey)
}
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="browser-stack-driver-factory"><a class="link" href="#browser-stack-driver-factory">14.1.2. <code>BrowserStackDriverFactory</code></a></h4>
<div class="paragraph">
<p>The following is an example of utilizing <code>BrowserStackDriverFactory</code> in <code>GebConfig.groovy</code> to configure a driver that will use a browser provided in the BrowserStack cloud.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def browserStackBrowser = System.getProperty("geb.browserstack.browser")
if (browserStackBrowser) {
driver = {
def username = System.getenv("GEB_BROWSERSTACK_USERNAME")
assert username
def accessKey = System.getenv("GEB_BROWSERSTACK_AUTHKEY")
assert accessKey
new BrowserStackDriverFactory().create(browserStackBrowser, username, accessKey)
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>If using <code>localIdentifier</code> support:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">def browserStackBrowser = System.getProperty("geb.browserstack.browser")
if (browserStackBrowser) {
driver = {
def username = System.getenv("GEB_BROWSERSTACK_USERNAME")
assert username
def accessKey = System.getenv("GEB_BROWSERSTACK_AUTHKEY")
assert accessKey
def localId = System.getenv("GEB_BROWSERSTACK_LOCALID")
assert localId
new BrowserStackDriverFactory().create(browserStackBrowser, username, accessKey, localId)
}
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="gradle-plugins"><a class="link" href="#gradle-plugins">14.2. Gradle plugins</a></h3>
<div class="paragraph">
<p>For both SauceLabs and BrowserStack, Geb provides a Gradle plugin which simplifies declaring the account and browsers that are desired, as well as configuring a tunnel to allow the cloud provider to
access local applications.
These plugins allow easily creating multiple <code>Test</code> tasks that will have the appropriate <code>geb.PROVIDER.browser</code> property set (where <em>PROVIDER</em> is either <code>saucelabs</code> or <code>browserstack</code>).
The value of that property can be then passed in configuration file to <a href="#sauce-labs-driver-factory"><code>SauceLabsDriverFactory</code></a>/<a href="#browser-stack-driver-factory"><code>BrowserStackDriverFactory</code></a> as the ”browser specification“.
Examples of typical usage are included below.</p>
</div>
<div class="sect3">
<h4 id="geb-saucelabs-plugin"><a class="link" href="#geb-saucelabs-plugin">14.2.1. geb-saucelabs plugin</a></h4>
<div class="paragraph">
<p>Following is an example of using the geb-saucelabs Gradle plugin.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.gradle.saucelabs.SauceAccount
apply plugin: "geb-saucelabs" <i class="conum" data-value="1"></i><b>(1)</b>
buildscript { <i class="conum" data-value="2"></i><b>(2)</b>
repositories {
mavenCentral()
}
dependencies {
classpath 'org.gebish:geb-gradle:2.0'
}
}
repositories { <i class="conum" data-value="3"></i><b>(3)</b>
maven { url "http://repository-saucelabs.forge.cloudbees.com/release" }
}
dependencies { <i class="conum" data-value="4"></i><b>(4)</b>
sauceConnect "com.saucelabs:ci-sauce:1.81"
}
sauceLabs {
browsers { <i class="conum" data-value="5"></i><b>(5)</b>
firefox_linux_19
chrome_mac
delegate."internet explorer_vista_9"
nexus4 { <i class="conum" data-value="6"></i><b>(6)</b>
capabilities(
browserName: "android",
platform: "Linux",
version: "4.4",
deviceName: "LG Nexus 4"
)
}
}
task { <i class="conum" data-value="7"></i><b>(7)</b>
testClassesDir = test.testClassesDir
testSrcDirs = test.testSrcDirs
classpath = test.classpath
}
account { <i class="conum" data-value="8"></i><b>(8)</b>
username = System.getenv(SauceAccount.USER_ENV_VAR)
accessKey = System.getenv(SauceAccount.ACCESS_KEY_ENV_VAR)
}
connect { <i class="conum" data-value="9"></i><b>(9)</b>
port = 4444 <i class="conum" data-value="10"></i><b>(10)</b>
additionalOptions = ['--proxy', 'proxy.example.com:8080'] <i class="conum" data-value="11"></i><b>(11)</b>
}
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Apply the plugin to the build.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Specify how to resolve the plugin.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Declare a repository for resolving SauceConnect.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Declare version of SauceConnect to be used as part of the <code>sauceConnect</code> configuration. This will be used by tasks that open a <a href="https://saucelabs.com/docs/connect">SauceConnect</a> tunnel before
running the generated test tasks which means that the browsers in the cloud will have localhost pointing at the machine running the build.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Declare that tests should run in 3 different browsers using the shorthand syntax; this will generate the following <code>Test</code> tasks: <code>firefoxLinux19Test</code>, <code>chromeMacTest</code> and
<code>internet explorerVista9Test</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Explicitly specify the required browser capabilities if the shorthand syntax doesn&#8217;t allow you to express all needed capabilities; the example will generate a <code>Test</code> task named <code>nexus4Test</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Configure all of the generated test tasks; for each of them the closure is run with delegate set to a test task being configured.</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td>Pass credentials for <a href="https://saucelabs.com/docs/connect">SauceConnect</a>.</td>
</tr>
<tr>
<td><i class="conum" data-value="9"></i><b>9</b></td>
<td>Additionally configure <a href="https://saucelabs.com/docs/connect">SauceConnect</a> if desired.</td>
</tr>
<tr>
<td><i class="conum" data-value="10"></i><b>10</b></td>
<td>Override the port used by SauceConnect, defaults to 4445.</td>
</tr>
<tr>
<td><i class="conum" data-value="11"></i><b>11</b></td>
<td>Pass additional <a href="https://docs.saucelabs.com/reference/sauce-connect/#command-line-options">command line options</a> to SauceConnect.</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>You can use <code>allSauceLabsTests</code> task that will depend on all of the generated test tasks to run all of them during a build.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="geb-browserstack-plugin"><a class="link" href="#geb-browserstack-plugin">14.2.2. geb-browserstack plugin</a></h4>
<div class="paragraph">
<p>Following is an example of using the geb-browserstack Gradle plugin.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">import geb.gradle.browserstack.BrowserStackAccount
apply plugin: "geb-browserstack" <i class="conum" data-value="1"></i><b>(1)</b>
buildscript { <i class="conum" data-value="2"></i><b>(2)</b>
repositories {
mavenCentral()
}
dependencies {
classpath 'org.gebish:geb-gradle:2.0'
}
}
browserStack {
application 'http://localhost:8080' <i class="conum" data-value="3"></i><b>(3)</b>
forceLocal = true <i class="conum" data-value="4"></i><b>(4)</b>
browsers { <i class="conum" data-value="5"></i><b>(5)</b>
firefox_mac_19
chrome_mac
delegate."internet explorer_windows_9"
nexus4 { <i class="conum" data-value="6"></i><b>(6)</b>
capabilities browserName: "android", platform: "ANDROID", device: "Google Nexus 4"
}
}
task { <i class="conum" data-value="7"></i><b>(7)</b>
testClassesDir = test.testClassesDir
testSrcDirs = test.testSrcDirs
classpath = test.classpath
}
account { <i class="conum" data-value="8"></i><b>(8)</b>
username = System.getenv(BrowserStackAccount.USER_ENV_VAR)
accessKey = System.getenv(BrowserStackAccount.ACCESS_KEY_ENV_VAR)
}
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Apply the plugin to the build.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Specify how to resolve the plugin.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Specify which urls the BrowserStack Tunnel should be able to access.
Multiple applications can be specified.
If no applications are specified, the tunnel will not be restricted to particular URLs.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Configure BrowserStack tunnel to route all traffic via the local machine.
This configuration property controls the <code>-forcelocal</code> flag and the default value for it is <code>false</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Declare that tests should run in 3 different browsers using the shorthand syntax; this will generate the following <code>Test</code> tasks: <code>firefoxLinux19Test</code>, <code>chromeMacTest</code> and
<code>internet explorerVista9Test</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Explicitly specify the required browser capabilities if the shorthand syntax doesn&#8217;t allow you to express all needed capabilities; the example will generate a <code>Test</code> task named <code>nexus4Test</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Configure all of the generated test tasks; for each of them the closure is run with delegate set to a test task being configured.</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td>Pass credentials for BrowserStack.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>It&#8217;s also possible to specify location and credentials for the proxy to be used with the BrowserStack Tunnel:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">browserStack {
account {
proxyHost = '127.0.0.1'
proxyPort = '8080'
proxyUser = 'user'
proxyPass = 'secret'
}
}</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>You can use <code>allBrowserStackTests</code> task that will depend on all of the generated test tasks to run all of them during a build.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="build-integrations"><a class="link" href="#build-integrations">15. Build system &amp; framework integrations</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>This kind of integration for Geb is typically focused on managing the base URL and reports dir, as build systems tend to be able to provide this configuration (via the
<a href="#build-adapter-config">build adapter</a> mechanism).</p>
</div>
<div class="sect2">
<h3 id="gradle"><a class="link" href="#gradle">15.1. Gradle</a></h3>
<div class="paragraph">
<p>Using Geb with Gradle simply involves pulling in the appropriate dependencies, and configuring the base URL and reports dir in the build script if they are necessary.</p>
</div>
<div class="paragraph">
<p>Below is a valid Gradle <code>build.gradle</code> file for working with Geb for testing.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">apply plugin: "groovy"
repositories {
mavenCentral()
}
dependencies {
groovy "org.codehaus.groovy:groovy-all:2.4.7"
def gebVersion = "2.0"
def seleniumVersion = "3.6.0"
// If using Spock, need to depend on geb-spock
testCompile "org.gebish:geb-spock:${geb-version}"
testCompile "org.spockframework:spock-core:1.1-groovy-2.4"
// If using JUnit, need to depend on geb-junit (3 or 4)
testCompile "org.gebish:geb-junit4:${geb-version}"
testCompile "junit:junit-dep:4.8.2"
// Need a driver implementation
testCompile "org.seleniumhq.selenium:selenium-firefox-driver:${selenium-version}"
testRuntime "org.seleniumhq.selenium:selenium-support:${selenium-version}"
}
test {
systemProperties "geb.build.reportsDir": "$reportsDir/geb"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>There is a Gradle example project available.</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/geb/geb-example-gradle">geb-example-gradle</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="maven"><a class="link" href="#maven">15.2. Maven</a></h3>
<div class="paragraph">
<p>Using Geb with Maven simply involves pulling in the appropriate dependencies, and configuring the base URL and reports dir in the build script if they are necessary.</p>
</div>
<div class="paragraph">
<p>Below is a valid <code>pom.xml</code> file for working with Geb for testing (with Spock).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-xml" data-lang="xml">&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
&lt;groupId&gt;org.gebish.example&lt;/groupId&gt;
&lt;artifactId&gt;geb-maven-example&lt;/artifactId&gt;
&lt;packaging&gt;jar&lt;/packaging&gt;
&lt;version&gt;1&lt;/version&gt;
&lt;name&gt;Geb Maven Example&lt;/name&gt;
&lt;url&gt;http://www.gebish.org&lt;/url&gt;
&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.codehaus.groovy&lt;/groupId&gt;
&lt;artifactId&gt;groovy-all&lt;/artifactId&gt;
&lt;version&gt;2.4.7&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;junit&lt;/groupId&gt;
&lt;artifactId&gt;junit&lt;/artifactId&gt;
&lt;version&gt;4.8.2&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.spockframework&lt;/groupId&gt;
&lt;artifactId&gt;spock-core&lt;/artifactId&gt;
&lt;version&gt;1.1-groovy-2.4&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.gebish&lt;/groupId&gt;
&lt;artifactId&gt;geb-spock&lt;/artifactId&gt;
&lt;version&gt;2.0&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.seleniumhq.selenium&lt;/groupId&gt;
&lt;artifactId&gt;selenium-firefox-driver&lt;/artifactId&gt;
&lt;version&gt;3.6.0&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.seleniumhq.selenium&lt;/groupId&gt;
&lt;artifactId&gt;selenium-support&lt;/artifactId&gt;
&lt;version&gt;3.6.0&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
&lt;build&gt;
&lt;plugins&gt;
&lt;plugin&gt;
&lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
&lt;artifactId&gt;maven-surefire-plugin&lt;/artifactId&gt;
&lt;version&gt;2.18.1&lt;/version&gt;
&lt;configuration&gt;
&lt;includes&gt;
&lt;include&gt;*Spec.*&lt;/include&gt;
&lt;/includes&gt;
&lt;systemPropertyVariables&gt;
&lt;geb.build.baseUrl&gt;http://google.com/ncr&lt;/geb.build.baseUrl&gt;
&lt;geb.build.reportsDir&gt;target/test-reports/geb&lt;/geb.build.reportsDir&gt;
&lt;/systemPropertyVariables&gt;
&lt;/configuration&gt;
&lt;/plugin&gt;
&lt;plugin&gt;
&lt;groupId&gt;org.codehaus.gmaven&lt;/groupId&gt;
&lt;artifactId&gt;gmaven-plugin&lt;/artifactId&gt;
&lt;version&gt;1.3&lt;/version&gt;
&lt;configuration&gt;
&lt;providerSelection&gt;1.7&lt;/providerSelection&gt;
&lt;/configuration&gt;
&lt;executions&gt;
&lt;execution&gt;
&lt;goals&gt;
&lt;goal&gt;testCompile&lt;/goal&gt;
&lt;/goals&gt;
&lt;/execution&gt;
&lt;/executions&gt;
&lt;/plugin&gt;
&lt;/plugins&gt;
&lt;/build&gt;
&lt;/project&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>There is a Maven example project available.</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/geb/geb-example-maven">geb-example-maven</a></p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="ide-support"><a class="link" href="#ide-support">16. IDE support</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Geb does not require any special plugins or configuration for use inside an IDE.
However, there are some considerations that will be addressed in this chapter.</p>
</div>
<div class="sect2">
<h3 id="execution"><a class="link" href="#execution">16.1. Execution</a></h3>
<div class="paragraph">
<p>Geb <em>scripts</em> can be executed in an IDE if that IDE supports executing Groovy scripts.
All IDEs that support Groovy typically support this.
There are typically only two concerns in the configuration of this: getting the Geb classes on the classpath, and the <code>GebConfig.groovy</code> file.</p>
</div>
<div class="paragraph">
<p>Geb <em>tests</em> can be executed in an IDE if that IDE supports Groovy scripts and the testing framework that you are using with Geb.
If you are using JUnit or Spock (which is based on JUnit) this is trivial, as all modern Java IDEs support JUnit.
As far as the IDE is concerned, the Geb test is simply a JUnit test and no special support is required.
As with executing scripts, the IDE must put the Geb classes on the classpath for test execution and the <code>GebConfig.groovy</code> file must be accessible (typically putting this file at the root of the
test source tree is sufficient).</p>
</div>
<div class="paragraph">
<p>In both cases, the simplest way to create such an IDE configuration is to use a build tool (such as Gradle or Maven) that supports IDE integration.
This will take care of the classpath setup and other concerns.</p>
</div>
</div>
<div class="sect2">
<h3 id="authoring-assistance-autocomplete-and-navigation"><a class="link" href="#authoring-assistance-autocomplete-and-navigation">16.2. Authoring assistance (autocomplete and navigation)</a></h3>
<div class="paragraph">
<p>This section discusses what kind of authoring assistance can be provided by IDEs and usage patterns that enable better authoring support.</p>
</div>
<div class="sect3">
<h4 id="dynamism-and-conciseness-vs-tooling-support"><a class="link" href="#dynamism-and-conciseness-vs-tooling-support">16.2.1. Dynamism and conciseness vs tooling support</a></h4>
<div class="paragraph">
<p>Geb heavily embraces the dynamic typing offered by Groovy, to achieve conciseness for the purpose of readability.
This immediately reduces the amount of authoring assistance that an IDE can provide when authoring Geb code.
This is an intentional compromise.
The primary cost in functional/acceptance testing is in the <em>maintenance</em> of the test suite over time.
Geb optimizes for this in several ways, one of which being the focus on intention revealing code (which is achieved through conciseness).</p>
</div>
<div class="paragraph">
<p>That said, if authoring support is a concern for you, read on to learn for details on ways to forsake conciseness in order to improve authoring support.</p>
</div>
</div>
<div class="sect3">
<h4 id="strong-typing"><a class="link" href="#strong-typing">16.2.2. Strong typing</a></h4>
<div class="paragraph">
<p>In order to gain improved authoring support, you must include types in your tests and page objects.
Additionally, you must explicitly access the browser and page objects instead of relying on dynamic dispatch.</p>
</div>
<div class="paragraph">
<p>Here&#8217;s an example of idiomatic (untyped) Geb code.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">to HomePage
loginPageLink.click()
at LoginPage
username = "user1"
password = "password1"
loginButton.click()
at SecurePage</code></pre>
</div>
</div>
<div class="paragraph">
<p>The same code written with types would look like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">HomePage homePage = browser.to HomePage
homePage.loginPageLink.click()
LoginPage loginPage = browser.at LoginPage
SecurePage securePage = loginPage.login("user1", "password1")</code></pre>
</div>
</div>
<div class="paragraph">
<p>Where the page objects are:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">class HomePage extends Page {
Navigator getLoginPageLink() {
$("#loginLink")
}
}
class LoginPage extends Page {
static at = { title == "Login Page" }
Navigator getLoginButton() {
$("input", type: "submit")
}
SecurePage login(String username, String password) {
$(name: "username").value username
$(name: "password").value password
loginButton.click()
browser.at SecurePage
}
}
class SecurePage extends Page {
static at = { title == "Secure Page" }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In summary:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Use the <code>browser</code> object explicitly (made available by the testing adapters)</p>
</li>
<li>
<p>Use the page instance returned by the <code>to()</code> and <code>at()</code> methods instead of calling through the browser</p>
</li>
<li>
<p>Use methods on the <code>Page</code> classes instead of the <code>content {}</code> block and dynamic properties</p>
</li>
<li>
<p>If you need to use content definition options like <code>required:</code> and <code>wait:</code> then you can still reference content elements defined using the DSL in methods on <code>Page</code> and <code>Module</code> classes as usual, e.g.:</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">static content = {
async(wait: true) { $("#async") }
}
String asyncText() {
async.text() <i class="conum" data-value="1"></i><b>(1)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Wait here for the async definition to return a non-empty Navigator…</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Using this “typed” style is not an all or nothing proposition.
The typing options exist on a spectrum and can be used selectively where/when the cost of the extra “noise” is worth it to achieve better IDE support.
For example, a mix of using the <code>content {}</code> DSL and methods can easily be used.
The key enabler is to capture the result of the <code>to()</code> and <code>at()</code> methods in order to access the page object instance.</p>
</div>
<div class="sect4">
<h5 id="intellij-idea-support"><a class="link" href="#intellij-idea-support">IntelliJ IDEA support</a></h5>
<div class="paragraph">
<p>IntelliJ IDEA (since version 12) has special support for authoring Geb code.
This is built in to the Groovy support; no additional installations are required.</p>
</div>
<div class="paragraph">
<p>The support provides:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Understanding of implicit browser methods (e.g. <code>to()</code>, <code>at()</code>) in test classes (e.g. <code>extends GebSpec</code>)</p>
</li>
<li>
<p>Understanding of content defined via the Content DSL (within <code>Page</code> and <code>Module</code> classes only)</p>
</li>
<li>
<p>Completion in <code>at {}</code> and <code>content {}</code> blocks</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>This effectively enables more authoring support with less explicit type information.
The Geb development team would like to thank the good folks at JetBrains for adding this explicit support for Geb to IDEA.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="about-the-project"><a class="link" href="#about-the-project">17. About the project</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>The Geb home page can be found at <a href="http://www.gebish.org">http://www.gebish.org</a>.</p>
</div>
<div class="sect2">
<h3 id="api-reference"><a class="link" href="#api-reference">17.1. API reference</a></h3>
<div class="paragraph">
<p>The API reference can be found <a href="api/index.html">here</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="support-development"><a class="link" href="#support-development">17.2. Support &amp; development</a></h3>
<div class="paragraph">
<p>Support for Geb is offered on the <a href="mailto:geb-user@googlegroups.com">geb-user@googlegroups.com</a> mailing list, which can be subscribed to <a href="https://groups.google.com/forum/#!forum/geb-user">here</a>.</p>
</div>
<div class="paragraph">
<p>Ideas and new features for Geb can be discussed on the <a href="mailto:geb-dev@googlegroups.com">geb-dev@googlegroups.com</a> mailing list, which can be subscribed to <a href="https://groups.google.com/d/forum/geb-dev">here</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="credits"><a class="link" href="#credits">17.3. Credits</a></h3>
<div class="sect3">
<h4 id="committers"><a class="link" href="#committers">17.3.1. Committers</a></h4>
<div class="ulist">
<ul>
<li>
<p><a href="http://ldaley.com">Luke Daley</a></p>
</li>
<li>
<p><a href="http://blog.proxerd.pl">Marcin Erdmann</a></p>
</li>
<li>
<p><a href="https://github.com/chris-prior">Chris Prior</a></p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="contributors"><a class="link" href="#contributors">17.3.2. Contributors</a></h4>
<div class="ulist">
<ul>
<li>
<p><a href="http://adhockery.blogspot.com/">Robert Fletcher</a></p>
</li>
<li>
<p><a href="http://pniederw.wordpress.com/">Peter Niederwieser</a></p>
</li>
<li>
<p><a href="https://github.com/zolotov">Alexander Zolotov</a> - TestNG Integration</p>
</li>
<li>
<p><a href="http://c089.wordpress.com/">Christoph Neuroth</a> - Various fixes and patches</p>
</li>
<li>
<p><a href="https://github.com/antony">Antony Jones</a> - Various fixes and patches, doc improvements</p>
</li>
<li>
<p><a href="https://github.com/hennr">Jan-Hendrik Peters</a> - Doc improvements</p>
</li>
<li>
<p><a href="http://fbflex.wordpress.com/">Tomás Lin</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/measlyweasel">Jason Cahoon</a> - Bug fix around text matchers</p>
</li>
<li>
<p><a href="http://refaktor.blogspot.com/">Tomasz Kalkosiński</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/richdouglasevans">Rich Douglas Evans</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/idurkan">Ian Durkan</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/ColinHarrington">Colin Harrington</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/bherrmann7">Bob Herrmann</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/walterg2">George T Walters II</a> - Page option support for <code>withWindow()</code></p>
</li>
<li>
<p><a href="https://github.com/craigatk">Craig Atkinson</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/andyjduncan">Andy Duncan</a> - Fail fast when unexpected pages are encountered</p>
</li>
<li>
<p><a href="https://github.com/johnrengelman">John Engelman</a> - Grails integration improvements</p>
</li>
<li>
<p><a href="https://github.com/legart">Michael Legart</a> - Grails integration improvements</p>
</li>
<li>
<p><a href="https://github.com/graemerocher">Graeme Rocher</a> - Grails integration improvements</p>
</li>
<li>
<p><a href="https://github.com/craigatk">Craig Atkinson</a> - Bug fix around unexpected pages, fix for [<a href="https://github.com/geb/issues/issues/422">#422</a>]</p>
</li>
<li>
<p><a href="https://github.com/kgeis">Ken Geis</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/kellyrob99">Kelly Robinson</a> - Additional configuration parameters for SauceLabs</p>
</li>
<li>
<p><a href="https://github.com/tygerpatch">Todd Gerspacher</a> - Doc improvements, Cleaned up settings.gradle</p>
</li>
<li>
<p><a href="https://github.com/davidmc24">David M. Carr</a> - BrowserStack integration</p>
</li>
<li>
<p><a href="https://github.com/tomdcc">Tom Dunstan</a> - Cucumber integration and related documentation</p>
</li>
<li>
<p><a href="https://github.com/brian428">Brian Kotek</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/david-w-millar">David W Millar</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/alincode">Ai-Lin Liou</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/menonvarun">Varun Menon</a> - Selenium By selector support and related documentation, Support navigating to page instances in addition to classes</p>
</li>
<li>
<p><a href="https://github.com/adjohnson916">Anders D. Johnson</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/azusa">Hiroyuki Ohnaka</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/bodiam">Erik Pragt</a> - Initial migration of the manual to AsciiDoctor</p>
</li>
<li>
<p><a href="https://github.com/vijaybolleypally">Vijay Bolleypally</a> - Various fixes</p>
</li>
<li>
<p><a href="https://github.com/pierre-hilt">Pierre Hilt</a> - <code>hasNot()</code> filtering</p>
</li>
<li>
<p><a href="https://github.com/PoohSunny">Yotaro Takahashi</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/jochenberger">Jochen Berger</a> - Better error reporting when trying to set a nonexistent select option</p>
</li>
<li>
<p><a href="https://github.com/matankatz">Matan Katz</a> - Support for setting the <code>forcelocal</code> flag for BrowserStack tunnel</p>
</li>
<li>
<p><a href="https://github.com/victorparmar">Victor Parmar</a> - Add configuration option for including cause string representation in message of <code>WaitTimeoutException</code></p>
</li>
<li>
<p><a href="https://github.com/berardino">Berardino la Torre</a> - Fix incorrect delegation of waitFor() methods</p>
</li>
<li>
<p><a href="https://github.com/madmas">Markus Schlichting</a> - Doc fixes</p>
</li>
<li>
<p><a href="https://github.com/ahitrin">Andrey Hitrin</a> - Improvement to character replacing in report file names</p>
</li>
<li>
<p><a href="https://github.com/leo-13">Leo Fedorov</a> - Support for <code>reportOnTestFailureOnly</code> config option for Spock integration</p>
</li>
<li>
<p><a href="https://github.com/chrisbyrneham">Chris Byrneham</a> - Ability to specify proxy location and credentials to use with BrowserStack tunnel</p>
</li>
<li>
<p><a href="https://github.com/anshbansal">Aseem Bansal</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/topr">Tomasz Przybysz</a> - Unwrapping module to its declared type</p>
</li>
<li>
<p><a href="https://github.com/thecodesmith">Brian Stewart</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/JacobAae">Jacob Aae Mikkelsen</a> - Various fixes and improvements</p>
</li>
<li>
<p><a href="https://github.com/pradtke">Patrick Radtke</a> - Doc improvements</p>
</li>
<li>
<p><a href="https://github.com/leonard84">Leonard Brünings</a> - Fix for unexpected pages only being checked after at check fails for the expected page</p>
</li>
<li>
<p><a href="https://github.com/mahileeb">Lee Butts</a> - Improved error message when trying to select null on a single-select select element</p>
</li>
<li>
<p><a href="https://github.com/rickirunge">Ricki Runge</a> - Ignore case of tag names passed as part of css selectors to <code>Navigator.filter()</code></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect2">
<h3 id="history"><a class="link" href="#history">17.4. History</a></h3>
<div class="paragraph">
<p>This page lists the high level changes between versions of Geb.</p>
</div>
<div class="sect3">
<h4 id="2-0"><a class="link" href="#2-0">2.0</a></h4>
<div class="sect4">
<h5 id="new-features"><a class="link" href="#new-features">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p>Allow specifying the expected number of elements a content definition should return. [<a href="https://github.com/geb/issues/issues/149">#149</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="fixes"><a class="link" href="#fixes">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Improved error message when trying to select null on a single-select select element. [<a href="https://github.com/geb/issues/issues/477">#477</a>]</p>
</li>
<li>
<p>Return a list of results instead of <code>null</code> from <code>newWindow()</code> methods taking a window matching specification closure. [<a href="https://github.com/geb/issues/issues/476">#476</a>]</p>
</li>
<li>
<p>Ignore case of tag names passed as part of css selectors to <code>Navigator.filter()</code> [<a href="https://github.com/geb/issues/issues/482">#482</a>]</p>
</li>
<li>
<p>Gracefully handle incorrectly encoded URIs returned from <code>WebDriver.getCurrentUrl()</code> when navigating to urls. [<a href="https://github.com/geb/issues/issues/492">#492</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="improvements"><a class="link" href="#improvements">Improvements</a></h5>
<div class="ulist">
<ul>
<li>
<p>Change signatures of methods from <code>FrameSupport</code> to be more strongly typed. [<a href="https://github.com/geb/issues/issues/470">#470</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="breaking-changes"><a class="link" href="#breaking-changes">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Use Java 8 to compile the project. [<a href="https://github.com/geb/issues/issues/502">#502</a>]</p>
</li>
<li>
<p>Remove <code>FileInput#getFile()</code>. [<a href="https://github.com/geb/issues/issues/503">#503</a>]</p>
</li>
<li>
<p>Build using WebDriver 3.6.0. [<a href="https://github.com/geb/issues/issues/504">#504</a>]</p>
</li>
<li>
<p>Calling <code>click()</code> on empty navigators results in a <code>UnsupportedOperationException</code>. [<a href="https://github.com/geb/issues/issues/501">#501</a>]</p>
</li>
<li>
<p>Build using Spock 1.1. [<a href="https://github.com/geb/issues/issues/505">#505</a>]</p>
</li>
<li>
<p>Unchecked checkboxes return <code>null</code> as value for consistency with other input types. [<a href="https://github.com/geb/issues/issues/465">#465</a>]</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="1-1-1"><a class="link" href="#1-1-1">1.1.1</a></h4>
<div class="sect4">
<h5 id="fixes-2"><a class="link" href="#fixes-2">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Do not double encode query parameters when building urls for arguments passed to <code>go()</code>, <code>to()</code> and <code>via()</code>. [<a href="https://github.com/geb/issues/issues/469">#469</a>]</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="1-1"><a class="link" href="#1-1">1.1</a></h4>
<div class="sect4">
<h5 id="fixes-3"><a class="link" href="#fixes-3">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Delegate to <code>browser</code> instead of the module from blocks passed to <code>withFrame()</code> in module classes. [<a href="https://github.com/geb/issues/issues/461">#461</a>]</p>
</li>
<li>
<p>Fix implicit assertions in “at checkers” to not return <code>null</code> if the last call is to a void method. [<a href="https://github.com/geb/issues/issues/462">#462</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="improvements-2"><a class="link" href="#improvements-2">Improvements</a></h5>
<div class="ulist">
<ul>
<li>
<p>Support for selecting Edge as the browser using name in configuration. [<a href="https://github.com/geb/issues/issues/425">#425</a>]</p>
</li>
<li>
<p>Support for using url fragment identifiers when navigating to pages. [<a href="https://github.com/geb/issues/issues/463">#463</a>]</p>
</li>
<li>
<p>Unexpected pages are only checked after at check fails for the expected page. [<a href="https://github.com/geb/issues/issues/450">#450</a>]</p>
</li>
<li>
<p>Support equality checking between all core implementations of <code>Navigator</code>, based on comparing collections of web elements wrapped by them. [<a href="https://github.com/geb/issues/issues/459">#459</a>]</p>
</li>
<li>
<p>Support using label text to select checkboxes and using collections as value to select multiple checkboxes when dealing a number of checkboxes with the same name. [<a href="https://github.com/geb/issues/issues/460">#460</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="deprecations"><a class="link" href="#deprecations">Deprecations</a></h5>
<div class="ulist">
<ul>
<li>
<p>Grails 2.x plugin has been discontinued. [<a href="https://github.com/geb/issues/issues/456">#456</a>]</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="1-0"><a class="link" href="#1-0">1.0</a></h4>
<div class="sect4">
<h5 id="fixes-4"><a class="link" href="#fixes-4">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Fix the direct field operator shortcut (<code>@</code>) for accessing element attributes to work on classes extending <code>Module</code>. [<a href="https://github.com/geb/issues/issues/438">#438</a>]</p>
</li>
<li>
<p>Fix reporting on failure only in <code>GebReportingSpec</code> to work with Spock 1.1. [<a href="https://github.com/geb/issues/issues/445">#445</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="improvements-3"><a class="link" href="#improvements-3">Improvements</a></h5>
<div class="ulist">
<ul>
<li>
<p>Add ability to unwrap modules returned from content dsl to their original type. [<a href="https://github.com/geb/issues/issues/434">#434</a>]</p>
</li>
<li>
<p>Add support for using attribute css selectors with navigator filtering methods like <code>filter()</code>, <code>not()</code>, <code>closest()</code>, etc. [<a href="https://github.com/geb/issues/issues/437">#437</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="breaking-changes-2"><a class="link" href="#breaking-changes-2">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p><code>geb.testng.GebTest</code> and <code>geb.testng.GebReportingTest</code> which were deprecated in 0.13.0 have been removed.</p>
</li>
<li>
<p><code>isDisabled()</code>, <code>isEnabled()</code>, <code>isReadOnly()</code> and <code>isEditable()</code> methods of <code>Navigator</code> which were deprecated in 0.12.0 have been removed.</p>
</li>
<li>
<p>Loosely typed <code>module()</code> and <code>moduleList()</code> methods of the content DSL which were deprecated in 0.12.0 have been removed.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-13-1"><a class="link" href="#0-13-1">0.13.1</a></h4>
<div class="sect4">
<h5 id="fixes-5"><a class="link" href="#fixes-5">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Fix a MissingMethodException thrown from <code>Navigator.value()</code> when using Groovy versions &lt; 2.4.0. [<a href="https://github.com/geb/issues/issues/422">#422</a>]</p>
</li>
<li>
<p>Don&#8217;t unnecessarily synchronize methods of <code>CachingDirverFactory.ThreadLocalCache</code>. [<a href="https://github.com/geb/issues/issues/421">#421</a>]</p>
</li>
<li>
<p>Ensure <code>ConfigSlurper.parse(Script, URL)</code> is called in a thread safe way from <code>ConfigurationLoader</code>. [<a href="https://github.com/geb/issues/issues/423">#423</a>]</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-13-0"><a class="link" href="#0-13-0">0.13.0</a></h4>
<div class="sect4">
<h5 id="new-features-2"><a class="link" href="#new-features-2">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p><code>reportOnTestFailureOnly</code> config option is now also effective in Spock and JUnit4 integrations. [<a href="https://github.com/geb/issues/issues/92">#92</a>]</p>
</li>
<li>
<p><code>isFocused()</code> method has been added to the <code>Navigator</code> class. [<a href="https://github.com/geb/issues/issues/208">#208</a>]</p>
</li>
<li>
<p><code>InvalidPageContent</code> exception is thrown when a content is defined with a name that will result in that content being not accessible. [<a href="https://github.com/geb/issues/issues/109">#109</a>] [<a href="https://github.com/geb/issues/issues/122">#122</a>]</p>
</li>
<li>
<p>Ability to specify proxy location and credentials to use with BrowserStack tunnel. [<a href="https://github.com/geb/issues/issues/419">#419</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="fixes-6"><a class="link" href="#fixes-6">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Fix a bug that caused reports for all but the last executed test class in TestNG integration to be wiped out. [<a href="https://github.com/geb/issues/issues/407">#407</a>]</p>
</li>
<li>
<p>Fix a bug preventing using module as a base of another module. [<a href="https://github.com/geb/issues/issues/411">#411</a>]</p>
</li>
<li>
<p>Restore <code>browser</code> property of <code>Module</code>. [<a href="https://github.com/geb/issues/issues/416">#416</a>]</p>
</li>
<li>
<p>Handle setting values of form elements that cause page change or reload when their value changes. [<a href="https://github.com/geb/issues/issues/155">#155</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="improvements-4"><a class="link" href="#improvements-4">Improvements</a></h5>
<div class="ulist">
<ul>
<li>
<p>Non-ASCII word characters are not longer replaced in report file names. [<a href="https://github.com/geb/issues/issues/399">#399</a>]</p>
</li>
<li>
<p>Change TestNG support to be based on traits. [<a href="https://github.com/geb/issues/issues/412">#412</a>]</p>
</li>
<li>
<p>Add <code>Navigator.moduleList()</code> methods as an alternative to the deprecated <code>moduleList()</code> methods available in the content DSL. [<a href="https://github.com/geb/issues/issues/402">#402</a>]</p>
</li>
<li>
<p>Add support for using Geb with Selendroid and other Selenium based frameworks for testing non-web applications. [<a href="https://github.com/geb/issues/issues/320">#320</a>]</p>
</li>
<li>
<p>Improve documentation for <code>Browser.clearCookies()</code> around what exactly is cleared, add a helper method for removing cookies across multiple domains. [<a href="https://github.com/geb/issues/issues/159">#159</a>]</p>
</li>
<li>
<p>Don&#8217;t depend on UndefinedAtCheckerException for flow control. [<a href="https://github.com/geb/issues/issues/368">#368</a>]</p>
</li>
<li>
<p>Document that <code>Navigator.text()</code> returns the text of the element only if it&#8217;s visible. [<a href="https://github.com/geb/issues/issues/403">#403</a>]</p>
</li>
<li>
<p>Make implementation of <code>interact()</code> less dynamic. [<a href="https://github.com/geb/issues/issues/190">#190</a>]</p>
</li>
<li>
<p>Improve documentation for <code>interact()</code>. [<a href="https://github.com/geb/issues/issues/207">#207</a>]</p>
</li>
<li>
<p>Don&#8217;t unnecessarily request tag name and type attribute multiple times when setting text input values. [<a href="https://github.com/geb/issues/issues/417">#417</a>]</p>
</li>
<li>
<p>Improve usefulness of string representation of content elements. [<a href="https://github.com/geb/issues/issues/274">#274</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="deprecations-2"><a class="link" href="#deprecations-2">Deprecations</a></h5>
<div class="ulist">
<ul>
<li>
<p><code>geb.testng.GebTest</code> and <code>geb.testng.GebReportingTest</code> have been deprecated in favour of <code>geb.testng.GebTestTrait</code> and <code>geb.testng.GebReportingTestTrait</code> respectively.</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="breaking-changes-3"><a class="link" href="#breaking-changes-3">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Geb is now built with Groovy 2.4.5 and Spock 1.0-groovy-2.4.</p>
</li>
<li>
<p>The following <code>Navigator</code> methods now throw <code>SingleElementNavigatorOnlyMethodException</code> when called on a multi element <code>Navigator</code>: <code>hasClass(java.lang.String)</code>, <code>is(java.lang.String)</code>,
<code>isDisplayed()</code>, <code>isDisabled()</code>, <code>isEnabled()</code>, <code>isReadOnly()</code>, <code>isEditable()</code>, <code>tag()</code>, <code>text()</code>, <code>getAttribute(java.lang.String)</code>, <code>attr(java.lang.String)</code>, <code>classes()</code>, <code>value()</code>, <code>click()</code>,
<code>getHeight()</code>, <code>getWidth()</code>, <code>getX()</code>, <code>getY()</code>, <code>css(java.lang.String)</code>, <code>isFocused()</code>. [<a href="https://github.com/geb/issues/issues/284">#284</a>]</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-12-2"><a class="link" href="#0-12-2">0.12.2</a></h4>
<div class="sect4">
<h5 id="fixes-7"><a class="link" href="#fixes-7">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Fix incorrect delegation in variant of <code>waitFor()</code> that takes timeout and interval. [<a href="https://github.com/geb/issues/issues/395">#395</a>]</p>
</li>
<li>
<p>Fix NPE on implicitly asserted statements that contain a safely navigated method call on null target. [<a href="https://github.com/geb/issues/issues/398">#398</a>]</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-12-1"><a class="link" href="#0-12-1">0.12.1</a></h4>
<div class="sect4">
<h5 id="fixes-8"><a class="link" href="#fixes-8">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Change implementation of <code>waitFor()</code> method delegation so that IntelliJ does not complain that page and module classes supposedly need to implement it. [<a href="https://github.com/geb/issues/issues/391">#391</a>]</p>
</li>
<li>
<p>Properly handle class attribute when it&#8217;s passed to <code>$()</code> together with a css selector. [<a href="https://github.com/geb/issues/issues/390">#390</a>]</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-12-0"><a class="link" href="#0-12-0">0.12.0</a></h4>
<div class="sect4">
<h5 id="new-features-3"><a class="link" href="#new-features-3">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p>Support for finding elements using Webdriver&#8217;s <code><a href="http://static.javadoc.io/org.seleniumhq.selenium/selenium-api/3.6.0/org/openqa/selenium/By.html">By</a></code> selectors. [<a href="https://github.com/geb/issues/issues/348">#348</a>]</p>
</li>
<li>
<p>Support for navigating to page instances in addition to classes. [<a href="https://github.com/geb/issues/issues/310">#310</a>]</p>
</li>
<li>
<p>Support for using page instances as <code>page</code> option value of window switching methods. [<a href="https://github.com/geb/issues/issues/352">#352</a>]</p>
</li>
<li>
<p>Support for using page instances together with frame switching methods. [<a href="https://github.com/geb/issues/issues/354">#354</a>]</p>
</li>
<li>
<p>Support for using page instances with <code>Navigator.click()</code> methods. [<a href="https://github.com/geb/issues/issues/355">#355</a>]</p>
</li>
<li>
<p>Support for using page instances and lists of page instances as <code>page</code> option value of content templates. [<a href="https://github.com/geb/issues/issues/356">#356</a>]</p>
</li>
<li>
<p>New <code>Navigator.module(Class&lt;? extends Module&gt;)</code> and <code>Navigable.module(Class&lt;? extends Module&gt;)</code>. [<a href="https://github.com/geb/issues/issues/312">#312</a>]</p>
</li>
<li>
<p>New <code>Navigable.module(Module)</code> and <code>Navigable.module(Module)</code>. [<a href="https://github.com/geb/issues/issues/311">#311</a>]</p>
</li>
<li>
<p>Support for using <code>interact {}</code> blocks in modules. [<a href="https://github.com/geb/issues/issues/364">#364</a>]</p>
</li>
<li>
<p>Support page level <code>atCheckWaiting</code> configuration. [<a href="https://github.com/geb/issues/issues/287">#287</a>]</p>
</li>
<li>
<p><code>Navigator</code> elements can now also be filtered using <code>hasNot()</code> method. [<a href="https://github.com/geb/issues/issues/370">#370</a>]</p>
</li>
<li>
<p>Custom implementation of <code>equals()</code> and <code>hashCode()</code> methods have been added to classes implementing <code>Navigator</code>. [<a href="https://github.com/geb/issues/issues/374">#374</a>]</p>
</li>
<li>
<p>Support setting <code>forcelocal</code> flag for BrowserStack tunnel. [<a href="https://github.com/geb/issues/issues/385">#385</a>]</p>
</li>
<li>
<p>Add configuration option for including cause string representation in message of <code>WaitTimeoutException</code>. [<a href="https://github.com/geb/issues/issues/386">#386</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="improvements-5"><a class="link" href="#improvements-5">Improvements</a></h5>
<div class="ulist">
<ul>
<li>
<p>Using unrecognized content template parameters result in an <code>InvalidPageContent</code> exception to make catching typos in them easier. [<a href="https://github.com/geb/issues/issues/377">#377</a>]</p>
</li>
<li>
<p>Improve error reporting when no at checkers pass if using multiple candidates for page switching. [<a href="https://github.com/geb/issues/issues/346">#346</a>]</p>
</li>
<li>
<p>Don&#8217;t unnecessarily lookup root element for every baseless element lookup using <code>$()</code> in context of <code>Navigable</code>. [<a href="https://github.com/geb/issues/issues/306">#306</a>]</p>
</li>
<li>
<p>Attribute based searches are compiled to CSS selectors where possible. [<a href="https://github.com/geb/issues/issues/280">#280</a>]</p>
</li>
<li>
<p>Attribute based searches using an id, class or name are performed using an appropriate <code>By</code> selector where possible. [<a href="https://github.com/geb/issues/issues/333">#333</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="fixes-9"><a class="link" href="#fixes-9">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Improved message thrown from Navigator.isDisabled() and Navigator.isReadOnly() when navigator does not contain a form element. [<a href="https://github.com/geb/issues/issues/345">#345</a>]</p>
</li>
<li>
<p>Browser.verifyAtIfPresent() should fail for at checkers returning false when implicit assertions are disabled. [<a href="https://github.com/geb/issues/issues/357">#357</a>]</p>
</li>
<li>
<p>Provide better error reporting when unexpected pages configuration is not a collection that contains classes which extend <code>Page</code>. [<a href="https://github.com/geb/issues/issues/270">#270</a>]</p>
</li>
<li>
<p>Don&#8217;t fail when creating a report and driver&#8217;s screenshot taking method returns null. [<a href="https://github.com/geb/issues/issues/292">#292</a>]</p>
</li>
<li>
<p>Classes that can define content should not throw custom exceptions from <code>propertyMissing()</code>. [<a href="https://github.com/geb/issues/issues/367">#367</a>]</p>
</li>
<li>
<p>“At checkers” of pages passed to <code>withFrame()</code> methods are now verified. [<a href="https://github.com/geb/issues/issues/358">#358</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="breaking-changes-4"><a class="link" href="#breaking-changes-4">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p><code>Page.toString()</code> now returns full page class name instead of its simple name.</p>
</li>
<li>
<p><code>MissingPropertyException</code> is thrown instead of <code>UnresolvablePropertyException</code> when content with a given name is not found on page or module.</p>
</li>
<li>
<p>Geb is now built with Groovy 2.3.10 and Spock 1.0-groovy-2.3.</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="deprecations-3"><a class="link" href="#deprecations-3">Deprecations</a></h5>
<div class="ulist">
<ul>
<li>
<p><code>module(Class&lt;? extends Module&gt;, Navigator base)</code> available in content DSL has been deprecated in favour of <code>Navigator.module(Class&lt;? extends Module&gt;)</code> and will be removed in a future
version of Geb.</p>
</li>
<li>
<p><code>module(Class&lt;? extends Module&gt;, Map args)</code> available in content DSL has been deprecated in favour of <code>Navigable.module(Module)</code> and will be removed in a future version of Geb.</p>
</li>
<li>
<p><code>module(Class&lt;? extends Module&gt;, Navigator base, Map args)</code> available in content DSL has been deprecated in favour of <code>Navigator.module(Module)</code> and will be removed in a future version of Geb.</p>
</li>
<li>
<p>all variants of <code>moduleList()</code> method available in content DSL have been deprecated in favour of using <code>Navigator.module()</code> methods together with a <code>collect()</code> call and will be removed in a future
version of Geb, see <a href="#repeating-content">chapter on using modules for repeating content</a> for examples [<a href="https://github.com/geb/issues/issues/362">#362</a>]</p>
</li>
<li>
<p><code>isDisabled()</code>, <code>isEnabled()</code>, <code>isReadOnly()</code> and <code>isEditable()</code> methods of <code>Navigator</code> have been deprecated and will be removed in a future version of Geb. These methods are now available on the
new <a href="#form-element"><code>FormElement</code></a> module class.</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="project-related-changes"><a class="link" href="#project-related-changes">Project related changes</a></h5>
<div class="ulist">
<ul>
<li>
<p>User mailing list has moved to <a href="https://groups.google.com/d/forum/geb-user">Google Groups</a>.</p>
</li>
<li>
<p>The Book of Geb has been migrated to Asciidoctor and the examples have been made executable. [<a href="https://github.com/geb/issues/issues/350">#350</a>]</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-10-0"><a class="link" href="#0-10-0">0.10.0</a></h4>
<div class="sect4">
<h5 id="new-features-4"><a class="link" href="#new-features-4">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p>New <code>css()</code> method on <code>Navigator</code> that allows to access CSS properties of elements. [<a href="https://github.com/geb/issues/issues/141">#141</a>]</p>
</li>
<li>
<p>Added attribute based methods to relative content navigators such as next(), children() etc. [<a href="https://github.com/geb/issues/issues/299">#299</a>]</p>
</li>
<li>
<p>Added signature that accepts <code>localIdentifier</code> to <code>BrowserStackDriverFactory.create</code>. [<a href="https://github.com/geb/issues/issues/332">#332</a>]</p>
</li>
<li>
<p>Added <a href="#content-dsl-to-wait"><code>toWait</code></a> content definition option which allows specifying that page transition happens asynchronously. [<a href="https://github.com/geb/issues/issues/134">#134</a>]</p>
</li>
<li>
<p>Added support for explicitly specifying browser capabilities when using cloud browsers Gradle plugins. [<a href="https://github.com/geb/issues/issues/340">#340</a>]</p>
</li>
<li>
<p>Added an overloaded <code>create()</code> method on cloud driver factories that allow specifying browser capabilities in a map and don&#8217;t require a string capabilities specification. [<a href="https://github.com/geb/issues/issues/281">#281</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="fixes-10"><a class="link" href="#fixes-10">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Allow access to module properties from its content block. [<a href="https://github.com/geb/issues/issues/245">#245</a>]</p>
</li>
<li>
<p>Support setting of elements for WebDriver implementations that return uppercase tag name. [<a href="https://github.com/geb/issues/issues/318">#318</a>]</p>
</li>
<li>
<p>Use native binaries for running BrowserStack tunnel. [<a href="https://github.com/geb/issues/issues/326">#326</a>]</p>
</li>
<li>
<p>Update BrowserStack support to use command-line arguments introduced in tunnel version 3.1. [<a href="https://github.com/geb/issues/issues/332">#332</a>]</p>
</li>
<li>
<p>Fix PermGen memory leak when using groovy script backed configuration. [<a href="https://github.com/geb/issues/issues/335">#335</a>]</p>
</li>
<li>
<p>Don&#8217;t fail in <code>Browser.isAt()</code> if at check waiting is enabled and it times out. [<a href="https://github.com/geb/issues/issues/337">#337</a>]</p>
</li>
<li>
<p>The value passed to <code>aliases</code> content option in documentation examples should be a String [<a href="https://github.com/geb/issues/issues/338">#338</a>]</p>
</li>
<li>
<p>Added <code>$()</code> method on Navigator with all signatures of <code>find()</code>. [<a href="https://github.com/geb/issues/issues/321">#321</a>]</p>
</li>
<li>
<p><code>geb-saucelabs</code> plugin now uses a native version of SauceConnect. [<a href="https://github.com/geb/issues/issues/341">#341</a>]</p>
</li>
<li>
<p>Don&#8217;t modify the predicate map passed to <a href="api/geb/navigator/BasicLocator.html#find(Map%3CString,%20Object%3E,%20java.lang.String)">&#8220;Navigator.find(Map&lt;String</a>. [<a href="https://github.com/geb/issues/issues/339">#339</a>]</p>
</li>
<li>
<p>Functional tests implemented using JUnit and Geb run twice in Grails 2.3+. [<a href="https://github.com/geb/issues/issues/314">#314</a>]</p>
</li>
<li>
<p>Mention in the manual where snapshot artifacts can be downloaded from. [<a href="https://github.com/geb/issues/issues/305">#305</a>]</p>
</li>
<li>
<p>Document that <code>withNewWindow()</code> and <code>withWindow()</code> switch page back to the original one. [<a href="https://github.com/geb/issues/issues/279">#279</a>]</p>
</li>
<li>
<p>Fix selectors in documentation for manipulating checkboxes. [<a href="https://github.com/geb/issues/issues/268">#268</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="project-related-changes-2"><a class="link" href="#project-related-changes-2">Project related changes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Updated cucumber integration example to use <code>cucumber-jvm</code> instead of the now defunct <code>cuke4duke</code>. [<a href="https://github.com/geb/issues/issues/324">#324</a>]</p>
</li>
<li>
<p>Setup CI for all of the example projects. [<a href="https://github.com/geb/issues/issues/188">#188</a>]</p>
</li>
<li>
<p>Incorporate the example projects into the main build. [<a href="https://github.com/geb/issues/issues/189">#189</a>]</p>
</li>
<li>
<p>Add a test crawling the site in search for broken links. [<a href="https://github.com/geb/issues/issues/327">#327</a>]</p>
</li>
<li>
<p>Document the <a href="https://github.com/geb/geb/blob/master/RELEASING.md">release process</a>. [<a href="https://github.com/geb/issues/issues/325">#325</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="breaking-changes-5"><a class="link" href="#breaking-changes-5">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Use Groovy 2.3.6 to build Geb. [<a href="https://github.com/geb/issues/issues/330">#330</a>]</p>
</li>
<li>
<p>Format of browser specification passed to <code>BrowserStackBrowserFactory.create()</code> and <code>SauceLabsBrowserFactory.create()</code> has changed to be a string in Java properties file format defining the required browser capabilities.</p>
</li>
<li>
<p><code>sauceConnect</code> configuration used with <code>geb-saucelabs</code> plugin should now point at a version of 'ci-sauce' artifact from 'com.saucelabs' group.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-9-3"><a class="link" href="#0-9-3">0.9.3</a></h4>
<div class="sect4">
<h5 id="new-features-5"><a class="link" href="#new-features-5">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p>Added <code>baseNavigatorWaiting</code> setting to prevent intermittent Firefox driver errors when creating base navigator. [<a href="https://github.com/geb/issues/issues/269">#269</a>]</p>
</li>
<li>
<p>Page content classes including <code>Module</code> now implement <code>Navigator</code> interface [<a href="https://github.com/geb/issues/issues/181">#181</a>]</p>
</li>
<li>
<p>Added some tests that guard performance by verifying which WebDriver commands are executed [<a href="https://github.com/geb/issues/issues/302">#302</a>]</p>
</li>
<li>
<p>Added <a href="http://www.browserstack.com">BrowserStack</a> integration [<a href="https://github.com/geb/issues/issues/307">#307</a>]</p>
</li>
<li>
<p>Added a shortcut to <code>Browser</code> for getting current url [<a href="https://github.com/geb/issues/issues/294">#294</a>]</p>
</li>
<li>
<p>Verify pages at checker when passed as an option to open a new window via <code>withWindow()</code> and <code>withNewWindow()</code> [<a href="https://github.com/geb/issues/issues/278">#278</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="fixes-11"><a class="link" href="#fixes-11">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Ignore <code>atCheckWaiting</code> setting when checking for unexpected pages. [<a href="https://github.com/geb/issues/issues/267">#267</a>]</p>
</li>
<li>
<p>Added missing range variants of find/$ methods. [<a href="https://github.com/geb/issues/issues/283">#283</a>]</p>
</li>
<li>
<p>Migrated <code>UnableToLoadException</code> to java. [<a href="https://github.com/geb/issues/issues/263">#263</a>]</p>
</li>
<li>
<p>Exception thrown when trying to set value on an invalid element (non form control). [<a href="https://github.com/geb/issues/issues/286">#286</a>]</p>
</li>
<li>
<p>Support for jQuery methods like offset() and position() which return a native Javascript object. [<a href="https://github.com/geb/issues/issues/271">#271</a>]</p>
</li>
<li>
<p>Finding elements when passing ids with spaces in the predicates map to the $() method. [<a href="https://github.com/geb/issues/issues/308">#308</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="breaking-changes-6"><a class="link" href="#breaking-changes-6">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Removed easyb support. [<a href="https://github.com/geb/issues/issues/277">#277</a>]</p>
</li>
<li>
<p><code>MissingMethodException</code> is now thrown when using shortcut for obtaining a navigator based on a control name and the returned navigator is empty. [<a href="https://github.com/geb/issues/issues/239">#239</a>]</p>
</li>
<li>
<p>When using SauceLabs integration, the <code>allSauceTests</code> task was renamed to`allSauceLabsTests`</p>
</li>
<li>
<p>When using SauceLabs integration, the <code>geb.sauce.browser</code> system property was renamed to <code>geb.saucelabs.browser</code></p>
</li>
<li>
<p><code>Module</code> now implements <code>Navigator</code> instead of <code>Navigable</code> so <code>Navigator&#8217;s methods can be called on it without having to first call `$()</code> to get the module&#8217;s base <code>Navigator</code>.</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="project-related-changes-3"><a class="link" href="#project-related-changes-3">Project related changes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Documentation site has been migrated to <a href="http://ratpack.io">Ratpack</a>. [<a href="https://github.com/geb/issues/issues/261">#261</a>]</p>
</li>
<li>
<p>Cross browser tests are now also executed using Safari driver [<a href="https://github.com/geb/issues/issues/276">#276</a>]</p>
</li>
<li>
<p>Artifact snapshots are uploaded and gebish.org is updated after every successful build in CI [<a href="https://github.com/geb/issues/issues/295">#295</a>]</p>
</li>
<li>
<p>Added a <a href="https://travis-ci.org/geb/geb">Travis CI build</a> that runs tests on submitted pull requests [<a href="https://github.com/geb/issues/issues/309">#309</a>]</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-9-2"><a class="link" href="#0-9-2">0.9.2</a></h4>
<div class="sect4">
<h5 id="new-features-6"><a class="link" href="#new-features-6">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p><code>page</code> and <code>close</code> options can be passed to <code>withWindow()</code> calls, see <a href="#already-opened-windows">this manual section</a> for more information.</p>
</li>
<li>
<p>Unexpected pages can be specified to fail fast when performing “at” checks. This feature was contributed at a Hackergarten thanks to Andy Duncan. See <a href="#unexpected-pages">this manual section</a> for details. [<a href="https://github.com/geb/issues/issues/70">#70</a>]</p>
</li>
<li>
<p>Support for running Geb using SauceLabs provided browsers, see <a href="#cloud-browser-testing">this manual section</a> for details.</p>
</li>
<li>
<p>New <code>isEnabled()</code> and <code>isEditable()</code> methods on <code>Navigator</code>.</p>
</li>
<li>
<p>Support for ephemeral port allocation with Grails integration</p>
</li>
<li>
<p>Compatibility with Grails 2.3</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="fixes-12"><a class="link" href="#fixes-12">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Default value of <code>close</code> option for <code>withNewWindow()</code> is set to <code>true</code> as specified in the documentation. [<a href="https://github.com/geb/issues/issues/258">#258</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="breaking-changes-7"><a class="link" href="#breaking-changes-7">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p><code>isDisabled()</code> now throws <code>UnsupportedOperationException</code> if called on an <code>EmptyNavigator</code> or on a <code>Navigator</code> that contains anything else than a button, input, option, select or textarea.</p>
</li>
<li>
<p><code>isReadOnly()</code> now throws <code>UnsupportedOperationException</code> if called on an <code>EmptyNavigator</code> or on a <code>Navigator</code> that contains anything else than an input or a textarea.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-9-1"><a class="link" href="#0-9-1">0.9.1</a></h4>
<div class="sect4">
<h5 id="breaking-changes-8"><a class="link" href="#breaking-changes-8">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Explicitly calling <code>at()</code> with a page object will throw <code>UndefinedAtCheckerException</code> instead of silently passing if the page object does not define an at checker.</p>
</li>
<li>
<p>Passing a page with no at checker to <code>click(List&lt;Class&lt;? extends Page&gt;&gt;)</code> or as one of the pages in <code>to</code> template option will throw <code>UndefinedAtCheckerException</code>.</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="new-features-7"><a class="link" href="#new-features-7">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p>Support for dealing with self-signed certificates in Download API using <code>SelfSignedCertificateHelper</code>. [<a href="https://github.com/geb/issues/issues/150">#150</a>]</p>
</li>
<li>
<p>Connections created when using Download API can be configured globally using <code>defaultDownloadConfig</code> configuration option.</p>
</li>
<li>
<p>New <code>atCheckWaiting</code> configuration option allowing to implictly wrap “at” checkers in <code>waitFor</code> calls. [<a href="https://github.com/geb/issues/issues/253">#253</a>]</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="fixes-13"><a class="link" href="#fixes-13">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p><code>containsWord()</code> and <code>iContainsWord()</code> now return expected results when matching against text that contains spaces [<a href="https://github.com/geb/issues/issues/254">#254</a>]</p>
</li>
<li>
<p><code>has(Map&lt;String, Object&gt; predicates, String selector)</code> and <code>has(Map&lt;String, Object&gt; predicates)</code> were added to Navigator for consistency with <code>find()</code> and <code>filter()</code> [<a href="https://github.com/geb/issues/issues/256">#256</a>]</p>
</li>
<li>
<p>Also catch WaitTimeoutException when page verification has failed following a <code>click()</code> call [<a href="https://github.com/geb/issues/issues/255">#255</a>]</p>
</li>
<li>
<p><code>not(Map&lt;String, Object&gt; predicates, String selector)</code> and <code>not(Map&lt;String, Object&gt; predicates)</code> were added to Navigator for consistency with <code>find()</code> and <code>filter()</code> [<a href="https://github.com/geb/issues/issues/257">#257</a>]</p>
</li>
<li>
<p>Make sure that <code>NullPointerException</code> is not thrown for incorrect driver implementations of getting current url without previously driving the browser to a url [<a href="https://github.com/geb/issues/issues/291">#291</a>]</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-9-0"><a class="link" href="#0-9-0">0.9.0</a></h4>
<div class="sect4">
<h5 id="new-features-8"><a class="link" href="#new-features-8">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p>New <code>via()</code> method that behaves the same way as <code>to()</code> behaved previously - it sets the page on the browser and does not verify the at checker of that page[<a href="https://github.com/geb/issues/issues/249">#249</a>].</p>
</li>
<li>
<p>It is now possible to provide your own <code><a href="api/geb/navigator/Navigator.html">Navigator</a></code> implementations by specifying a custom <a href="api/geb/navigator/factory/NavigatorFactory.html"><code>NavigatorFactory</code></a>, see <a href="#navigator-factory">this manual section</a> for more information [<a href="https://github.com/geb/issues/issues/96">#96</a>].</p>
</li>
<li>
<p>New variants of <code>withFrame()</code> method that allow to switch into frame context and change the page in one go and also automatically change it back to the original page after the call, see [switching pages and frames at once][switch-frame-and-page] in the manual [<a href="https://github.com/geb/issues/issues/213">#213</a>].</p>
</li>
<li>
<p><code>wait</code>, <code>page</code> and <code>close</code> options can be passed to <code>withNewWindow()</code> calls, see <a href="#newly-opened-windows">this manual section</a> for more information [<a href="https://github.com/geb/issues/issues/167">#167</a>].</p>
</li>
<li>
<p>Improved message of UnresolvablePropertyException to include a hint about forgetting to import the class [<a href="https://github.com/geb/issues/issues/240">#240</a>].</p>
</li>
<li>
<p>Improved signature of <code>Browser.at()</code> and <code>Browser.to()</code> to return the exact type of the page that was asserted to be at / was navigated to.</p>
</li>
<li>
<p><a href="api/geb/report/ReportingListener.html"><code>ReportingListener</code></a> objects can be registered to observe reporting (see: <a href="#listening-to-reporting">this manual section</a>)</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="fixes-14"><a class="link" href="#fixes-14">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Fixed an issue where waitFor would throw a WaitTimeoutException even if the last evaluation before timeout returned a truthy value [<a href="https://github.com/geb/issues/issues/215">#215</a>].</p>
</li>
<li>
<p>Fixed taking screenshots for reporting when the browser is not on a HTML page (e.g. XML file) [<a href="https://github.com/geb/issues/issues/126">#126</a>].</p>
</li>
<li>
<p>Return the last evaluation value for a <code>(wait: true, required: false)</code> content instead of always returning null [<a href="https://github.com/geb/issues/issues/216">#216</a>].</p>
</li>
<li>
<p><code>isAt()</code> behaves the same as <code>at()</code> in regards to updating the browser&#8217;s page instance to the given page type if its at checker is successful [<a href="https://github.com/geb/issues/issues/227">#227</a>].</p>
</li>
<li>
<p>Handling of <code>select</code> elements has been reworked to be far more efficient [<a href="https://github.com/geb/issues/issues/229">#229</a>].</p>
</li>
<li>
<p>Modules support accessing base attributes' values using @attributeName notation [<a href="https://github.com/geb/issues/issues/237">#237</a>].</p>
</li>
<li>
<p>Use of text matchers in module base definitions is supported [<a href="https://github.com/geb/issues/issues/241">#241</a>].</p>
</li>
<li>
<p>Reading of textareas have been updated so that the current value of the text field is returned, instead of the initial [<a href="https://github.com/geb/issues/issues/174">#174</a>].</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="breaking-changes-9"><a class="link" href="#breaking-changes-9">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p><code>to(Class&lt;? extends Page&gt;)</code> method now changes the page on the browser and verifies the at checker of that page in one method call [<a href="https://github.com/geb/issues/issues/1">#1</a>], [<a href="https://github.com/geb/issues/issues/249">#249</a>]; use <code>via()</code> if you need the old behaviour</p>
</li>
<li>
<p><code>getAttribute(String)</code> on <code>Navigator</code> now returns <code>null</code> for boolean attributes that are not present.</p>
</li>
<li>
<p><code>at()</code> and <code>to()</code> methods on <code>Browser</code> now return a page instance if they succeed and <code>via()</code> method always returns a page instance [<a href="https://github.com/geb/issues/issues/217">#217</a>].</p>
</li>
<li>
<p><code>withFrame()</code> calls that don&#8217;t take a page argument now change the browser page to what it was before the call, after the call [<a href="https://github.com/geb/issues/issues/222">#222</a>].</p>
</li>
<li>
<p>due to performance improvements duplicate elements are not removed when creating new <code>Navigator`s anymore; the new `unique()</code> method on <code>Navigator</code> can be used to remove duplicates if needed [<a href="https://github.com/geb/issues/issues/223">#223</a>].</p>
</li>
<li>
<p><code>at(Page)</code> and <code>isAt(Page)</code> methods on <code>Browser</code> have been removed as they were inconsistent with the rest of the API [<a href="https://github.com/geb/issues/issues/242">#242</a>].</p>
</li>
<li>
<p>Navigator&#8217;s <code>click(Class&lt;? extends Page&gt;)</code> and <code>to:</code> content option now verify page after switching to the new one to stay consistent with the new behaviour of <code>to(Class&lt;? extends Page&gt;)</code> [<a href="https://github.com/geb/issues/issues/250">#250</a>].</p>
</li>
<li>
<p>Reading an attribute that is not set on a navigator now returns an empty string across all drivers [<a href="https://github.com/geb/issues/issues/251">#251</a>].</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-7-2"><a class="link" href="#0-7-2">0.7.2</a></h4>
<div class="sect4">
<h5 id="fixes-15"><a class="link" href="#fixes-15">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Further fixes for Java 7 [<a href="https://github.com/geb/issues/issues/211">#211</a>].</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-7-1"><a class="link" href="#0-7-1">0.7.1</a></h4>
<div class="sect4">
<h5 id="new-features-9"><a class="link" href="#new-features-9">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p>Geb is now built with Groovy 1.8.6. This was forced to resolve [<a href="https://github.com/geb/issues/issues/194">#194</a>].</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="fixes-16"><a class="link" href="#fixes-16">Fixes</a></h5>
<div class="ulist">
<ul>
<li>
<p><code>startsWith()</code>, <code>contains()</code> etc. now work for selecting via element text now works for multiline (i.e. <code>&lt;br/&gt;</code>) text [<a href="https://github.com/geb/issues/issues/202">#202</a>]</p>
</li>
<li>
<p>Geb now works with Java 7 [<a href="https://github.com/geb/issues/issues/194">#194</a>].</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-7-0"><a class="link" href="#0-7-0">0.7.0</a></h4>
<div class="sect4">
<h5 id="new-features-10"><a class="link" href="#new-features-10">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p>Added support for indexes and ranges in <code>moduleList()</code> method</p>
</li>
<li>
<p>Form control shortcuts now also work on page and module content</p>
</li>
<li>
<p>Custom timeout message for <code>waitFor()</code> calls</p>
</li>
<li>
<p>Navigators can be composed also from content</p>
</li>
<li>
<p>Closure expressions passed to <code>waitFor()</code> calls are now transformed so that every statement in them is asserted - this provides better reporting on <code>waitFor()</code> timeouts.</p>
</li>
<li>
<p><code>at</code> closure properties of Page classes are now transformed so that every statement in them is asserted - this provides better reporting on failed at checks</p>
</li>
<li>
<p>new <code>isAt()</code> method on Browser that behaves like <code>at()</code> used to behave before, i.e. does not throw AssertionError but returns <code>false</code> if at checking fails</p>
</li>
<li>
<p><code>withAlert()</code> and <code>withConfirm()</code> now accept a <code>wait</code> option and the possible values are the same as for waiting content</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="breaking-changes-10"><a class="link" href="#breaking-changes-10">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p><code>click()</code> now instructs the browser to click <strong>only on the first</strong> element the navigator has matched</p>
</li>
<li>
<p>All <code>click()</code> method variants return the reciever</p>
</li>
<li>
<p>Content definitions with <code>required: false, wait: true</code> return <code>null</code> and do not throw <code>WaitTimeoutException</code> if the timeout expires</p>
</li>
<li>
<p>Assignment statements are not allowed anymore in closure expressions passed to <code>waitFor()</code> calls</p>
</li>
<li>
<p><code>at()</code> now throws AssertionException if at checking fails instead of returning false</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-6-3"><a class="link" href="#0-6-3">0.6.3</a></h4>
<div class="sect4">
<h5 id="new-features-11"><a class="link" href="#new-features-11">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p>Compatibility with Spock 0.6</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-6-2"><a class="link" href="#0-6-2">0.6.2</a></h4>
<div class="sect4">
<h5 id="new-features-12"><a class="link" href="#new-features-12">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p>New <code>interact()</code> function for mouse and keyboard actions which delegates to the WebDriver Actions class</p>
</li>
<li>
<p>New <code>moduleList()</code> function for repeating content</p>
</li>
<li>
<p>New <code>withFrame()</code> method for working with frames</p>
</li>
<li>
<p>New <code>withWindow()</code> and <code>withNewWindow()</code> methods for working with multiple windows</p>
</li>
<li>
<p>Added <code>getCurrentWindow()</code> and <code>getAvailableWindows()</code> methods to browser that delegate to the underlying driver instance</p>
</li>
<li>
<p>Content aliasing is now possible using <code>aliases</code> parameter in content DSL</p>
</li>
<li>
<p>If config script is not found a config class will be used if there is any - this is usefull if you run test using Geb from IDE</p>
</li>
<li>
<p>Drivers are now cached across the whole JVM, which avoids the browser startup cost in some situations</p>
</li>
<li>
<p>Added config option to disable quitting of cached browsers on JVM shutdown</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="breaking-changes-11"><a class="link" href="#breaking-changes-11">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p>The <code>Page.convertToPath()</code> function is now responsible for adding a prefix slash if required (i.e. it&#8217;s not added implicitly in <code>Page.getPageUrl()</code>) [GEB-139].</p>
</li>
<li>
<p>Unchecked checkboxes now report their value as <code>false</code> instead of null</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-6-1"><a class="link" href="#0-6-1">0.6.1</a></h4>
<div class="sect4">
<h5 id="new-features-13"><a class="link" href="#new-features-13">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p>Compatibility with at least Selenium 2.9.0 (version 0.6.0 of Geb did not work with Selenium 2.5.0 and up)</p>
</li>
<li>
<p>Attempting to set a select to a value that it does not contain now throws an exception</p>
</li>
<li>
<p>The waiting algorithm is now time based instead of number of retries based, which is better for blocks that are not near instant</p>
</li>
<li>
<p>Better support for working with already instantiated pages</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="breaking-changes-12"><a class="link" href="#breaking-changes-12">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Using <code>&lt;select&gt;</code> elements with Geb now requires an explicit dependency on an extra WebDriver jar (see <a href="#installation-usage">the section on installation for more info</a>)</p>
</li>
<li>
<p>The <code>Navigator</code> <code>classes()</code> method now returns a <code>List</code> (instead of <code>Set</code>) and guarantees that it will be sorted alphabetically</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-6"><a class="link" href="#0-6">0.6</a></h4>
<div class="sect4">
<h5 id="new-features-14"><a class="link" href="#new-features-14">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p>selenium-common is now a 'provided' scoped dependency of Geb</p>
</li>
<li>
<p>Radio buttons can be selected with their label text as well as their value attribute.</p>
</li>
<li>
<p>Select options can be selected with their text as well as their value attribute.</p>
</li>
<li>
<p><code>Navigator.getAttribute</code> returns <code>null</code> rather than the empty string when an attribute is not found.</p>
</li>
<li>
<p>The <code>jquery</code> property on <code>Navigator</code> now returns whatever the jQuery method called on it returns.</p>
</li>
<li>
<p>All waitFor clauses now treat exceptions raised in the condition as an evaluation failure, instead of propagating the exception</p>
</li>
<li>
<p>Content can be defined with <code>wait: true</code> to make Geb implicitly wait for it when it is requested</p>
</li>
<li>
<p>Screenshots are now taken when reporting for all drivers that implement the <code>TakesScreenshot</code> interface (which is nearly all)</p>
</li>
<li>
<p>Added <code>BindingUpdater</code> class that can manage a groovy script binding for use with Geb</p>
</li>
<li>
<p>Added <code>quit()</code> and <code>close()</code> methods to browser that delegate to the underlying driver instance</p>
</li>
<li>
<p><code>geb.Browser.drive()</code> methods now return the used <code>Browser</code> instance</p>
</li>
<li>
<p>The underlying WebElements of a Navigator are now retrievable</p>
</li>
<li>
<p>Added $() methods that take one or more Navigator or WebElement objects and returns a new Navigator composed of these objects</p>
</li>
<li>
<p>Added Direct Download API which can be used for directly downloading content (PDFs, CSVs etc.) into your Geb program (not via the browser)</p>
</li>
<li>
<p>Introduced new configuration mechanism for more flexible and environment sensitive configuration of Geb (e.g. driver implementation, base url)</p>
</li>
<li>
<p>Default wait timeout and retry interval is now configurable, and can now also use user configuration presets (e.g. quick, slow)</p>
</li>
<li>
<p>Added a “build adapter” mechanism, making it easier for build systems to take control of relevant configuration</p>
</li>
<li>
<p>The JUnit 3 integration now includes the test method name in the automatically generated reports</p>
</li>
<li>
<p>The reporting support has been rewritten, making it much friendlier to use outside of testing</p>
</li>
<li>
<p>Added the TestNG support (contributed by Alexander Zolotov)</p>
</li>
<li>
<p>Added the <code>height</code>, <code>width</code>, <code>x</code> and <code>y</code> properties to navigator objects and modules</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="breaking-changes-13"><a class="link" href="#breaking-changes-13">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Raised minimum Groovy version to 1.7</p>
</li>
<li>
<p>All failed waitFor clauses now throw a <code>geb.waiting.WaitTimeoutException</code> instead of <code>AssertionError</code></p>
</li>
<li>
<p>Upgraded minimum version requirement of WebDriver to 2.0rc1</p>
</li>
<li>
<p>The <code>onLoad()</code> and <code>onUnload()</code> page methods both have changed their return types from <code>def</code> to <code>void</code></p>
</li>
<li>
<p>The Grails specific testing subclasses have been REMOVED. Use the direct equivalent instead (e.g <code>geb.spock.GebReportingSpec</code> instead of <code>grails.plugin.geb.GebSpec</code>)</p>
</li>
<li>
<p>The Grails plugin no longer depends on the test integration modules, you need to depend on the one you want manually</p>
</li>
<li>
<p>The <code>getBaseUrl()</code> method from testing subclasses has been removed, use the configuration mechanism</p>
</li>
<li>
<p>Inputs with no value now report their value as an empty string instead of <code>null</code></p>
</li>
<li>
<p>Select elements that are not multiple select enabled no longer report their value as a 1 element list, but now as the value of the selected element (if no selection, <code>null</code> is returned)</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-5-1"><a class="link" href="#0-5-1">0.5.1</a></h4>
<div class="ulist">
<ul>
<li>
<p>Fixed problem with incorrectly compiled specs and the geb grails module</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="0-5"><a class="link" href="#0-5">0.5</a></h4>
<div class="sect4">
<h5 id="new-features-15"><a class="link" href="#new-features-15">New features</a></h5>
<div class="ulist">
<ul>
<li>
<p>Navigator objects now implement the Groovy truth (empty == false, non empty == true)</p>
</li>
<li>
<p>Introduced “js” short notation</p>
</li>
<li>
<p>Added “easyb” support (<code>geb-easyb</code>) and Grails support</p>
</li>
<li>
<p>Page change listening support through <code>geb.PageChangeListener</code></p>
</li>
<li>
<p><code>waitFor()</code> methods added, making dealing with dynamic pages easier</p>
</li>
<li>
<p>Support for <code>alert()</code> and <code>confirm()</code> dialogs</p>
</li>
<li>
<p>Added jQuery integration</p>
</li>
<li>
<p>Reporting integration classes (e.g. GebReportingSpec) now save a screenshot if using the FirefoxDriver</p>
</li>
<li>
<p>Added <code>displayed</code> property to navigator objects for determining visibility</p>
</li>
<li>
<p>Added <code>find</code> as an alias for <code>$</code> (e.g. <code>find(&quot;div.section&quot;)</code>)</p>
</li>
<li>
<p>Browser objects now implement the <code>page(List&lt;Class&gt;)</code> method that sets the page to the first type whose at-checker matches the page</p>
</li>
<li>
<p>The click() methods that take one or more page classes are now available on <code>Navigator</code> objects</p>
</li>
<li>
<p>Added page lifecycle methods <code>onLoad()</code>/<code>onUnload()</code></p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="breaking-changes-14"><a class="link" href="#breaking-changes-14">Breaking changes</a></h5>
<div class="ulist">
<ul>
<li>
<p>Exceptions raised in <code>drive()</code> blocks are no longer wrapped with <code>DriveException</code></p>
</li>
<li>
<p>the <code>at(Class pageClass)</code> method no longer requires the existing page instance to be of that class (page will be updated if the given type matches)</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect3">
<h4 id="0-4"><a class="link" href="#0-4">0.4</a></h4>
<div class="paragraph">
<p><strong>Initial Public Release</strong></p>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Version 2.0<br>
Last updated 2015-06-24 15:54:54 +01:00
</div>
</div>
</body>
</html>