playwright-ruby-client 0.0.8 → 1.58.1.alpha1
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 +4 -4
- data/AGENTS.md +4 -0
- data/CLAUDE/api_generation.md +28 -0
- data/CLAUDE/ci_expectations.md +23 -0
- data/CLAUDE/gem_release_flow.md +39 -0
- data/CLAUDE/past_upgrade_pr_patterns.md +42 -0
- data/CLAUDE/playwright_upgrade_workflow.md +35 -0
- data/CLAUDE/rspec_debugging.md +30 -0
- data/CLAUDE/unimplemented_examples.md +18 -0
- data/CLAUDE.md +32 -0
- data/CONTRIBUTING.md +5 -0
- data/README.md +60 -16
- data/documentation/README.md +33 -0
- data/documentation/babel.config.js +3 -0
- data/documentation/docs/api/api_request.md +7 -0
- data/documentation/docs/api/api_request_context.md +298 -0
- data/documentation/docs/api/api_response.md +114 -0
- data/documentation/docs/api/browser.md +237 -0
- data/documentation/docs/api/browser_context.md +503 -0
- data/documentation/docs/api/browser_type.md +184 -0
- data/documentation/docs/api/cdp_session.md +44 -0
- data/documentation/docs/api/clock.md +154 -0
- data/documentation/docs/api/console_message.md +85 -0
- data/documentation/docs/api/dialog.md +84 -0
- data/documentation/docs/api/download.md +111 -0
- data/documentation/docs/api/element_handle.md +694 -0
- data/documentation/docs/api/experimental/_category_.yml +3 -0
- data/documentation/docs/api/experimental/android.md +42 -0
- data/documentation/docs/api/experimental/android_device.md +109 -0
- data/documentation/docs/api/experimental/android_input.md +43 -0
- data/documentation/docs/api/experimental/android_socket.md +7 -0
- data/documentation/docs/api/experimental/android_web_view.md +7 -0
- data/documentation/docs/api/file_chooser.md +53 -0
- data/documentation/docs/api/frame.md +1218 -0
- data/documentation/docs/api/frame_locator.md +348 -0
- data/documentation/docs/api/js_handle.md +121 -0
- data/documentation/docs/api/keyboard.md +170 -0
- data/documentation/docs/api/locator.md +1495 -0
- data/documentation/docs/api/locator_assertions.md +827 -0
- data/documentation/docs/api/mouse.md +86 -0
- data/documentation/docs/api/page.md +1946 -0
- data/documentation/docs/api/page_assertions.md +65 -0
- data/documentation/docs/api/playwright.md +66 -0
- data/documentation/docs/api/request.md +255 -0
- data/documentation/docs/api/response.md +176 -0
- data/documentation/docs/api/route.md +205 -0
- data/documentation/docs/api/selectors.md +63 -0
- data/documentation/docs/api/touchscreen.md +22 -0
- data/documentation/docs/api/tracing.md +129 -0
- data/documentation/docs/api/web_socket.md +51 -0
- data/documentation/docs/api/worker.md +83 -0
- data/documentation/docs/article/api_coverage.mdx +11 -0
- data/documentation/docs/article/getting_started.md +161 -0
- data/documentation/docs/article/guides/_category_.yml +3 -0
- data/documentation/docs/article/guides/download_playwright_driver.md +55 -0
- data/documentation/docs/article/guides/inspector.md +31 -0
- data/documentation/docs/article/guides/launch_browser.md +121 -0
- data/documentation/docs/article/guides/playwright_on_alpine_linux.md +112 -0
- data/documentation/docs/article/guides/rails_integration.md +278 -0
- data/documentation/docs/article/guides/rails_integration_with_null_driver.md +145 -0
- data/documentation/docs/article/guides/recording_video.md +79 -0
- data/documentation/docs/article/guides/rspec_integration.md +59 -0
- data/documentation/docs/article/guides/semi_automation.md +71 -0
- data/documentation/docs/article/guides/use_storage_state.md +78 -0
- data/documentation/docs/include/api_coverage.md +671 -0
- data/documentation/docusaurus.config.js +114 -0
- data/documentation/package.json +39 -0
- data/documentation/sidebars.js +15 -0
- data/documentation/src/components/HomepageFeatures.js +61 -0
- data/documentation/src/components/HomepageFeatures.module.css +13 -0
- data/documentation/src/css/custom.css +44 -0
- data/documentation/src/pages/index.js +49 -0
- data/documentation/src/pages/index.module.css +41 -0
- data/documentation/src/pages/markdown-page.md +7 -0
- data/documentation/static/.nojekyll +0 -0
- data/documentation/static/img/playwright-logo.svg +9 -0
- data/documentation/static/img/playwright-ruby-client.png +0 -0
- data/documentation/static/img/undraw_dropdown_menu.svg +1 -0
- data/documentation/static/img/undraw_web_development.svg +1 -0
- data/documentation/static/img/undraw_windows.svg +1 -0
- data/documentation/yarn.lock +9005 -0
- data/lib/playwright/{input_types/android_input.rb → android_input_impl.rb} +5 -1
- data/lib/playwright/api_implementation.rb +18 -0
- data/lib/playwright/api_response_impl.rb +77 -0
- data/lib/playwright/channel.rb +62 -1
- data/lib/playwright/channel_owner.rb +70 -7
- data/lib/playwright/channel_owners/android.rb +16 -3
- data/lib/playwright/channel_owners/android_device.rb +22 -66
- data/lib/playwright/channel_owners/api_request_context.rb +247 -0
- data/lib/playwright/channel_owners/artifact.rb +40 -0
- data/lib/playwright/channel_owners/binding_call.rb +70 -0
- data/lib/playwright/channel_owners/browser.rb +114 -22
- data/lib/playwright/channel_owners/browser_context.rb +589 -15
- data/lib/playwright/channel_owners/browser_type.rb +90 -1
- data/lib/playwright/channel_owners/cdp_session.rb +19 -0
- data/lib/playwright/channel_owners/dialog.rb +32 -0
- data/lib/playwright/channel_owners/element_handle.rb +107 -43
- data/lib/playwright/channel_owners/fetch_request.rb +8 -0
- data/lib/playwright/channel_owners/frame.rb +334 -104
- data/lib/playwright/channel_owners/js_handle.rb +9 -13
- data/lib/playwright/channel_owners/local_utils.rb +82 -0
- data/lib/playwright/channel_owners/page.rb +778 -95
- data/lib/playwright/channel_owners/playwright.rb +25 -30
- data/lib/playwright/channel_owners/request.rb +120 -18
- data/lib/playwright/channel_owners/response.rb +113 -0
- data/lib/playwright/channel_owners/route.rb +181 -0
- data/lib/playwright/channel_owners/stream.rb +30 -0
- data/lib/playwright/channel_owners/tracing.rb +117 -0
- data/lib/playwright/channel_owners/web_socket.rb +96 -0
- data/lib/playwright/channel_owners/worker.rb +46 -0
- data/lib/playwright/channel_owners/writable_stream.rb +14 -0
- data/lib/playwright/clock_impl.rb +67 -0
- data/lib/playwright/connection.rb +111 -63
- data/lib/playwright/console_message_impl.rb +29 -0
- data/lib/playwright/download_impl.rb +32 -0
- data/lib/playwright/errors.rb +42 -5
- data/lib/playwright/event_emitter.rb +17 -3
- data/lib/playwright/event_emitter_proxy.rb +49 -0
- data/lib/playwright/events.rb +10 -5
- data/lib/playwright/file_chooser_impl.rb +24 -0
- data/lib/playwright/frame_locator_impl.rb +66 -0
- data/lib/playwright/har_router.rb +89 -0
- data/lib/playwright/http_headers.rb +14 -0
- data/lib/playwright/input_files.rb +102 -15
- data/lib/playwright/javascript/expression.rb +7 -11
- data/lib/playwright/javascript/regex.rb +23 -0
- data/lib/playwright/javascript/source_url.rb +16 -0
- data/lib/playwright/javascript/value_parser.rb +108 -19
- data/lib/playwright/javascript/value_serializer.rb +47 -8
- data/lib/playwright/javascript/visitor_info.rb +26 -0
- data/lib/playwright/javascript.rb +2 -10
- data/lib/playwright/{input_types/keyboard.rb → keyboard_impl.rb} +6 -2
- data/lib/playwright/locator_assertions_impl.rb +571 -0
- data/lib/playwright/locator_impl.rb +544 -0
- data/lib/playwright/locator_utils.rb +136 -0
- data/lib/playwright/mouse_impl.rb +57 -0
- data/lib/playwright/page_assertions_impl.rb +154 -0
- data/lib/playwright/playwright_api.rb +102 -30
- data/lib/playwright/raw_headers.rb +61 -0
- data/lib/playwright/route_handler.rb +78 -0
- data/lib/playwright/select_option_values.rb +34 -13
- data/lib/playwright/selectors_impl.rb +45 -0
- data/lib/playwright/test.rb +102 -0
- data/lib/playwright/timeout_settings.rb +9 -4
- data/lib/playwright/touchscreen_impl.rb +14 -0
- data/lib/playwright/transport.rb +61 -10
- data/lib/playwright/url_matcher.rb +24 -2
- data/lib/playwright/utils.rb +48 -13
- data/lib/playwright/version.rb +2 -1
- data/lib/playwright/video.rb +54 -0
- data/lib/playwright/waiter.rb +166 -0
- data/lib/playwright/web_socket_client.rb +167 -0
- data/lib/playwright/web_socket_transport.rb +116 -0
- data/lib/playwright.rb +188 -11
- data/lib/playwright_api/android.rb +46 -11
- data/lib/playwright_api/android_device.rb +182 -31
- data/lib/playwright_api/android_input.rb +22 -13
- data/lib/playwright_api/android_socket.rb +18 -0
- data/lib/playwright_api/android_web_view.rb +24 -0
- data/lib/playwright_api/api_request.rb +26 -0
- data/lib/playwright_api/api_request_context.rb +311 -0
- data/lib/playwright_api/api_response.rb +92 -0
- data/lib/playwright_api/browser.rb +116 -103
- data/lib/playwright_api/browser_context.rb +290 -389
- data/lib/playwright_api/browser_type.rb +96 -118
- data/lib/playwright_api/cdp_session.rb +36 -39
- data/lib/playwright_api/clock.rb +121 -0
- data/lib/playwright_api/console_message.rb +35 -19
- data/lib/playwright_api/dialog.rb +53 -50
- data/lib/playwright_api/download.rb +49 -43
- data/lib/playwright_api/element_handle.rb +354 -402
- data/lib/playwright_api/file_chooser.rb +15 -18
- data/lib/playwright_api/frame.rb +703 -603
- data/lib/playwright_api/frame_locator.rb +285 -0
- data/lib/playwright_api/js_handle.rb +50 -76
- data/lib/playwright_api/keyboard.rb +67 -146
- data/lib/playwright_api/locator.rb +1304 -0
- data/lib/playwright_api/locator_assertions.rb +704 -0
- data/lib/playwright_api/mouse.rb +23 -29
- data/lib/playwright_api/page.rb +1196 -1176
- data/lib/playwright_api/page_assertions.rb +60 -0
- data/lib/playwright_api/playwright.rb +54 -122
- data/lib/playwright_api/request.rb +112 -74
- data/lib/playwright_api/response.rb +92 -20
- data/lib/playwright_api/route.rb +152 -62
- data/lib/playwright_api/selectors.rb +47 -61
- data/lib/playwright_api/touchscreen.rb +8 -2
- data/lib/playwright_api/tracing.rb +128 -0
- data/lib/playwright_api/web_socket.rb +43 -5
- data/lib/playwright_api/worker.rb +74 -34
- data/playwright.gemspec +14 -9
- data/sig/playwright.rbs +658 -0
- metadata +216 -50
- data/docs/api_coverage.md +0 -354
- data/lib/playwright/channel_owners/chromium_browser.rb +0 -8
- data/lib/playwright/channel_owners/chromium_browser_context.rb +0 -8
- data/lib/playwright/channel_owners/console_message.rb +0 -21
- data/lib/playwright/channel_owners/firefox_browser.rb +0 -8
- data/lib/playwright/channel_owners/selectors.rb +0 -4
- data/lib/playwright/channel_owners/webkit_browser.rb +0 -8
- data/lib/playwright/input_type.rb +0 -19
- data/lib/playwright/input_types/mouse.rb +0 -4
- data/lib/playwright/input_types/touchscreen.rb +0 -4
- data/lib/playwright/javascript/function.rb +0 -67
- data/lib/playwright/wait_helper.rb +0 -73
- data/lib/playwright_api/accessibility.rb +0 -93
- data/lib/playwright_api/binding_call.rb +0 -23
- data/lib/playwright_api/chromium_browser_context.rb +0 -57
- data/lib/playwright_api/video.rb +0 -24
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 3
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Capybara driver for Ruby on Rails application
|
|
6
|
+
|
|
7
|
+
`playwright-ruby-client` is a client library just for browser automation, while Rails uses [Capybara](https://github.com/teamcapybara/capybara) for system testing.
|
|
8
|
+
|
|
9
|
+
`capybara-playwright-driver` provides a [Capybara](https://github.com/teamcapybara/capybara) driver based on playwright-ruby-client and makes it easy to integrate into Ruby on Rails applications.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Add the line below into Gemfile:
|
|
14
|
+
|
|
15
|
+
```rb
|
|
16
|
+
gem 'capybara-playwright-driver'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
and then `bundle install`.
|
|
20
|
+
|
|
21
|
+
Note that capybara-playwright-driver does not depend on Selenium. But `selenium-webdriver` is also required [on Rails 5.x, 6.0](https://github.com/rails/rails/pull/39179)
|
|
22
|
+
|
|
23
|
+
## Register and configure Capybara driver
|
|
24
|
+
|
|
25
|
+
```rb
|
|
26
|
+
Capybara.register_driver(:customized_playwright) do |app|
|
|
27
|
+
Capybara::Playwright::Driver.new(app,
|
|
28
|
+
browser_type: :chromium, # :chromium (default) or :firefox, :webkit
|
|
29
|
+
headless: false, # true for headless mode (default), false for headful mode.
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
:::note
|
|
35
|
+
|
|
36
|
+
Rails itself (since Rails 6.1) reserves the driver name `:playwright` for its built‑in integration (see [rails/rails#39987](https://github.com/rails/rails/issues/39987) and [YusukeIwaki/capybara-playwright-driver#93](https://github.com/YusukeIwaki/capybara-playwright-driver/issues/93)). **If you call `Capybara.register_driver(:playwright) { ... }` and then use `driven_by :playwright`, Rails' built‑in Playwright driver will be selected** instead of the one defined by `Capybara.register_driver(:playwright) { ... }`. Therefore, when you want to use the driver from this gem with custom options, register it with another name such as `:customized_playwright`.
|
|
37
|
+
|
|
38
|
+
:::
|
|
39
|
+
|
|
40
|
+
### When running Playwright in a container
|
|
41
|
+
|
|
42
|
+
If Playwright is running in an independent container, with docker-compose.yaml config like this
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
playwright: # this is our PLAYWRIGHT_HOST value
|
|
46
|
+
image: mcr.microsoft.com/playwright:v1.57.0-noble
|
|
47
|
+
command: >
|
|
48
|
+
/bin/sh -c "npx -y playwright@1.57.0 run-server --port 3000 --host 0.0.0.0 --path /ws"
|
|
49
|
+
init: true
|
|
50
|
+
restart: unless-stopped
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Configure capybara to use the `browser_server_endpoint_url`
|
|
54
|
+
|
|
55
|
+
```rb
|
|
56
|
+
Capybara.register_driver(:playwright_remote) do |app|
|
|
57
|
+
Capybara::Playwright::Driver.new(
|
|
58
|
+
app,
|
|
59
|
+
browser_type: :chromium,
|
|
60
|
+
headless: true,
|
|
61
|
+
browser_server_endpoint_url: "ws://#{ENV.fetch('PLAYWRIGHT_HOST')}:3000/ws"
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Update timeout
|
|
67
|
+
|
|
68
|
+
Capybara sets the default value of timeout to _2 seconds_. Generally it is too short to wait for HTTP responses.
|
|
69
|
+
|
|
70
|
+
It is recommended to set the timeout to 15-30 seconds for Playwright driver.
|
|
71
|
+
|
|
72
|
+
```rb
|
|
73
|
+
Capybara.default_max_wait_time = 15
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### (Optional) Update default driver
|
|
77
|
+
|
|
78
|
+
By default, Capybara driver is set to `:rack_test`, which works only with non-JS contents. If your Rails application has many JavaScript contents, it is recommended to change the default driver to `:playwright`.
|
|
79
|
+
|
|
80
|
+
```rb
|
|
81
|
+
Capybara.default_driver = :playwright
|
|
82
|
+
Capybara.javascript_driver = :playwright
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
If you registered a customized driver (e.g. `:customized_playwright` as above) and want that to be the default, set:
|
|
86
|
+
|
|
87
|
+
```rb
|
|
88
|
+
Capybara.default_driver = :customized_playwright
|
|
89
|
+
Capybara.javascript_driver = :customized_playwright
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Remember: choosing `:playwright` here will use Rails' built‑in driver, not the customized one from this gem.
|
|
93
|
+
|
|
94
|
+
It is not mandatory. Without changing the default driver, you can still use the customized Playwright driver by specifying `Capybara.current_driver = :customized_playwright` (or `driven_by :customized_playwright` in system spec) explicitly. Use `:playwright` only if you intend to run against Rails' built‑in Playwright driver (see issues: https://github.com/YusukeIwaki/capybara-playwright-driver/issues/93, https://github.com/rails/rails/issues/39987).
|
|
95
|
+
|
|
96
|
+
### (reference) Available driver options
|
|
97
|
+
|
|
98
|
+
These parameters can be passed into `Capybara::Playwright::Driver.new`
|
|
99
|
+
|
|
100
|
+
- `playwright_cli_executable_path`
|
|
101
|
+
- Refer [this article](./download_playwright_driver) to understand what to specify.
|
|
102
|
+
- `browser_type`
|
|
103
|
+
- `:chromium` (default), `:firefox`, or `:webkit`
|
|
104
|
+
- Parameters for [Playwright::BrowserType#launch](/docs/api/browser_type#launch)
|
|
105
|
+
- args
|
|
106
|
+
- channel
|
|
107
|
+
- `chrome`, `msedge`, `chrome-beta`, `chrome-dev`, `chrome-canary`, `msedge-beta`, `msedge-dev` Browser distribution channel. Read more about using [Google Chrome & Microsoft Edge](https://playwright.dev/docs/browsers#google-chrome--microsoft-edge)
|
|
108
|
+
- devtools
|
|
109
|
+
- downloadsPath
|
|
110
|
+
- env
|
|
111
|
+
- executablePath
|
|
112
|
+
- firefoxUserPrefs
|
|
113
|
+
- headless
|
|
114
|
+
- ignoreDefaultArgs
|
|
115
|
+
- proxy
|
|
116
|
+
- slowMo
|
|
117
|
+
- timeout
|
|
118
|
+
- Parameters for [Playwright::Browser#new_context](/docs/api/browser#new_context)
|
|
119
|
+
- bypassCSP
|
|
120
|
+
- colorScheme
|
|
121
|
+
- deviceScaleFactor
|
|
122
|
+
- extraHTTPHeaders
|
|
123
|
+
- geolocation
|
|
124
|
+
- hasTouch
|
|
125
|
+
- httpCredentials
|
|
126
|
+
- ignoreHTTPSErrors
|
|
127
|
+
- isMobile
|
|
128
|
+
- javaScriptEnabled
|
|
129
|
+
- locale
|
|
130
|
+
- noViewport
|
|
131
|
+
- offline
|
|
132
|
+
- permissions
|
|
133
|
+
- proxy
|
|
134
|
+
- record_har_omit_content
|
|
135
|
+
- record_har_path
|
|
136
|
+
- record_video_dir
|
|
137
|
+
- record_video_size
|
|
138
|
+
- screen
|
|
139
|
+
- serviceWorkers
|
|
140
|
+
- storageState
|
|
141
|
+
- timezoneId
|
|
142
|
+
- userAgent
|
|
143
|
+
- viewport
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
driver_opts = {
|
|
147
|
+
# `playwright` command path.
|
|
148
|
+
playwright_cli_executable_path: './node_modules/.bin/playwright',
|
|
149
|
+
|
|
150
|
+
# Use firefox for testing.
|
|
151
|
+
browser_type: :firefox,
|
|
152
|
+
|
|
153
|
+
# Headful mode.
|
|
154
|
+
headless: false,
|
|
155
|
+
|
|
156
|
+
# Slower operation
|
|
157
|
+
slowMo: 50, # integer. (50-100 would be good for most cases)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
Capybara::Playwright::Driver.new(app, driver_opts)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Available functions and Limitations
|
|
164
|
+
|
|
165
|
+
### Capybara DSL
|
|
166
|
+
|
|
167
|
+
Most of the methods of `Capybara::Session` and `Capybara::Node::Element` are available. However the following method is not yet implemented.
|
|
168
|
+
|
|
169
|
+
- `Capybara::Node::Element#drop`
|
|
170
|
+
|
|
171
|
+
### Playwright-native scripting
|
|
172
|
+
|
|
173
|
+
We can also describe Playwright-native automation script using `with_playwright_page` and `with_playwright_element_handle`.
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
# With Capybara DSL
|
|
177
|
+
find('a[data-item-type="global_search"]').click
|
|
178
|
+
|
|
179
|
+
# With Playwright-native Page
|
|
180
|
+
Capybara.current_session.driver.with_playwright_page do |page|
|
|
181
|
+
# `page` is an instance of Playwright::Page.
|
|
182
|
+
page.click('a[data-item-type="global_search"]')
|
|
183
|
+
end
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
all('.list-item').each do |li|
|
|
188
|
+
# With Capybara::Node::Element method
|
|
189
|
+
puts li.all('a').first.text
|
|
190
|
+
|
|
191
|
+
# With Playwright-native ElementHandle
|
|
192
|
+
puts li.with_playwright_element_handle do |handle|
|
|
193
|
+
# `handle` is an instance of Playwright::ElementHandle
|
|
194
|
+
handle.query_selector('a').text_content
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Generally, Capybara DSL seems simple, but Playwright-native scripting are more precise and efficient. Also `waitForNavigation`, `waitForSelector`, and many other Playwright functions are available with Playwright-native scripting.
|
|
200
|
+
|
|
201
|
+
### Screen recording
|
|
202
|
+
|
|
203
|
+
NO NEED to keep sitting in front of screen during test. Just record what happened with video.
|
|
204
|
+
|
|
205
|
+
For example, we can store the video for [Allure report](https://github.com/allure-framework/allure-ruby) as below:
|
|
206
|
+
|
|
207
|
+
```ruby
|
|
208
|
+
before do |example|
|
|
209
|
+
Capybara.current_session.driver.on_save_screenrecord do |video_path|
|
|
210
|
+
Allure.add_attachment(
|
|
211
|
+
name: "screenrecord - #{example.description}",
|
|
212
|
+
source: File.read(video_path),
|
|
213
|
+
type: Allure::ContentType::WEBM,
|
|
214
|
+
test_case: true,
|
|
215
|
+
)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+

|
|
221
|
+
|
|
222
|
+
For more details, refer [Recording video](./recording_video.md#using-screen-recording-from-capybara-driver)
|
|
223
|
+
|
|
224
|
+
### Screenshot just before teardown
|
|
225
|
+
|
|
226
|
+
In addition to `Capybara::Session#save_screenshot`, capybara-playwright-driver have another method for storing last screen state just before teardown.
|
|
227
|
+
|
|
228
|
+
For example, we can attach the screenshot for [Allure report](https://github.com/allure-framework/allure-ruby) as below:
|
|
229
|
+
|
|
230
|
+
```ruby
|
|
231
|
+
before do |example|
|
|
232
|
+
Capybara.current_session.driver.on_save_raw_screenshot_before_reset do |raw_screenshot|
|
|
233
|
+
Allure.add_attachment(
|
|
234
|
+
name: "screenshot - #{example.description}",
|
|
235
|
+
source: raw_screenshot,
|
|
236
|
+
type: Allure::ContentType::PNG,
|
|
237
|
+
test_case: true,
|
|
238
|
+
)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Tracing for postmortem
|
|
244
|
+
|
|
245
|
+
Playwright users often expect to see what happened in the browser when the test failed. `capybara-playwright-driver` provides a feature to store the trace easily.
|
|
246
|
+
|
|
247
|
+
```ruby
|
|
248
|
+
before do |example|
|
|
249
|
+
Capybara.current_session.driver.on_save_trace do |trace_zip_path|
|
|
250
|
+
Allure.add_attachment(
|
|
251
|
+
name: "trace - #{example.description}",
|
|
252
|
+
source: File.read(trace_zip_path),
|
|
253
|
+
type: 'application/zip',
|
|
254
|
+
test_case: true,
|
|
255
|
+
)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
This example code would attach the trace zip file to Allure report for each test case.
|
|
261
|
+
|
|
262
|
+

|
|
263
|
+
|
|
264
|
+
We can download and show the trace with `playwright show-trace` command.
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
npx playwright show-trace ababcdcdefef.zip
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+

|
|
271
|
+
|
|
272
|
+
Instead of the easy configuration using `on_save_trace`, we can also use `page.driver.start_tracing` / `page.driver.stop_tracing` or `page.driver.with_trace do { ... }` for storing the trace with detailed options. See [the PR](https://github.com/YusukeIwaki/capybara-playwright-driver/pull/66) for more details including example codes for RSpec.
|
|
273
|
+
|
|
274
|
+
### Limitations
|
|
275
|
+
|
|
276
|
+
- Playwright doesn't allow clicking invisible DOM elements or moving elements. `click` sometimes doesn't work as Selenium does. See the detail in https://playwright.dev/docs/actionability/
|
|
277
|
+
- `current_window.maximize` and `current_window.fullscreen` work only on headful (non-headless) mode, as selenium driver does.
|
|
278
|
+
- `Capybara::Node::Element#drag_to` does not accept `html5` parameter. HTML5 drag and drop is not fully supported in Playwright.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 4
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Use Capybara without DSL
|
|
6
|
+
|
|
7
|
+
:::note
|
|
8
|
+
|
|
9
|
+
This article shows advanced-level configuration of Capybara and RSpec for more accurate automation/testing.
|
|
10
|
+
If you want to just integrate Playwright into Rails application, refer the basic [configuration guide](./rails_integration)
|
|
11
|
+
:::
|
|
12
|
+
|
|
13
|
+
## Background
|
|
14
|
+
|
|
15
|
+
[capybara-playwright-driver](./rails_integration) is easy to configure and migrate from Selenium or another Capybara driver, however it is a little **inaccurate** and would sometimes cause 'flaky test' problem originated from the internal implementation of Capybara DSL.
|
|
16
|
+
|
|
17
|
+
Also **we cannot use most of useful Playwright features in Capybara driver**, such as auto-waiting, various kind of selectors, and some users would want to use Playwright features as it is without Capybara DSL.
|
|
18
|
+
|
|
19
|
+
This article shows how to use playwright-ruby-client without Capybara DSL in Rails and RSpec.
|
|
20
|
+
|
|
21
|
+
## Configure Capybara driver just for launching Rails server
|
|
22
|
+
|
|
23
|
+
Capybara prepares the test server only when the configured driver returns true on `needs_server?` method. So we have to implement minimum driver like this:
|
|
24
|
+
|
|
25
|
+
```ruby {5-7} title=spec/support/capybara_null_driver.rb
|
|
26
|
+
RSpec.configure do |config|
|
|
27
|
+
require 'capybara'
|
|
28
|
+
|
|
29
|
+
class CapybaraNullDriver < Capybara::Driver::Base
|
|
30
|
+
def needs_server?
|
|
31
|
+
true
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Capybara.register_driver(:null) { CapybaraNullDriver.new }
|
|
36
|
+
|
|
37
|
+
...
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Launch browser on each test
|
|
42
|
+
|
|
43
|
+
Now Capybara DSL is unavailable with CapybaraNullDriver, we have to manually launch browsers using playwright-ruby-client.
|
|
44
|
+
|
|
45
|
+
```rb
|
|
46
|
+
RSpec.configure do |config|
|
|
47
|
+
require 'capybara'
|
|
48
|
+
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
require 'playwright'
|
|
52
|
+
|
|
53
|
+
config.around(driver: :null) do |example|
|
|
54
|
+
Capybara.current_driver = :null
|
|
55
|
+
|
|
56
|
+
# Rails server is launched here, at the first time of accessing Capybara.current_session.server
|
|
57
|
+
base_url = Capybara.current_session.server.base_url
|
|
58
|
+
|
|
59
|
+
Playwright.create(playwright_cli_executable_path: './node_modules/.bin/playwright') do |playwright|
|
|
60
|
+
# pass any option for Playwright#launch and Browser#new_page as you prefer.
|
|
61
|
+
playwright.chromium.launch(headless: false) do |browser|
|
|
62
|
+
@playwright_page = browser.new_page(baseURL: base_url)
|
|
63
|
+
example.run
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
With the configuration above, we can describe system-test codes with native Playwright methods like below:
|
|
71
|
+
|
|
72
|
+
```rb
|
|
73
|
+
require 'rails_helper'
|
|
74
|
+
|
|
75
|
+
describe 'example', driver: :null do
|
|
76
|
+
let!(:user) { FactoryBot.create(:user) }
|
|
77
|
+
let(:page) { @playwright_page }
|
|
78
|
+
|
|
79
|
+
it 'can browse' do
|
|
80
|
+
page.goto("/tests/#{user.id}")
|
|
81
|
+
page.wait_for_selector('input').type('hoge')
|
|
82
|
+
page.keyboard.press('Enter')
|
|
83
|
+
expect(page.text_content('#content')).to include('hoge')
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Minitest Usage
|
|
89
|
+
|
|
90
|
+
We can do something similar with the default Rails setup using Minitest. Here's the same example written with Minitest:
|
|
91
|
+
|
|
92
|
+
```rb
|
|
93
|
+
# test/application_system_test_case.rb
|
|
94
|
+
|
|
95
|
+
require 'playwright'
|
|
96
|
+
|
|
97
|
+
class CapybaraNullDriver < Capybara::Driver::Base
|
|
98
|
+
def needs_server?
|
|
99
|
+
true
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
Capybara.register_driver(:null) { CapybaraNullDriver.new }
|
|
104
|
+
|
|
105
|
+
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
|
106
|
+
driven_by :null
|
|
107
|
+
|
|
108
|
+
def self.playwright
|
|
109
|
+
@playwright ||= Playwright.create(playwright_cli_executable_path: Rails.root.join("node_modules/.bin/playwright"))
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def before_setup
|
|
113
|
+
super
|
|
114
|
+
base_url = Capybara.current_session.server.base_url
|
|
115
|
+
@playwright_browser = self.class.playwright.playwright.chromium.launch(headless: false)
|
|
116
|
+
@playwright_page = @playwright_browser.new_page(baseURL: base_url)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def after_teardown
|
|
120
|
+
super
|
|
121
|
+
@browser.close
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
And here is the same test:
|
|
127
|
+
|
|
128
|
+
```rb
|
|
129
|
+
require "application_system_test_case"
|
|
130
|
+
|
|
131
|
+
class ExampleTest < ApplicationSystemTestCase
|
|
132
|
+
def setup
|
|
133
|
+
@user = User.create!
|
|
134
|
+
@page = @playwright_page
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
test 'can browse' do
|
|
138
|
+
@page.goto("/tests/#{user.id}")
|
|
139
|
+
@page.wait_for_selector('input').type('hoge')
|
|
140
|
+
@page.keyboard.press('Enter')
|
|
141
|
+
|
|
142
|
+
assert @page.text_content('#content').include?('hoge')
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
```
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 10
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Recording video
|
|
6
|
+
|
|
7
|
+
Playwright allows us to record the browser screen during automation.
|
|
8
|
+
https://playwright.dev/docs/videos
|
|
9
|
+
|
|
10
|
+
With this awesome feature, NO NEED to keep our attention focused on the screen during the automation :)
|
|
11
|
+
|
|
12
|
+
```ruby {7,11-12,15-16}
|
|
13
|
+
require 'tmpdir'
|
|
14
|
+
|
|
15
|
+
playwright.chromium.launch do |browser|
|
|
16
|
+
Dir.mktmpdir do |tmp|
|
|
17
|
+
video_path = nil
|
|
18
|
+
|
|
19
|
+
browser.new_context(record_video_dir: tmp) do |context|
|
|
20
|
+
page = context.new_page
|
|
21
|
+
# play with page
|
|
22
|
+
|
|
23
|
+
# NOTE: Page#video is available **only when browser context is alive.**
|
|
24
|
+
video_path = page.video.path
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# NOTE: video is completely saved **only after browser context is closed.**
|
|
28
|
+
handle_video_as_you_like(video_path)
|
|
29
|
+
end
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Specify where to put videos
|
|
33
|
+
|
|
34
|
+
Playwright puts videos on the directory specified at `record_video_dir`.
|
|
35
|
+
|
|
36
|
+
The previous example uses [Dir#mktmpdir](https://docs.ruby-lang.org/ja/latest/method/Dir/s/mktmpdir.html) for storing videos into a temporary directory. Also we simply specify a relative or absolute path like `./my_videos/` or `/path/to/videos`.
|
|
37
|
+
|
|
38
|
+
## Getting video path and recorded video
|
|
39
|
+
|
|
40
|
+
This is really confusing for beginners, but in Playwright
|
|
41
|
+
|
|
42
|
+
* We can get the video path **only when page is alive (before calling BrowserContext#close or Page#close)**
|
|
43
|
+
* We can acquire the completely saved video **only after calling BrowserContext#close**
|
|
44
|
+
|
|
45
|
+
So in most cases, we have to store the video path in advance, and handle the saved video after BrowserContext is closed, as shown in the previous example code.
|
|
46
|
+
|
|
47
|
+
### Using `video#save_as(path)`
|
|
48
|
+
|
|
49
|
+
If you want to just save video to somewhere without handling the video using `File.open`, you can simply use `video#save_as(path_to_save)`.
|
|
50
|
+
|
|
51
|
+
```ruby {5,8,12-13}
|
|
52
|
+
require 'tmpdir'
|
|
53
|
+
|
|
54
|
+
playwright.chromium.launch do |browser|
|
|
55
|
+
Dir.mktmpdir do |tmp|
|
|
56
|
+
page = nil
|
|
57
|
+
|
|
58
|
+
browser.new_context(record_video_dir: tmp) do |context|
|
|
59
|
+
page = context.new_page
|
|
60
|
+
# play with page
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# NOTE: video is completely saved **only after browser context is closed.**
|
|
64
|
+
page.video.save_as('my-important-video.webm')
|
|
65
|
+
end
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Using screen recording from Capybara driver
|
|
69
|
+
|
|
70
|
+
capybara-playwright-driver exposes a function to store the video.
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
Capybara.current_session.driver.on_save_screenrecord do |video_path|
|
|
74
|
+
# Handling recorded video here.
|
|
75
|
+
# video_path is like '/var/folders/xx/xxxxxxxxxx_xxxxxxxx/T/xxxxxxx-xxxxx-xxxxxxxx/e6bde41c5d05b2a02344b058bf1bfea2.webm'
|
|
76
|
+
end
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
With this callback registration, we can record the videos without specifying `record_video_dir` explicitly or preparing a temporary directory. capybara-playwright-driver automatically prepare and set `record_video_dir` internally.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 5
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Web-First assertions for RSpec
|
|
6
|
+
|
|
7
|
+
Playwright introduces clever assertions for E2E testing, so called [web-first assertions](https://playwright.dev/docs/test-assertions).
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
it 'should show username after login' do
|
|
11
|
+
page.fill('input[name="username"]', 'playwright')
|
|
12
|
+
page.fill('input[name="password"]', 'password123')
|
|
13
|
+
page.expect_navigation do
|
|
14
|
+
page.locator('button[type="submit"]').click
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
dashboard_container = page.locator('.dashboard')
|
|
18
|
+
|
|
19
|
+
# Not web-first assertion
|
|
20
|
+
expect(dashboard_container.text_content).to include('Hi, playwright!')
|
|
21
|
+
|
|
22
|
+
# Web-first assertion
|
|
23
|
+
expect(dashboard_container).to have_text('Hi, playwright!')
|
|
24
|
+
end
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The spec above have 2 similar expectations. The first one is a normal assertion, which is not web-first. The second one is a web-first assertion, which is introduced by Playwright.
|
|
28
|
+
|
|
29
|
+
Imagine the case that 'Hi, playwright!' is shown after loading some data from API server. In this case, the first assertion may fail because 'Hi, playwright!' is not present soon after login. On the other hand, the second assertion automatically waits for the 'Hi, playwright!' to be shown.
|
|
30
|
+
|
|
31
|
+
## Configure
|
|
32
|
+
|
|
33
|
+
For avoiding matcher name conflicts, web-first assertions are not loaded by default. To enable web-first assertions, we have to configure RSpec as below:
|
|
34
|
+
|
|
35
|
+
```ruby title=spec/support/web_first_assertion.rb
|
|
36
|
+
require 'playwright/test'
|
|
37
|
+
|
|
38
|
+
RSpec.configure do |config|
|
|
39
|
+
# include web-first assertions just for feature specs.
|
|
40
|
+
config.include Playwright::Test::Matchers, type: :feature
|
|
41
|
+
end
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If you want to use web-first assertions only for some specs, you can include `Playwright::Test::Matchers` in the spec file directly.
|
|
45
|
+
|
|
46
|
+
```ruby title=spec/system/example_spec.rb
|
|
47
|
+
require 'rails_helper'
|
|
48
|
+
require 'playwright/test'
|
|
49
|
+
|
|
50
|
+
describe 'example' do
|
|
51
|
+
include Playwright::Test::Matchers
|
|
52
|
+
|
|
53
|
+
it 'should work' do
|
|
54
|
+
...
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Matchers
|
|
58
|
+
|
|
59
|
+
Please refer to [API doc](/docs/api/locator_assertions).
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 20
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Semi-automation
|
|
6
|
+
|
|
7
|
+
Playwright Browser context is isolated and not persisted by default. But we can also use persistent browser context using [BrowserType#launch_persistent_context](/docs/api/browser_type#launch_persistent_context).
|
|
8
|
+
This allows us to intervene in automation, for example
|
|
9
|
+
|
|
10
|
+
* Authenticate with OAuth2 manually before automation
|
|
11
|
+
* Testing a page after some chrome extensions are installed manually
|
|
12
|
+
|
|
13
|
+
Keep in mind repeatedly that persistent browser context is NOT RECOMMENDED for most cases because it would bring many side effects. Consider [reusing cookie and local storage](./use_storage_state) when you just want to keep authenticated across browser contexts.
|
|
14
|
+
|
|
15
|
+
## Pause automation for manual operation
|
|
16
|
+
|
|
17
|
+
We can simply use `binding.pry` (with `pry-byebug` installed).
|
|
18
|
+
|
|
19
|
+
```ruby {4}
|
|
20
|
+
playwright.chromium.launch_persistent_context('./data/', headless: false) do |context|
|
|
21
|
+
page = context.new_page
|
|
22
|
+
page.goto('https://example.com/')
|
|
23
|
+
binding.pry
|
|
24
|
+
end
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
When script is executed, it is paused as below.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
3:
|
|
31
|
+
4: playwright.chromium.launch_persistent_context('./data/', headless: false) do |context|
|
|
32
|
+
5: page = context.new_page
|
|
33
|
+
6: page.goto('https://example.com/')
|
|
34
|
+
=> 7: binding.pry
|
|
35
|
+
8: end
|
|
36
|
+
|
|
37
|
+
[1] pry(main)>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
We can inspect using `page`, `context` and also we can operate something manually during the pause.
|
|
41
|
+
|
|
42
|
+
See https://github.com/deivid-rodriguez/pry-byebug for more detailed debugging options.
|
|
43
|
+
|
|
44
|
+
## Working with Chrome extensions
|
|
45
|
+
|
|
46
|
+
**Playwright disables the Chrome extension feature by default.**
|
|
47
|
+
We have to enable it for installing Chrome extension, by passing these 3 parameters on launch.
|
|
48
|
+
|
|
49
|
+
* `acceptDownloads: true`
|
|
50
|
+
* `headless: false`
|
|
51
|
+
* `ignoreDefaultArgs: ['--disable-extensions']`
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
require 'playwright'
|
|
55
|
+
require 'pry'
|
|
56
|
+
|
|
57
|
+
Playwright.create(playwright_cli_executable_path: './node_modules/.bin/playwright') do |playwright|
|
|
58
|
+
launch_params = {
|
|
59
|
+
acceptDownloads: true,
|
|
60
|
+
channel: 'chrome',
|
|
61
|
+
headless: false,
|
|
62
|
+
ignoreDefaultArgs: ['--disable-extensions'],
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
playwright.chromium.launch_persistent_context('./data/', **launch_params) do |context|
|
|
66
|
+
page = context.new_page
|
|
67
|
+
page.goto('https://example.com/')
|
|
68
|
+
binding.pry
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
```
|