async-webdriver 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe93050116a6c87ff0cdb48231966242d1c99e2bab1b773f455ff2d0fdda5a87
4
- data.tar.gz: 25977c570eb1235fadf1eee31a2e42c1fc97f98b7ccd9759aea46959b6fd82ed
3
+ metadata.gz: 7d44f03f80dc5674a12fabaeb89138fb2edc8d4aa240644a554d12edd9b70686
4
+ data.tar.gz: '07129f8f6173caebca9e23629018ceec2299c5093e5eea9bec252ad4641f0b1f'
5
5
  SHA512:
6
- metadata.gz: 5a433e7bd71265ef9e65097cdac2d19b966ac3c7ddabf2cbe02b64ffc17367fcfb9218aac5cfcd230ed481c819b0bed34ed91c8238e445cc426c6b219a64e5f8
7
- data.tar.gz: 8e762c8be4bf92aab638d38c047d8644a29a9e856256717eb6153ff6b8e1efc563f36fe6267ad6a8a2bf0a9a4c1521de1387249f00fab74da6987287609dbd16
6
+ metadata.gz: 729f7b01d41bae86173a3b0c26b7741ebae3ba3254da57c4d1583eb50c4564d1b1eace54bcf54b0f1c993f761e960d4ddaeda4389bb26dfe2255fcafb0885f9a
7
+ data.tar.gz: bb089d62cdf93ddf7db5799e1d64f7f97c7819b8272694d0c657bf1a3f990bd5f247677d38b631111df99576f290b14507de3f3cc9f4ce8150050b0be7357b3a
checksums.yaml.gz.sig CHANGED
Binary file
@@ -0,0 +1,252 @@
1
+ # Debugging
2
+
3
+ This guide explains how to debug WebDriver issues by capturing HTML source and screenshots when tests fail.
4
+
5
+ ## Overview
6
+
7
+ When WebDriver tests fail, it's often helpful to capture the current state of the page to understand what went wrong. The most useful debugging artifacts are:
8
+
9
+ - **HTML Source**: Shows the current DOM structure, helpful for understanding why element selectors might be failing
10
+ - **Screenshots**: Provides a visual representation of what the browser is actually showing
11
+
12
+ ## Core Concepts
13
+
14
+ `async-webdriver` provides built-in methods for capturing debugging information:
15
+
16
+ - {ruby Async::WebDriver::Session#document_source} returns the HTML source of the current page.
17
+ - {ruby Async::WebDriver::Session#screenshot} captures a screenshot of the entire page.
18
+ - {ruby Async::WebDriver::Element#screenshot} captures a screenshot of a specific element.
19
+
20
+ ## Basic Debugging
21
+
22
+ ### Capturing HTML Source
23
+
24
+ To save the current page HTML to a file:
25
+
26
+ ```ruby
27
+ require "async/webdriver"
28
+
29
+ Async::WebDriver::Bridge::Pool.open do |pool|
30
+ pool.session do |session|
31
+ session.visit("https://example.com")
32
+
33
+ # Save HTML source for debugging
34
+ html = session.document_source
35
+ File.write("debug.html", html)
36
+
37
+ puts "HTML saved to debug.html"
38
+ end
39
+ end
40
+ ```
41
+
42
+ ### Capturing Screenshots
43
+
44
+ To save a screenshot of the current page:
45
+
46
+ ```ruby
47
+ require "async/webdriver"
48
+
49
+ Async::WebDriver::Bridge::Pool.open do |pool|
50
+ pool.session do |session|
51
+ session.visit("https://example.com")
52
+
53
+ # Take a screenshot (returns PNG binary data)
54
+ screenshot_data = session.screenshot
55
+ File.binwrite("debug.png", screenshot_data)
56
+
57
+ puts "Screenshot saved to debug.png"
58
+ end
59
+ end
60
+ ```
61
+
62
+ ### Element Screenshots
63
+
64
+ To capture a screenshot of a specific element:
65
+
66
+ ```ruby
67
+ require "async/webdriver"
68
+
69
+ Async::WebDriver::Bridge::Pool.open do |pool|
70
+ pool.session do |session|
71
+ session.visit("https://example.com")
72
+
73
+ # Find an element and screenshot it
74
+ element = session.find_element_by_tag_name("body")
75
+ element_screenshot = element.screenshot
76
+ File.binwrite("element-debug.png", element_screenshot)
77
+
78
+ puts "Element screenshot saved to element-debug.png"
79
+ end
80
+ end
81
+ ```
82
+
83
+ ## Debugging Failed Element Searches
84
+
85
+ A common debugging scenario is when `find_element` fails. Here's how to capture debugging information:
86
+
87
+ ```ruby
88
+ require "async/webdriver"
89
+
90
+ def debug_element_search(session, locator_type, locator_value)
91
+ begin
92
+ # Use the correct locator format for async-webdriver
93
+ locator = {using: locator_type, value: locator_value}
94
+ element = session.find_element(locator)
95
+ puts "✅ Element found: #{locator_type}=#{locator_value}"
96
+ return element
97
+ rescue Async::WebDriver::NoSuchElementError => e
98
+ puts "❌ Element not found: #{locator_type}=#{locator_value}"
99
+
100
+ # Capture debugging information
101
+ timestamp = Time.now.strftime("%Y%m%d-%H%M%S")
102
+
103
+ # Save HTML source
104
+ html = session.document_source
105
+ html_file = "debug-#{timestamp}.html"
106
+ File.write(html_file, html)
107
+ puts "📄 HTML saved to #{html_file}"
108
+
109
+ # Save screenshot
110
+ screenshot_data = session.screenshot
111
+ screenshot_file = "debug-#{timestamp}.png"
112
+ File.binwrite(screenshot_file, screenshot_data)
113
+ puts "📸 Screenshot saved to #{screenshot_file}"
114
+
115
+ # Re-raise the original error
116
+ raise e
117
+ end
118
+ end
119
+
120
+ # Usage example
121
+ Async::WebDriver::Bridge::Pool.open do |pool|
122
+ pool.session do |session|
123
+ session.visit("https://example.com")
124
+
125
+ # This will save debug files if the element isn't found
126
+ button = debug_element_search(session, "id", "submit-button")
127
+ end
128
+ end
129
+ ```
130
+
131
+ ## Advanced Debugging Techniques
132
+
133
+ ### Configuring Timeouts for Debugging
134
+
135
+ WebDriver uses different timeout settings that affect how long operations wait:
136
+
137
+ ```ruby
138
+ require "async/webdriver"
139
+
140
+ Async::WebDriver::Bridge::Pool.open do |pool|
141
+ pool.session do |session|
142
+ # Configure timeouts for debugging (values in milliseconds)
143
+ session.implicit_wait_timeout = 10_000 # 10 seconds for element finding
144
+ session.page_load_timeout = 30_000 # 30 seconds for page loads
145
+ session.script_timeout = 5_000 # 5 seconds for JavaScript execution
146
+
147
+ puts "Current timeouts: #{session.timeouts}"
148
+
149
+ # Now element finding will wait up to 10 seconds
150
+ session.visit("https://example.com")
151
+ element = session.find_element(:id, "dynamic-content") # Will wait up to 10s
152
+ end
153
+ end
154
+ ```
155
+
156
+ ### Wait and Debug Pattern
157
+
158
+ Sometimes elements appear after a delay. Here's how to debug timing issues:
159
+
160
+ ```ruby
161
+ require "async/webdriver"
162
+
163
+ def wait_and_debug(session, locator_type, locator_value, timeout: 10000)
164
+ # Set implicit wait timeout (in milliseconds)
165
+ original_timeout = session.implicit_wait_timeout
166
+ session.implicit_wait_timeout = timeout
167
+
168
+ start_time = Time.now
169
+
170
+ begin
171
+ # Try to find the element (will use implicit wait timeout)
172
+ locator = {using: locator_type, value: locator_value}
173
+ session.find_element(locator)
174
+ rescue Async::WebDriver::NoSuchElementError => error
175
+ elapsed = Time.now - start_time
176
+ puts "⏰ Timeout after #{elapsed.round(2)}s waiting for #{locator_type}=#{locator_value}"
177
+
178
+ # Capture final state
179
+ timestamp = Time.now.strftime("%Y%m%d-%H%M%S")
180
+
181
+ html = session.document_source
182
+ File.write("timeout-debug-#{timestamp}.html", html)
183
+
184
+ screenshot_data = session.screenshot
185
+ File.binwrite("timeout-debug-#{timestamp}.png", screenshot_data)
186
+
187
+ puts "📄 Final HTML saved to timeout-debug-#{timestamp}.html"
188
+ puts "📸 Final screenshot saved to timeout-debug-#{timestamp}.png"
189
+
190
+ raise
191
+ ensure
192
+ # Restore original timeout
193
+ session.implicit_wait_timeout = original_timeout
194
+ end
195
+ end
196
+ ```
197
+
198
+ ### Multi-Step Debugging
199
+
200
+ For complex test scenarios, capture state at multiple points:
201
+
202
+ ```ruby
203
+ require "async/webdriver"
204
+
205
+ class DebugHelper
206
+ def initialize(test_name)
207
+ @test_name = test_name
208
+ @step = 0
209
+ end
210
+
211
+ def capture_state(session, description)
212
+ @step += 1
213
+ timestamp = Time.now.strftime("%Y%m%d-%H%M%S")
214
+ prefix = "#{@test_name}-step#{@step}-#{timestamp}"
215
+
216
+ # Save HTML
217
+ html = session.document_source
218
+ html_file = "#{prefix}-#{description}.html"
219
+ File.write(html_file, html)
220
+
221
+ # Save screenshot
222
+ screenshot_data = session.screenshot
223
+ screenshot_file = "#{prefix}-#{description}.png"
224
+ File.binwrite(screenshot_file, screenshot_data)
225
+
226
+ puts "🔍 Step #{@step}: #{description}"
227
+ puts " 📄 #{html_file}"
228
+ puts " 📸 #{screenshot_file}"
229
+ end
230
+ end
231
+
232
+ # Usage example
233
+ debug = DebugHelper.new("login-test")
234
+
235
+ Async::WebDriver::Bridge::Pool.open do |pool|
236
+ pool.session do |session|
237
+ debug.capture_state(session, "initial-page")
238
+
239
+ session.visit("https://example.com/login")
240
+ debug.capture_state(session, "login-page-loaded")
241
+
242
+ session.find_element_by_id("username").send_keys("user@example.com")
243
+ debug.capture_state(session, "username-entered")
244
+
245
+ session.find_element_by_id("password").send_keys("password")
246
+ debug.capture_state(session, "password-entered")
247
+
248
+ session.find_element_by_id("submit").click
249
+ debug.capture_state(session, "form-submitted")
250
+ end
251
+ end
252
+ ```
@@ -0,0 +1,90 @@
1
+ # Getting Started
2
+
3
+ This guide explains how to use `async-webdriver` for controlling a browser.
4
+
5
+ ## Installation
6
+
7
+ Add the gem to your project:
8
+
9
+ ~~~ bash
10
+ $ bundle add async-webdriver
11
+ ~~~
12
+
13
+ ## Core Concepts
14
+
15
+ `async-webdriver` is a Ruby implementation of the [WebDriver](https://www.w3.org/TR/webdriver/) protocol. It allows you to control a browser from Ruby code. It is built on top of [async](https://github.com/socketry/async) and [async-http](https://github.com/socketry/async-http). It has several core concepts:
16
+
17
+ - A {ruby Async::WebDriver::Bridge} can be used to start a web driver process, e.g. `chromedriver`, `geckodriver`, etc. It can be used in isolation, or not at all.
18
+ - A {ruby Async::WebDriver::Client} is used to connect to a running web driver and can be used to create new sessions.
19
+ - A {ruby Async::WebDriver::Session} represents a single browser session. It is used to control a browser window and navigate to different pages.
20
+ - A {ruby Async::WebDriver::Element} represents a single element on a page. It can be used to interact with the element, e.g. click, type, etc.
21
+
22
+ ## Basic Usage
23
+
24
+ The following example shows how to use `async-webdriver` to open a browser, navigate to a page, and click a button:
25
+
26
+ ~~~ ruby
27
+ require 'async/webdriver'
28
+
29
+ Async do
30
+ bridge = Async::WebDriver::Bridge::Chrome.new(headless: false)
31
+
32
+ driver = bridge.start
33
+ client = Async::WebDriver::Client.open(driver.endpoint)
34
+
35
+ session = client.session(bridge.default_capabilities)
36
+ # Set the implicit wait timeout to 10 seconds since we are dealing with the real internet (which can be slow):
37
+ session.implicit_wait_timeout = 10_000
38
+
39
+ session.visit('https://google.com')
40
+
41
+ session.fill_in('q', 'async-webdriver')
42
+ session.click_button("I'm Feeling Lucky")
43
+
44
+ puts session.document_title
45
+ ensure
46
+ session&.close
47
+ client&.close
48
+ driver&.close
49
+ end
50
+ ~~~
51
+
52
+ ### Using a Pool to Manage Sessions
53
+
54
+ If you are running multiple tests in parallel, you may want to use a session pool to manage the sessions. This can be done as follows:
55
+
56
+ ~~~ ruby
57
+ require 'async/webdriver'
58
+
59
+ Async do
60
+ bridge = Async::WebDriver::Bridge::Pool.new(Async::WebDriver::Bridge::Chrome.new(headless: false))
61
+
62
+ session = bridge.session
63
+ # Set the implicit wait timeout to 10 seconds since we are dealing with the real internet (which can be slow):
64
+ session.implicit_wait_timeout = 10_000
65
+
66
+ session.visit('https://google.com')
67
+
68
+ session.fill_in('q', 'async-webdriver')
69
+ session.click_button("I'm Feeling Lucky")
70
+
71
+ puts session.document_title
72
+ ensure
73
+ session&.close
74
+ bridge&.close
75
+ end
76
+ ~~~
77
+
78
+ The sessions will be cached and reused if possible.
79
+
80
+ ## Integration vs Unit Testing
81
+
82
+ `async-webdriver` is designed for integration testing. It is not designed for unit testing (e.g. wrapping a tool like `rack-test` as `capybara` can do). It is designed for testing your application in a real browser and web server. It is designed for testing your application in the same way that a real user would use it. Unfortunately, this style of integration testing is significantly slower than unit testing, but it is also significantly more representative of how your application will behave in production. There are other tools, e.g. [rack-test](https://github.com/rack/rack-test) which provide significantly faster unit testing, but they do not test how your application will behave in an actual web browser. A comprehensive test suite should include both unit tests and integration tests.
83
+
84
+ ### Headless Mode
85
+
86
+ During testing, often you will want to see the real browser window to determine if the test is working correctly. By default, for performance reasons, `async-webdriver` will run the browser in headless mode. This means that the browser will not be visible on the screen. If you want to see the browser window, you can disable headless mode by setting the `headless` option to `false`:
87
+
88
+ ~~~ shell
89
+ $ ASYNC_WEBDRIVER_BRIDGE_HEADLESS=false ./webdriver-script.rb
90
+ ~~~
@@ -0,0 +1,68 @@
1
+ # GitHub Actions Integrations
2
+
3
+ This guide explains how to use `async-webdriver` with GitHub Actions.
4
+
5
+ We recommend using the [browser-actions](https://github.com/browser-actions) for setting up `chromedriver` and `geckodriver`. They are pre-configured to work with `async-webdriver` and are easy to use.
6
+
7
+ ## Pipeline Configuration
8
+
9
+ The following example shows how to setup both `chromedriver` and `geckodriver` in a single pipeline:
10
+
11
+ ~~~ yaml
12
+ name: Test
13
+
14
+ on: [push, pull_request]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ env:
20
+ CONSOLE_OUTPUT: XTerm
21
+
22
+ jobs:
23
+ test:
24
+ name: ${{matrix.ruby}} on ${{matrix.os}}
25
+ runs-on: ${{matrix.os}}-latest
26
+ continue-on-error: ${{matrix.experimental}}
27
+
28
+ strategy:
29
+ matrix:
30
+ os:
31
+ - ubuntu
32
+ - macos
33
+
34
+ ruby:
35
+ - "3.0"
36
+ - "3.1"
37
+ - "3.2"
38
+
39
+ experimental: [false]
40
+
41
+ include:
42
+ - os: ubuntu
43
+ ruby: truffleruby
44
+ experimental: true
45
+ - os: ubuntu
46
+ ruby: jruby
47
+ experimental: true
48
+ - os: ubuntu
49
+ ruby: head
50
+ experimental: true
51
+
52
+ steps:
53
+ - uses: actions/checkout@v3
54
+ - uses: ruby/setup-ruby@v1
55
+ with:
56
+ ruby-version: ${{matrix.ruby}}
57
+ bundler-cache: true
58
+
59
+ - uses: browser-actions/setup-chrome@v1
60
+ - uses: browser-actions/setup-firefox@v1
61
+ - uses: browser-actions/setup-geckodriver@latest
62
+ with:
63
+ token: ${{secrets.GITHUB_TOKEN}}
64
+
65
+ - name: Run tests
66
+ timeout-minutes: 10
67
+ run: bundle exec bake test
68
+ ~~~
@@ -0,0 +1,24 @@
1
+ # Automatically generated context index for Utopia::Project guides.
2
+ # Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
3
+ ---
4
+ description: A native library implementing the W3C WebDriver client specification.
5
+ metadata:
6
+ documentation_uri: https://socketry.github.io/async-webdriver/
7
+ funding_uri: https://github.com/sponsors/ioquatix
8
+ source_code_uri: https://github.com/socketry/async-webdriver.git
9
+ files:
10
+ - path: getting-started.md
11
+ title: Getting Started
12
+ description: This guide explains how to use `async-webdriver` for controlling a
13
+ browser.
14
+ - path: debugging.md
15
+ title: Debugging
16
+ description: This guide explains how to debug WebDriver issues by capturing HTML
17
+ source and screenshots when tests fail.
18
+ - path: github-actions-integration.md
19
+ title: GitHub Actions Integrations
20
+ description: This guide explains how to use `async-webdriver` with GitHub Actions.
21
+ - path: sus-integration.md
22
+ title: Sus Integration
23
+ description: This guide will show you how to integrate `async-webdriver` with the
24
+ sus test framework.
@@ -0,0 +1,51 @@
1
+ # Sus Integration
2
+
3
+ This guide will show you how to integrate `async-webdriver` with the sus test framework.
4
+
5
+ ## Usage
6
+
7
+ Sus has out of the box support for `async-webdriver`. You can use it like this:
8
+
9
+ ```shell
10
+ $ bundle add sus-fixtures-async-http sus-fixtures-async-webdriver protocol-rack
11
+ $ bundle update
12
+ ```
13
+
14
+ Then write your integration test:
15
+
16
+ ```ruby
17
+ # test/my_integration_test.rb
18
+
19
+ require "sus/fixtures/async/http/server_context"
20
+ require "sus/fixtures/async/webdriver/session_context"
21
+
22
+ require "protocol/rack/adapter"
23
+ require "rack/builder"
24
+
25
+ describe "my website" do
26
+ include Sus::Fixtures::Async::HTTP::ServerContext
27
+ include Sus::Fixtures::Async::WebDriver::SessionContext
28
+
29
+ def middleware
30
+ Protocol::Rack::Adapter.new(app)
31
+ end
32
+
33
+ def app
34
+ Rack::Builder.load_file(File.expand_path("../config.ru", __dir__))
35
+ end
36
+
37
+ it "has a title" do
38
+ navigate_to("/")
39
+
40
+ expect(session.document_title).to be == "Example"
41
+ end
42
+
43
+ it "has a paragraph" do
44
+ navigate_to("/")
45
+
46
+ expect(session).to have_element(tag_name: "p")
47
+ end
48
+ end
49
+ ```
50
+
51
+ For more information, refer to the [sus-fixtures-async-webdriver](https://github.com/socketry/sus-fixtures-async-webdriver) documentation.
@@ -5,6 +5,6 @@
5
5
 
6
6
  module Async
7
7
  module WebDriver
8
- VERSION = "0.9.0"
8
+ VERSION = "0.9.1"
9
9
  end
10
10
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-webdriver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -112,6 +112,11 @@ executables: []
112
112
  extensions: []
113
113
  extra_rdoc_files: []
114
114
  files:
115
+ - context/debugging.md
116
+ - context/getting-started.md
117
+ - context/github-actions-integration.md
118
+ - context/index.yaml
119
+ - context/sus-integration.md
115
120
  - lib/async/webdriver.rb
116
121
  - lib/async/webdriver/bridge.rb
117
122
  - lib/async/webdriver/bridge/chrome.rb
metadata.gz.sig CHANGED
@@ -1,2 +1,2 @@
1
- "�A��,Yq?
2
- Xy*�-+<�sB�:mº�&d��Y�,W$��XQn����u9���O�,����� ~������4qk��3v��'��i8:���w��@\1���\�1QF����QQ���J=�NdF�p���C؍a��?�la��.�P j�TL��eMHs��7�!��*,���Ar��5�~�?�$%d��DwkeF��p
1
+ ��BTKM�����hB�ڪ%�����N��=\K�2�-*fq2�@�v��[�f���K����F�;����gC���M8��D�$�XJ�뽊�r�|#k����댫|���<߰bN��-�d��t��D���!���Ũ�ډ�pjV�QԶW"D����$ S�����4q�EA�OWl,�m2�,�ʅt��@�9���-�����V#���� �P8�.�&�0�C+��:-����D+"�nadL���AW'������[B,El�k�����bۨ�C����m�C�S@�q��U�)A�
2
+ ��\P�ۙO�2e�O{l!Y̌�9�'���lM'U�b�.o���פ۞���: ��!ir}[�p����}8��ғzg�Í