Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve IDE experience for snippets #221 #222

Merged
merged 1 commit into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/manual/manual.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ asciidoctorj {
toc : 'left',
encoding : 'utf-8',
'snippets-dir' : snippetsPath,
'rb-snippets-dir' : snippetsPath,
'cm-snippets-dir' : snippetsPath,
'ct-snippets-dir' : snippetsPath,
numbered : true,
sectlinks : true,
idprefix : '',
Expand Down
28 changes: 16 additions & 12 deletions doc/manual/src/docs/asciidoc/010-intro.adoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
ifndef::snippets-dir[]
:snippets-dir: ../../../../manual-snippets/src/test/groovy
:rb-snippets-dir: ../../../../manual-snippets/real-browser/src/test/groovy
endif::[]
= Introduction

Geb (pronounced "jeb" and originally a contraction of "Groovy web") is a developer focused tool for automating the interaction between web browsers and web content.
Expand All @@ -6,7 +10,7 @@ inspection and traversal API (for finding and interacting with content).

Geb was born out of a desire to make browser automation (originally for web testing) easier and more productive.
It aims to be a *developer tool* in that it allows and encourages the using of programming and language constructs instead of creating a restricted environment.
It uses Groovy's dynamism to remove the noise and boiler plate code in order to focus on what's important — the content and interaction.
It uses Groovy's dynamism to remove the noise and boilerplate code in order to focus on what's important — the content and interaction.

== The browser automation technology

Expand Down Expand Up @@ -87,9 +91,9 @@ Here's an example of using Geb in an inline (i.e. no page objects or predefined

[source,groovy]
----
include::{snippets-dir}/intro/ScriptingSpec.groovy[tags=imports,indent=0]
include::{rb-snippets-dir}/intro/ScriptingSpec.groovy[tags=imports,indent=0]

include::{snippets-dir}/intro/ScriptingSpec.groovy[tags=inline,indent=0]
include::{rb-snippets-dir}/intro/ScriptingSpec.groovy[tags=inline,indent=0]
----
<1> Check that we are at Geb's homepage.
<2> Click on the manual menu entry to open it.
Expand All @@ -103,14 +107,14 @@ This time let us define our content up front using the Page Object pattern…

[source,groovy]
----
include::{snippets-dir}/intro/module/ManualsMenuModule.groovy[tags=imports,indent=0]
include::{snippets-dir}/intro/page/GebHomePage.groovy[tags=imports,indent=0]
include::{rb-snippets-dir}/intro/module/ManualsMenuModule.groovy[tags=imports,indent=0]
include::{rb-snippets-dir}/intro/page/GebHomePage.groovy[tags=imports,indent=0]

include::{snippets-dir}/intro/module/ManualsMenuModule.groovy[tags=class,indent=0]
include::{rb-snippets-dir}/intro/module/ManualsMenuModule.groovy[tags=class,indent=0]

include::{snippets-dir}/intro/page/GebHomePage.groovy[tags=class,indent=0]
include::{rb-snippets-dir}/intro/page/GebHomePage.groovy[tags=class,indent=0]

include::{snippets-dir}/intro/page/TheBookOfGebPage.groovy[tags=class,indent=0]
include::{rb-snippets-dir}/intro/page/TheBookOfGebPage.groovy[tags=class,indent=0]
----
<1> Modules are reusable fragments that can be used across pages. Here we are using a module to model a dropdown menu.
<2> Content DSL.
Expand All @@ -124,9 +128,9 @@ Now our script again, using the above defined content…

[source,groovy]
----
include::{snippets-dir}/intro/ScriptingSpec.groovy[tags=imports,indent=0]
include::{rb-snippets-dir}/intro/ScriptingSpec.groovy[tags=imports,indent=0]

include::{snippets-dir}/intro/ScriptingSpec.groovy[tags=using_page_objects,indent=0]
include::{rb-snippets-dir}/intro/ScriptingSpec.groovy[tags=using_page_objects,indent=0]
----
<1> Go to the url defined by `GebHomePage` and also verify it's “at checker”.

Expand All @@ -140,9 +144,9 @@ Here is our Geb homepage case again, this time using Geb's {spock} integration

[source,groovy]
----
include::{snippets-dir}/intro/GebHomepageSpec.groovy[tags=imports,indent=0]
include::{rb-snippets-dir}/intro/GebHomepageSpec.groovy[tags=imports,indent=0]

include::{snippets-dir}/intro/GebHomepageSpec.groovy[tags=class,indent=0]
include::{rb-snippets-dir}/intro/GebHomepageSpec.groovy[tags=class,indent=0]
----

For more information on using Geb for web and functional testing, see the <<testing,testing chapter>>.
Expand Down
6 changes: 5 additions & 1 deletion doc/manual/src/docs/asciidoc/020-browser.adoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
ifndef::snippets-dir[]
:snippets-dir: ../../../../manual-snippets/src/test/groovy
:rb-snippets-dir: ../../../../manual-snippets/real-browser/src/test/groovy
endif::[]
[[browser]]
= The `Browser`

Expand Down Expand Up @@ -469,7 +473,7 @@ Both of them can be used to read and write values to the underlying storage as i

[source,groovy,indent=0]
----
include::{snippets-dir}/browser/WebStorageSpec.groovy[tag=write_and_read]
include::{rb-snippets-dir}/browser/WebStorageSpec.groovy[tag=write_and_read]
----

Both `localStorage` and `sessionStorage` properties are of `geb.webstorage.WebStorage` type - please refer to link:api/geb/webstorage/WebStorage.html[its javadoc] for information about other operations that are supported apart from reading and writing values.
Expand Down
4 changes: 4 additions & 0 deletions doc/manual/src/docs/asciidoc/021-driver.adoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
ifndef::snippets-dir[]
:snippets-dir: ../../../../manual-snippets/src/test/groovy
:rb-snippets-dir: ../../../../manual-snippets/real-browser/src/test/groovy
endif::[]
[[driver]]
= The WebDriver implementation

Expand Down
49 changes: 27 additions & 22 deletions doc/manual/src/docs/asciidoc/030-navigator.adoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
ifndef::snippets-dir[]
:snippets-dir: ../../../../manual-snippets/src/test/groovy
:rb-snippets-dir: ../../../../manual-snippets/real-browser/src/test/groovy
:cm-snippets-dir: ../../../../../module/geb-core/src/main/groovy
endif::[]
[[navigator]]
= Interacting with content

Expand Down Expand Up @@ -94,14 +99,14 @@ Let's consider a simple link:https://vuejs.org/[Vue.js] application which render

[source,html]
----
include::{snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=template,indent=0]
include::{rb-snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=template,indent=0]
----

…and the following javascript code…

[source,groovy]
----
include::{snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=vue_app,indent=0]
include::{rb-snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=vue_app,indent=0]
----

The important thing to note is that the `li` elements re re-rendered when they are reordered.
Expand All @@ -110,40 +115,40 @@ Let's model each item in the list using the following module…

[source,groovy]
----
include::{snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=module,indent=0]
include::{rb-snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=module,indent=0]
----

Given the following content definition…

[source,groovy]
----
include::{snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=static_navigator,indent=0]
include::{rb-snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=static_navigator,indent=0]
----

The code below will fail with `StaleElementReferenceException`…

[source,groovy]
----
include::{snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=exception,indent=0]
include::{rb-snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=exception,indent=0]
----

If we change the base navigator of the list element module to be dynamic as in the following page class…

[source,groovy]
----
include::{snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=dynamic_navigator,indent=0]
include::{rb-snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=dynamic_navigator,indent=0]
----

Then the following will pass…

[source,groovy]
----
include::{snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=no_exception,indent=0]
include::{rb-snippets-dir}/navigator/DynamicModuleBaseSpec.groovy[tag=no_exception,indent=0]
----

[WARNING]
====
While using dynamic navigators might seem like a way to completely avoid `{stale-element-reference-exception-api}` it is unfortunatelly not the case.
While using dynamic navigators might seem like a way to completely avoid `{stale-element-reference-exception-api}` it is unfortunately not the case.
That's because operations which access elements of a dynamic `Navigator` are not atomic.
For example, when calling `click()` on a dynamic `Navigator`, first a WebDriver protocol command will be executed to refresh the web elements which underpin that instance and then another command will be executed to click on the element that was found - if between the two commands the element is removed from the DOM the exception will still occur.
While dynamic navigators make `StaleElementReferenceException` less likely, it's still far from impossible to get one when using them, especially in highly asynchronous applications with frequent DOM redraws.
Expand Down Expand Up @@ -732,8 +737,8 @@ It is possible to send non-textual characters to content by using the WebDriver

[source,groovy]
----
include::{snippets-dir}/navigator/NonCharacterKeystrokesSpec.groovy[tag=import,indent=0]
include::{snippets-dir}/navigator/NonCharacterKeystrokesSpec.groovy[tag=keystrokes,indent=0]
include::{rb-snippets-dir}/navigator/NonCharacterKeystrokesSpec.groovy[tag=import,indent=0]
include::{rb-snippets-dir}/navigator/NonCharacterKeystrokesSpec.groovy[tag=keystrokes,indent=0]
----

Here we are sending a “control-c” to an input.
Expand Down Expand Up @@ -1013,8 +1018,8 @@ Which an also be used for non-character keys…

[source,groovy]
----
include::{snippets-dir}/navigator/BackspaceSpec.groovy[tag=import,indent=0]
include::{snippets-dir}/navigator/BackspaceSpec.groovy[tag=backspace,indent=0]
include::{rb-snippets-dir}/navigator/BackspaceSpec.groovy[tag=import,indent=0]
include::{rb-snippets-dir}/navigator/BackspaceSpec.groovy[tag=backspace,indent=0]
----

[WARNING]
Expand Down Expand Up @@ -1055,29 +1060,29 @@ It is possible to access the contained `WebElement` instances via the following

[source,groovy]
----
include::{snippets-dir}/geb/navigator/Navigator.groovy[tag=web_element_returning_methods,indent=0]
include::{cm-snippets-dir}/geb/navigator/Navigator.groovy[tag=web_element_returning_methods,indent=0]
----

By using the methods of the WebDriver `{actions-api}` class with `WebElement` instances, complex user gestures can be emulated.
First you will need to create an `Actions` instance after obtaining the WebDriver driver:

[source,groovy]
----
include::{snippets-dir}/navigator/InteractionsSpec.groovy[tag=actions_creation,indent=0]
include::{rb-snippets-dir}/navigator/InteractionsSpec.groovy[tag=actions_creation,indent=0]
----

Next, use methods of `Actions` to compose a series of UI actions, then call `build()` to create a concrete `Action`:

[source,groovy]
----
include::{snippets-dir}/navigator/InteractionsSpec.groovy[tag=action_definition,indent=0]
include::{rb-snippets-dir}/navigator/InteractionsSpec.groovy[tag=action_definition,indent=0]
----

Finally, call `perform()` to actually trigger the desired mouse or keyboard behavior:

[source,groovy]
----
include::{snippets-dir}/navigator/InteractionsSpec.groovy[tag=action_execution,indent=0]
include::{rb-snippets-dir}/navigator/InteractionsSpec.groovy[tag=action_execution,indent=0]
----

[[interact-closures]]
Expand All @@ -1093,14 +1098,14 @@ This `interact()` call performs the same work as the calls in the <<using-action

[source,groovy]
----
include::{snippets-dir}/navigator/InteractionsSpec.groovy[tag=interact,indent=0]
include::{rb-snippets-dir}/navigator/InteractionsSpec.groovy[tag=interact,indent=0]
----

While usually not needed, it's possible to directly access the `Actions` instance backing the `InteractDelegate`:

[source,groovy]
----
include::{snippets-dir}/navigator/InteractionsSpec.groovy[tag=actions_in_interact,indent=0]
include::{rb-snippets-dir}/navigator/InteractionsSpec.groovy[tag=actions_in_interact,indent=0]
----

For the full list of available methods that can be used for emulating user gestures see the documentation for the `{interact-delegate-api}` class.
Expand All @@ -1115,14 +1120,14 @@ Calls to `interact()` can be used to perform behaviors that are more complicated

[source,groovy]
----
include::{snippets-dir}/navigator/DragAndDropSpec.groovy[tag=verbose,indent=0]
include::{rb-snippets-dir}/navigator/DragAndDropSpec.groovy[tag=verbose,indent=0]
----

Drag-and-dropping can also be accomplished using the `dragAndDropBy()` convenience method from the Actions API:

[source,groovy]
----
include::{snippets-dir}/navigator/DragAndDropSpec.groovy[tag=convenience,indent=0]
include::{rb-snippets-dir}/navigator/DragAndDropSpec.groovy[tag=convenience,indent=0]
----

In this particular example, the element will be clicked then dragged 150 pixels to the right and 200 pixels downward before being released.
Expand All @@ -1138,6 +1143,6 @@ Control-clicking several elements, such as items in a list, is performed the sam

[source,groovy]
----
include::{snippets-dir}/navigator/ControlClickSpec.groovy[tag=import,indent=0]
include::{snippets-dir}/navigator/ControlClickSpec.groovy[tag=interact,indent=0]
include::{rb-snippets-dir}/navigator/ControlClickSpec.groovy[tag=import,indent=0]
include::{rb-snippets-dir}/navigator/ControlClickSpec.groovy[tag=interact,indent=0]
----
11 changes: 8 additions & 3 deletions doc/manual/src/docs/asciidoc/040-pages.adoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
ifndef::snippets-dir[]
:snippets-dir: ../../../../manual-snippets/src/test/groovy
:rb-snippets-dir: ../../../../manual-snippets/real-browser/src/test/groovy
:cm-snippets-dir: ../../../../../module/geb-core/src/main/groovy
endif::[]
[[pages]]
= Pages

Expand Down Expand Up @@ -442,13 +447,13 @@ This essentially means that verification of page transition (“at checking”)

[source,groovy]
----
include::{snippets-dir}/pages/ToWaitOptionSpec.groovy[tag=to_wait_page,indent=0]
include::{rb-snippets-dir}/pages/ToWaitOptionSpec.groovy[tag=to_wait_page,indent=0]
----
<1> Page change is asynchronous, e.g. an ajax call is involved.

[source,groovy]
----
include::{snippets-dir}/pages/ToWaitOptionSpec.groovy[tag=to_wait,indent=0]
include::{rb-snippets-dir}/pages/ToWaitOptionSpec.groovy[tag=to_wait,indent=0]
----

See the <<waiting, section on waiting>> for the semantics of the `waitFor()` method, that is used here internally.
Expand Down Expand Up @@ -763,7 +768,7 @@ The `{page-api}` implementation of this method looks like this…

[source,groovy]
----
include::{snippets-dir}/geb/Page.groovy[tag=convert_to_path,indent=0]
include::{cm-snippets-dir}/geb/Page.groovy[tag=convert_to_path,indent=0]
----

You can either overwrite this catchall method to control path conversion for all invocations or provide an overloaded version for a specific type signature.
Expand Down
Loading
Loading