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.
Files changed (209) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +4 -0
  3. data/CLAUDE/api_generation.md +28 -0
  4. data/CLAUDE/ci_expectations.md +23 -0
  5. data/CLAUDE/gem_release_flow.md +39 -0
  6. data/CLAUDE/past_upgrade_pr_patterns.md +42 -0
  7. data/CLAUDE/playwright_upgrade_workflow.md +35 -0
  8. data/CLAUDE/rspec_debugging.md +30 -0
  9. data/CLAUDE/unimplemented_examples.md +18 -0
  10. data/CLAUDE.md +32 -0
  11. data/CONTRIBUTING.md +5 -0
  12. data/README.md +60 -16
  13. data/documentation/README.md +33 -0
  14. data/documentation/babel.config.js +3 -0
  15. data/documentation/docs/api/api_request.md +7 -0
  16. data/documentation/docs/api/api_request_context.md +298 -0
  17. data/documentation/docs/api/api_response.md +114 -0
  18. data/documentation/docs/api/browser.md +237 -0
  19. data/documentation/docs/api/browser_context.md +503 -0
  20. data/documentation/docs/api/browser_type.md +184 -0
  21. data/documentation/docs/api/cdp_session.md +44 -0
  22. data/documentation/docs/api/clock.md +154 -0
  23. data/documentation/docs/api/console_message.md +85 -0
  24. data/documentation/docs/api/dialog.md +84 -0
  25. data/documentation/docs/api/download.md +111 -0
  26. data/documentation/docs/api/element_handle.md +694 -0
  27. data/documentation/docs/api/experimental/_category_.yml +3 -0
  28. data/documentation/docs/api/experimental/android.md +42 -0
  29. data/documentation/docs/api/experimental/android_device.md +109 -0
  30. data/documentation/docs/api/experimental/android_input.md +43 -0
  31. data/documentation/docs/api/experimental/android_socket.md +7 -0
  32. data/documentation/docs/api/experimental/android_web_view.md +7 -0
  33. data/documentation/docs/api/file_chooser.md +53 -0
  34. data/documentation/docs/api/frame.md +1218 -0
  35. data/documentation/docs/api/frame_locator.md +348 -0
  36. data/documentation/docs/api/js_handle.md +121 -0
  37. data/documentation/docs/api/keyboard.md +170 -0
  38. data/documentation/docs/api/locator.md +1495 -0
  39. data/documentation/docs/api/locator_assertions.md +827 -0
  40. data/documentation/docs/api/mouse.md +86 -0
  41. data/documentation/docs/api/page.md +1946 -0
  42. data/documentation/docs/api/page_assertions.md +65 -0
  43. data/documentation/docs/api/playwright.md +66 -0
  44. data/documentation/docs/api/request.md +255 -0
  45. data/documentation/docs/api/response.md +176 -0
  46. data/documentation/docs/api/route.md +205 -0
  47. data/documentation/docs/api/selectors.md +63 -0
  48. data/documentation/docs/api/touchscreen.md +22 -0
  49. data/documentation/docs/api/tracing.md +129 -0
  50. data/documentation/docs/api/web_socket.md +51 -0
  51. data/documentation/docs/api/worker.md +83 -0
  52. data/documentation/docs/article/api_coverage.mdx +11 -0
  53. data/documentation/docs/article/getting_started.md +161 -0
  54. data/documentation/docs/article/guides/_category_.yml +3 -0
  55. data/documentation/docs/article/guides/download_playwright_driver.md +55 -0
  56. data/documentation/docs/article/guides/inspector.md +31 -0
  57. data/documentation/docs/article/guides/launch_browser.md +121 -0
  58. data/documentation/docs/article/guides/playwright_on_alpine_linux.md +112 -0
  59. data/documentation/docs/article/guides/rails_integration.md +278 -0
  60. data/documentation/docs/article/guides/rails_integration_with_null_driver.md +145 -0
  61. data/documentation/docs/article/guides/recording_video.md +79 -0
  62. data/documentation/docs/article/guides/rspec_integration.md +59 -0
  63. data/documentation/docs/article/guides/semi_automation.md +71 -0
  64. data/documentation/docs/article/guides/use_storage_state.md +78 -0
  65. data/documentation/docs/include/api_coverage.md +671 -0
  66. data/documentation/docusaurus.config.js +114 -0
  67. data/documentation/package.json +39 -0
  68. data/documentation/sidebars.js +15 -0
  69. data/documentation/src/components/HomepageFeatures.js +61 -0
  70. data/documentation/src/components/HomepageFeatures.module.css +13 -0
  71. data/documentation/src/css/custom.css +44 -0
  72. data/documentation/src/pages/index.js +49 -0
  73. data/documentation/src/pages/index.module.css +41 -0
  74. data/documentation/src/pages/markdown-page.md +7 -0
  75. data/documentation/static/.nojekyll +0 -0
  76. data/documentation/static/img/playwright-logo.svg +9 -0
  77. data/documentation/static/img/playwright-ruby-client.png +0 -0
  78. data/documentation/static/img/undraw_dropdown_menu.svg +1 -0
  79. data/documentation/static/img/undraw_web_development.svg +1 -0
  80. data/documentation/static/img/undraw_windows.svg +1 -0
  81. data/documentation/yarn.lock +9005 -0
  82. data/lib/playwright/{input_types/android_input.rb → android_input_impl.rb} +5 -1
  83. data/lib/playwright/api_implementation.rb +18 -0
  84. data/lib/playwright/api_response_impl.rb +77 -0
  85. data/lib/playwright/channel.rb +62 -1
  86. data/lib/playwright/channel_owner.rb +70 -7
  87. data/lib/playwright/channel_owners/android.rb +16 -3
  88. data/lib/playwright/channel_owners/android_device.rb +22 -66
  89. data/lib/playwright/channel_owners/api_request_context.rb +247 -0
  90. data/lib/playwright/channel_owners/artifact.rb +40 -0
  91. data/lib/playwright/channel_owners/binding_call.rb +70 -0
  92. data/lib/playwright/channel_owners/browser.rb +114 -22
  93. data/lib/playwright/channel_owners/browser_context.rb +589 -15
  94. data/lib/playwright/channel_owners/browser_type.rb +90 -1
  95. data/lib/playwright/channel_owners/cdp_session.rb +19 -0
  96. data/lib/playwright/channel_owners/dialog.rb +32 -0
  97. data/lib/playwright/channel_owners/element_handle.rb +107 -43
  98. data/lib/playwright/channel_owners/fetch_request.rb +8 -0
  99. data/lib/playwright/channel_owners/frame.rb +334 -104
  100. data/lib/playwright/channel_owners/js_handle.rb +9 -13
  101. data/lib/playwright/channel_owners/local_utils.rb +82 -0
  102. data/lib/playwright/channel_owners/page.rb +778 -95
  103. data/lib/playwright/channel_owners/playwright.rb +25 -30
  104. data/lib/playwright/channel_owners/request.rb +120 -18
  105. data/lib/playwright/channel_owners/response.rb +113 -0
  106. data/lib/playwright/channel_owners/route.rb +181 -0
  107. data/lib/playwright/channel_owners/stream.rb +30 -0
  108. data/lib/playwright/channel_owners/tracing.rb +117 -0
  109. data/lib/playwright/channel_owners/web_socket.rb +96 -0
  110. data/lib/playwright/channel_owners/worker.rb +46 -0
  111. data/lib/playwright/channel_owners/writable_stream.rb +14 -0
  112. data/lib/playwright/clock_impl.rb +67 -0
  113. data/lib/playwright/connection.rb +111 -63
  114. data/lib/playwright/console_message_impl.rb +29 -0
  115. data/lib/playwright/download_impl.rb +32 -0
  116. data/lib/playwright/errors.rb +42 -5
  117. data/lib/playwright/event_emitter.rb +17 -3
  118. data/lib/playwright/event_emitter_proxy.rb +49 -0
  119. data/lib/playwright/events.rb +10 -5
  120. data/lib/playwright/file_chooser_impl.rb +24 -0
  121. data/lib/playwright/frame_locator_impl.rb +66 -0
  122. data/lib/playwright/har_router.rb +89 -0
  123. data/lib/playwright/http_headers.rb +14 -0
  124. data/lib/playwright/input_files.rb +102 -15
  125. data/lib/playwright/javascript/expression.rb +7 -11
  126. data/lib/playwright/javascript/regex.rb +23 -0
  127. data/lib/playwright/javascript/source_url.rb +16 -0
  128. data/lib/playwright/javascript/value_parser.rb +108 -19
  129. data/lib/playwright/javascript/value_serializer.rb +47 -8
  130. data/lib/playwright/javascript/visitor_info.rb +26 -0
  131. data/lib/playwright/javascript.rb +2 -10
  132. data/lib/playwright/{input_types/keyboard.rb → keyboard_impl.rb} +6 -2
  133. data/lib/playwright/locator_assertions_impl.rb +571 -0
  134. data/lib/playwright/locator_impl.rb +544 -0
  135. data/lib/playwright/locator_utils.rb +136 -0
  136. data/lib/playwright/mouse_impl.rb +57 -0
  137. data/lib/playwright/page_assertions_impl.rb +154 -0
  138. data/lib/playwright/playwright_api.rb +102 -30
  139. data/lib/playwright/raw_headers.rb +61 -0
  140. data/lib/playwright/route_handler.rb +78 -0
  141. data/lib/playwright/select_option_values.rb +34 -13
  142. data/lib/playwright/selectors_impl.rb +45 -0
  143. data/lib/playwright/test.rb +102 -0
  144. data/lib/playwright/timeout_settings.rb +9 -4
  145. data/lib/playwright/touchscreen_impl.rb +14 -0
  146. data/lib/playwright/transport.rb +61 -10
  147. data/lib/playwright/url_matcher.rb +24 -2
  148. data/lib/playwright/utils.rb +48 -13
  149. data/lib/playwright/version.rb +2 -1
  150. data/lib/playwright/video.rb +54 -0
  151. data/lib/playwright/waiter.rb +166 -0
  152. data/lib/playwright/web_socket_client.rb +167 -0
  153. data/lib/playwright/web_socket_transport.rb +116 -0
  154. data/lib/playwright.rb +188 -11
  155. data/lib/playwright_api/android.rb +46 -11
  156. data/lib/playwright_api/android_device.rb +182 -31
  157. data/lib/playwright_api/android_input.rb +22 -13
  158. data/lib/playwright_api/android_socket.rb +18 -0
  159. data/lib/playwright_api/android_web_view.rb +24 -0
  160. data/lib/playwright_api/api_request.rb +26 -0
  161. data/lib/playwright_api/api_request_context.rb +311 -0
  162. data/lib/playwright_api/api_response.rb +92 -0
  163. data/lib/playwright_api/browser.rb +116 -103
  164. data/lib/playwright_api/browser_context.rb +290 -389
  165. data/lib/playwright_api/browser_type.rb +96 -118
  166. data/lib/playwright_api/cdp_session.rb +36 -39
  167. data/lib/playwright_api/clock.rb +121 -0
  168. data/lib/playwright_api/console_message.rb +35 -19
  169. data/lib/playwright_api/dialog.rb +53 -50
  170. data/lib/playwright_api/download.rb +49 -43
  171. data/lib/playwright_api/element_handle.rb +354 -402
  172. data/lib/playwright_api/file_chooser.rb +15 -18
  173. data/lib/playwright_api/frame.rb +703 -603
  174. data/lib/playwright_api/frame_locator.rb +285 -0
  175. data/lib/playwright_api/js_handle.rb +50 -76
  176. data/lib/playwright_api/keyboard.rb +67 -146
  177. data/lib/playwright_api/locator.rb +1304 -0
  178. data/lib/playwright_api/locator_assertions.rb +704 -0
  179. data/lib/playwright_api/mouse.rb +23 -29
  180. data/lib/playwright_api/page.rb +1196 -1176
  181. data/lib/playwright_api/page_assertions.rb +60 -0
  182. data/lib/playwright_api/playwright.rb +54 -122
  183. data/lib/playwright_api/request.rb +112 -74
  184. data/lib/playwright_api/response.rb +92 -20
  185. data/lib/playwright_api/route.rb +152 -62
  186. data/lib/playwright_api/selectors.rb +47 -61
  187. data/lib/playwright_api/touchscreen.rb +8 -2
  188. data/lib/playwright_api/tracing.rb +128 -0
  189. data/lib/playwright_api/web_socket.rb +43 -5
  190. data/lib/playwright_api/worker.rb +74 -34
  191. data/playwright.gemspec +14 -9
  192. data/sig/playwright.rbs +658 -0
  193. metadata +216 -50
  194. data/docs/api_coverage.md +0 -354
  195. data/lib/playwright/channel_owners/chromium_browser.rb +0 -8
  196. data/lib/playwright/channel_owners/chromium_browser_context.rb +0 -8
  197. data/lib/playwright/channel_owners/console_message.rb +0 -21
  198. data/lib/playwright/channel_owners/firefox_browser.rb +0 -8
  199. data/lib/playwright/channel_owners/selectors.rb +0 -4
  200. data/lib/playwright/channel_owners/webkit_browser.rb +0 -8
  201. data/lib/playwright/input_type.rb +0 -19
  202. data/lib/playwright/input_types/mouse.rb +0 -4
  203. data/lib/playwright/input_types/touchscreen.rb +0 -4
  204. data/lib/playwright/javascript/function.rb +0 -67
  205. data/lib/playwright/wait_helper.rb +0 -73
  206. data/lib/playwright_api/accessibility.rb +0 -93
  207. data/lib/playwright_api/binding_call.rb +0 -23
  208. data/lib/playwright_api/chromium_browser_context.rb +0 -57
  209. 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
+ ![screenrecord](https://user-images.githubusercontent.com/11763113/121126629-71b5f600-c863-11eb-8f88-7924ab669946.gif)
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
+ ![add trace image](https://user-images.githubusercontent.com/11763113/282307106-520d800b-67ac-4e14-98bb-75a8bbb98a1f.png)
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
+ ![show-trace image](https://user-images.githubusercontent.com/11763113/282307098-a4167c32-d5e7-4631-a3b6-62d278efbeef.png)
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
+ ```