Skip to content

Commit

Permalink
updates based on feedback and removing unnecessary explanations
Browse files Browse the repository at this point in the history
  • Loading branch information
titusfortner committed Aug 3, 2023
1 parent f9174d3 commit 357f13b
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 323 deletions.
113 changes: 33 additions & 80 deletions website_and_docs/content/documentation/webdriver/waits.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,89 +7,54 @@ aliases: ["/documentation/en/webdriver/waits/"]

Perhaps the most common challenge for browser automation is ensuring
that the web application is in a state to execute a particular
Selenium command as desired. the processes often end up in
Selenium command as desired. The processes often end up in
a _race condition_ where sometimes the browser gets into the right
state first (things work as intended) and sometimes the Selenium code
executes first (things do not work as intended). This is the
primary cause of _flaky tests_.
executes first (things do not work as intended). This is one of the
primary causes of _flaky tests_.

All navigation commands wait for a specific `readyState` value
based on the [page load strategy]({{< ref "drivers/options#pageloadstrategy" >}}) (the
default is `"complete"`) before the driver returns control to the code.
default value to wait for is `"complete"`) before the driver returns control to the code.
The `readyState` only concerns itself with loading assets defined in the HTML,
but loaded JavaScript assets often result in changes to the site,
and elements you need to interact with may not yet be on the page
and elements that need to be interacted with may not yet be on the page
when the code is ready to execute the next Selenium command.

Similarly, in a lot of single page applications, elements get dynamically
added to a page or change visibility based on a click. For example, the box
element takes a second to show up after clicking on the "adder" button, so
even trying to locate that button will error without some form of synchronization:
added to a page or change visibility based on a click.
An element must be both present and
[displayed](({{< ref "elements/information/#is-displayed" >}})) on the page
in order for Selenium to interact with it.

{{< tabpane text=true langEqualsHeader=true >}}
{{< tab header="Java" >}}
{{< gh-codeblock path="examples/java/src/test/java/dev/selenium/waits/WaitsTest.java#L23-L28" >}}
{{< /tab >}}
{{< tab header="Python" >}}
{{< gh-codeblock path="examples/python/tests/waits/test_waits.py#L9-L13" >}}
{{< /tab >}}
{{< tab header="CSharp" >}}
{{< gh-codeblock path="examples/dotnet/SeleniumDocs/Waits/WaitsTest.cs#L15-L20" >}}
{{< /tab >}}
{{< tab header="Ruby" >}}
{{< gh-codeblock path="examples/ruby/spec/waits/waits_spec.rb#L9-L14" >}}
{{< /tab >}}
{{< tab header="JavaScript" >}}
{{< badge-code >}}
{{< /tab >}}
{{< tab header="Kotlin" >}}
{{< badge-code >}}
{{< /tab >}}
{{< /tabpane >}}
Take this page for example: https://www.selenium.dev/selenium/web/dynamic.html
When the "Add a box!" button is clicked, a "div" element that does not exist is created.
When the "Reveal a new input" button is clicked, a hidden text field element is displayed.
In both cases the transition takes a couple seconds.
If the Selenium code is to click one of these buttons and interact with the resulting element,
it will do so before that element is ready and fail.

There are several synchronization strategies that can be used to properly wait for
the application to be in the state you need it to be for the next Selenium command.
The first solution many people turn to is adding a sleep statement to
pause the code execution for a set period of time.
Because the code can't know exactly how long it needs to wait, this
can fail when it doesn't sleep long enough. Alternately, if the value is set too high
and a sleep statement is added in every place it is needed, the duration of
the session can become prohibitive.

## Hard-coded sleeps
Selenium provides two different mechanisms for synchronization that are better.

This causes the code to stop executing for a set period of time.
Because your code can't know exactly how long you need to wait, this
will either fail when it doesn't sleep long enough, or will cause
your sessions to take much longer than they need to. That said, putting in a sleep command is one way to keep
the above code from failing:

{{< tabpane text=true langEqualsHeader=true >}}
{{< tab header="Java" >}}
{{< gh-codeblock path="examples/java/src/test/java/dev/selenium/waits/WaitsTest.java#L34-L38" >}}
{{< /tab >}}
{{< tab header="Python" >}}
{{< gh-codeblock path="examples/python/tests/waits/test_waits.py#L18-L21" >}}
{{< /tab >}}
{{< tab header="CSharp" >}}
{{< gh-codeblock path="examples/dotnet/SeleniumDocs/Waits/WaitsTest.cs#L27-L31" >}}
{{< /tab >}}
{{< tab header="Ruby" >}}
{{< gh-codeblock path="examples/ruby/spec/waits/waits_spec.rb#L19-L22" >}}
{{< /tab >}}
{{< tab header="JavaScript" >}}
{{< badge-code >}}
{{< /tab >}}
{{< tab header="Kotlin" >}}
{{< badge-code >}}
{{< /tab >}}
{{< /tabpane >}}

## Implicit waits
Selenium has a built-in way to automatically wait for elements called an _implicit wait_.
You set an implicit wait either with the [timeouts](({{< ref "drivers/options#timeouts" >}}))
An implicit wait value can be set either with the [timeouts](({{< ref "drivers/options#timeouts" >}}))
capability in the browser options, or with a driver method (as shown below).

This is a global setting that applies to every element location call for the entire session.
The default value is `0`, which means that if the element is not found, it will
immediately return an error. If an implicit wait is set, the driver will wait for the
duration of the provided value before returning the error. Note that as soon as the
element is located, the driver will return the value and your code may continue, so a larger
implicit wait value won't necessarily increase the time of your session.
element is located, the driver will return the element reference and the code will continue executing,
so a larger implicit wait value won't necessarily increase the duration of the session.

*Warning:*
Do not mix implicit and explicit waits.
Expand Down Expand Up @@ -123,25 +88,13 @@ Solving our example with an implicit wait looks like this:

## Explicit waits

_Explicit waits_ are loops you add to your code that poll the application
_Explicit waits_ are loops added to the code that poll the application
for a specific condition to evaluate as true before it exits the loop and
continues to the next command in your code. If the condition is not met before a designated timeout value,
the code will give a timeout error.

There are many ways for the application not to be in the desired state,
so explicit waits are a great choice to specify exactly what is desired
in each place they are needed.
For example, an element must be both present in the DOM and
[displayed](({{< ref "elements/information/#is-displayed" >}})) on the page
in order for Selenium to interact with it.

In this example, clicking the
"reveal" button displays an input field that is already present in the DOM.
An explicit wait can be used to ensure the element is interactable before
sending keys to it. An important feature of the Wait class in Selenium is that it will automatically retry
when a _no such element_ error happens, which makes it much easier to write succinct code.
We do not need to handle whether the element is there and we can just focus on whether it
evaluates as displayed:
continues to the next command in the code. If the condition is not met before a designated timeout value,
the code will give a timeout error. Since there are many ways for the application not to be in the desired state,
so explicit waits are a great choice to specify the exact condition to wait for
in each place it is needed.
Another nice feature is that, by default, the Selenium Wait class automatically waits for the designated element to exist.

{{< tabpane text=true langEqualsHeader=true >}}
{{% tab header="Java" %}}
Expand Down Expand Up @@ -171,11 +124,11 @@ JavaScript also supports [Expected Conditions](({{< ref "support_features/expect

### Customization

The Wait class can be created with various parameters that will change how the conditions are evaluated.
The Wait class can be instantiated with various parameters that will change how the conditions are evaluated.

This can include:
* Changing how often the code is evaluated (polling interval)
* Specifying which exceptions should be retried
* Specifying which exceptions should be handled automatically
* Changing the total timeout length
* Customizing the timeout message

Expand Down
115 changes: 34 additions & 81 deletions website_and_docs/content/documentation/webdriver/waits.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,89 +7,54 @@ aliases: ["/documentation/ja/webdriver/waits/"]

Perhaps the most common challenge for browser automation is ensuring
that the web application is in a state to execute a particular
Selenium command as desired. the processes often end up in
Selenium command as desired. The processes often end up in
a _race condition_ where sometimes the browser gets into the right
state first (things work as intended) and sometimes the Selenium code
executes first (things do not work as intended). This is the
primary cause of _flaky tests_.
executes first (things do not work as intended). This is one of the
primary causes of _flaky tests_.

All navigation commands wait for a specific `readyState` value
based on the [page load strategy]({{< ref "drivers/options#pageloadstrategy" >}}) (the
default is `"complete"`) before the driver returns control to the code.
based on the [page load strategy]({{< ref "drivers/options#pageloadstrategy" >}}) (the
default value to wait for is `"complete"`) before the driver returns control to the code.
The `readyState` only concerns itself with loading assets defined in the HTML,
but loaded JavaScript assets often result in changes to the site,
and elements you need to interact with may not yet be on the page
and elements that need to be interacted with may not yet be on the page
when the code is ready to execute the next Selenium command.

Similarly, in a lot of single page applications, elements get dynamically
added to a page or change visibility based on a click. For example, the box
element takes a second to show up after clicking on the "adder" button, so
even trying to locate that button will error without some form of synchronization:
added to a page or change visibility based on a click.
An element must be both present and
[displayed](({{< ref "elements/information/#is-displayed" >}})) on the page
in order for Selenium to interact with it.

{{< tabpane text=true langEqualsHeader=true >}}
{{< tab header="Java" >}}
{{< gh-codeblock path="examples/java/src/test/java/dev/selenium/waits/WaitsTest.java#L23-L28" >}}
{{< /tab >}}
{{< tab header="Python" >}}
{{< gh-codeblock path="examples/python/tests/waits/test_waits.py#L9-L13" >}}
{{< /tab >}}
{{< tab header="CSharp" >}}
{{< gh-codeblock path="examples/dotnet/SeleniumDocs/Waits/WaitsTest.cs#L15-L20" >}}
{{< /tab >}}
{{< tab header="Ruby" >}}
{{< gh-codeblock path="examples/ruby/spec/waits/waits_spec.rb#L9-L14" >}}
{{< /tab >}}
{{< tab header="JavaScript" >}}
{{< badge-code >}}
{{< /tab >}}
{{< tab header="Kotlin" >}}
{{< badge-code >}}
{{< /tab >}}
{{< /tabpane >}}
Take this page for example: https://www.selenium.dev/selenium/web/dynamic.html
When the "Add a box!" button is clicked, a "div" element that does not exist is created.
When the "Reveal a new input" button is clicked, a hidden text field element is displayed.
In both cases the transition takes a couple seconds.
If the Selenium code is to click one of these buttons and interact with the resulting element,
it will do so before that element is ready and fail.

There are several synchronization strategies that can be used to properly wait for
the application to be in the state you need it to be for the next Selenium command.
The first solution many people turn to is adding a sleep statement to
pause the code execution for a set period of time.
Because the code can't know exactly how long it needs to wait, this
can fail when it doesn't sleep long enough. Alternately, if the value is set too high
and a sleep statement is added in every place it is needed, the duration of
the session can become prohibitive.

## Hard-coded sleeps
Selenium provides two different mechanisms for synchronization that are better.

This causes the code to stop executing for a set period of time.
Because your code can't know exactly how long you need to wait, this
will either fail when it doesn't sleep long enough, or will cause
your sessions to take much longer than they need to. That said, putting in a sleep command is one way to keep
the above code from failing:

{{< tabpane text=true langEqualsHeader=true >}}
{{< tab header="Java" >}}
{{< gh-codeblock path="examples/java/src/test/java/dev/selenium/waits/WaitsTest.java#L34-L38" >}}
{{< /tab >}}
{{< tab header="Python" >}}
{{< gh-codeblock path="examples/python/tests/waits/test_waits.py#L18-L21" >}}
{{< /tab >}}
{{< tab header="CSharp" >}}
{{< gh-codeblock path="examples/dotnet/SeleniumDocs/Waits/WaitsTest.cs#L27-L31" >}}
{{< /tab >}}
{{< tab header="Ruby" >}}
{{< gh-codeblock path="examples/ruby/spec/waits/waits_spec.rb#L19-L22" >}}
{{< /tab >}}
{{< tab header="JavaScript" >}}
{{< badge-code >}}
{{< /tab >}}
{{< tab header="Kotlin" >}}
{{< badge-code >}}
{{< /tab >}}
{{< /tabpane >}}

## Implicit waits
Selenium has a built-in way to automatically wait for elements called an _implicit wait_.
You set an implicit wait either with the [timeouts](({{< ref "drivers/options#timeouts" >}}))
An implicit wait value can be set either with the [timeouts](({{< ref "drivers/options#timeouts" >}}))
capability in the browser options, or with a driver method (as shown below).

This is a global setting that applies to every element location call for the entire session.
The default value is `0`, which means that if the element is not found, it will
immediately return an error. If an implicit wait is set, the driver will wait for the
duration of the provided value before returning the error. Note that as soon as the
element is located, the driver will return the value and your code may continue, so a larger
implicit wait value won't necessarily increase the time of your session.
element is located, the driver will return the element reference and the code will continue executing,
so a larger implicit wait value won't necessarily increase the duration of the session.

*Warning:*
Do not mix implicit and explicit waits.
Expand Down Expand Up @@ -123,25 +88,13 @@ Solving our example with an implicit wait looks like this:

## Explicit waits

_Explicit waits_ are loops you add to your code that poll the application
_Explicit waits_ are loops added to the code that poll the application
for a specific condition to evaluate as true before it exits the loop and
continues to the next command in your code. If the condition is not met before a designated timeout value,
the code will give a timeout error.

There are many ways for the application not to be in the desired state,
so explicit waits are a great choice to specify exactly what is desired
in each place they are needed.
For example, an element must be both present in the DOM and
[displayed](({{< ref "elements/information/#is-displayed" >}})) on the page
in order for Selenium to interact with it.

In this example, clicking the
"reveal" button displays an input field that is already present in the DOM.
An explicit wait can be used to ensure the element is interactable before
sending keys to it. An important feature of the Wait class in Selenium is that it will automatically retry
when a _no such element_ error happens, which makes it much easier to write succinct code.
We do not need to handle whether the element is there and we can just focus on whether it
evaluates as displayed:
continues to the next command in the code. If the condition is not met before a designated timeout value,
the code will give a timeout error. Since there are many ways for the application not to be in the desired state,
so explicit waits are a great choice to specify the exact condition to wait for
in each place it is needed.
Another nice feature is that, by default, the Selenium Wait class automatically waits for the designated element to exist.

{{< tabpane text=true langEqualsHeader=true >}}
{{% tab header="Java" %}}
Expand Down Expand Up @@ -171,11 +124,11 @@ JavaScript also supports [Expected Conditions](({{< ref "support_features/expect

### Customization

The Wait class can be created with various parameters that will change how the conditions are evaluated.
The Wait class can be instantiated with various parameters that will change how the conditions are evaluated.

This can include:
* Changing how often the code is evaluated (polling interval)
* Specifying which exceptions should be retried
* Specifying which exceptions should be handled automatically
* Changing the total timeout length
* Customizing the timeout message

Expand Down

0 comments on commit 357f13b

Please sign in to comment.