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
@@ -1,7 +1,11 @@
1
+ require_relative '../locator_utils'
2
+
1
3
  module Playwright
2
4
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_frame.py
3
5
  define_channel_owner :Frame do
4
- def after_initialize
6
+ include LocatorUtils
7
+
8
+ private def after_initialize
5
9
  if @initializer['parentFrame']
6
10
  @parent_frame = ChannelOwners::Frame.from(@initializer['parentFrame'])
7
11
  @parent_frame.send(:append_child_frame_from_child, self)
@@ -22,14 +26,36 @@ module Playwright
22
26
  attr_reader :page, :parent_frame
23
27
  attr_writer :detached
24
28
 
29
+ private def _timeout(timeout)
30
+ @page.send(:_timeout_settings).timeout(timeout)
31
+ end
32
+
33
+ private def _navigation_timeout(timeout)
34
+ @page.send(:_timeout_settings).navigation_timeout(timeout)
35
+ end
36
+
25
37
  private def on_load_state(add:, remove:)
26
38
  if add
27
39
  @load_states << add
28
- @event_emitter.emit('loadstate', add)
40
+
41
+ # Original JS version of Playwright emit event here.
42
+ # @event_emitter.emit('loadstate', add)
29
43
  end
30
44
  if remove
31
45
  @load_states.delete(remove)
32
46
  end
47
+ unless @parent_frame
48
+ if add == 'load'
49
+ @page&.emit(Events::Page::Load, @page)
50
+ elsif add == 'domcontentloaded'
51
+ @page&.emit(Events::Page::DOMContentLoaded, @page)
52
+ end
53
+ end
54
+
55
+ # emit to waitForLoadState(load) listeners explicitly after waitForEvent(load) listeners
56
+ if add
57
+ @event_emitter.emit('loadstate', add)
58
+ end
33
59
  end
34
60
 
35
61
  private def on_frame_navigated(event)
@@ -38,14 +64,14 @@ module Playwright
38
64
  @event_emitter.emit('navigated', event)
39
65
 
40
66
  unless event['error']
41
- @page&.emit('framenavigated', self)
67
+ @page&.emit(Events::Page::FrameNavigated, self)
42
68
  end
43
69
  end
44
70
 
45
71
  def goto(url, timeout: nil, waitUntil: nil, referer: nil)
46
72
  params = {
47
73
  url: url,
48
- timeout: timeout,
74
+ timeout: _navigation_timeout(timeout),
49
75
  waitUntil: waitUntil,
50
76
  referer: referer
51
77
  }.compact
@@ -53,35 +79,56 @@ module Playwright
53
79
  ChannelOwners::Response.from_nullable(resp)
54
80
  end
55
81
 
56
- private def setup_navigation_wait_helper(timeout:)
57
- WaitHelper.new.tap do |helper|
58
- helper.reject_on_event(@page, Events::Page::Close, AlreadyClosedError.new)
59
- helper.reject_on_event(@page, Events::Page::Crash, CrashedError.new)
60
- helper.reject_on_event(@page, Events::Page::FrameDetached, FrameAlreadyDetachedError.new)
61
- helper.reject_on_timeout(timeout, "Timeout #{timeout}ms exceeded.")
82
+ class CrashedError < StandardError
83
+ def initialize
84
+ super('Navigation failed because page crashed!')
85
+ end
86
+ end
87
+
88
+ class FrameAlreadyDetachedError < StandardError
89
+ def initialize
90
+ super('Navigating frame was detached!')
91
+ end
92
+ end
93
+
94
+ private def setup_navigation_waiter(wait_name:, timeout_value:)
95
+ Waiter.new(page, wait_name: "frame.#{wait_name}").tap do |waiter|
96
+ waiter.reject_on_event(@page, Events::Page::Close, -> { @page.send(:close_error_with_reason) })
97
+ waiter.reject_on_event(@page, Events::Page::Crash, CrashedError.new)
98
+ waiter.reject_on_event(@page, Events::Page::FrameDetached, FrameAlreadyDetachedError.new, predicate: -> (frame) { frame == self })
99
+ waiter.reject_on_timeout(timeout_value, "Timeout #{timeout_value}ms exceeded.")
62
100
  end
63
101
  end
64
102
 
65
103
  def expect_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
66
104
  option_wait_until = waitUntil || 'load'
67
- option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
105
+ option_timeout = _navigation_timeout(timeout)
68
106
  time_start = Time.now
69
107
 
70
- wait_helper = setup_navigation_wait_helper(timeout: option_timeout)
108
+ waiter = setup_navigation_waiter(wait_name: :expect_navigation, timeout_value: option_timeout)
71
109
 
110
+ to_url = url ? " to \"#{url}\"" : ''
111
+ waiter.log("waiting for navigation#{to_url} until '#{option_wait_until}'")
72
112
  predicate =
73
113
  if url
74
- matcher = UrlMatcher.new(url)
75
- ->(event) { event['error'] || matcher.match?(event['url']) }
114
+ matcher = UrlMatcher.new(url, base_url: @page.context.send(:base_url))
115
+ ->(event) {
116
+ if event['error']
117
+ true
118
+ else
119
+ waiter.log(" navigated to \"#{event['url']}\"")
120
+ matcher.match?(event['url'])
121
+ end
122
+ }
76
123
  else
77
124
  ->(_) { true }
78
125
  end
79
126
 
80
- wait_helper.wait_for_event(@event_emitter, 'navigated', predicate: predicate)
127
+ waiter.wait_for_event(@event_emitter, 'navigated', predicate: predicate)
81
128
 
82
129
  block&.call
83
130
 
84
- event = wait_helper.promise.value!
131
+ event = waiter.result.value!
85
132
  if event['error']
86
133
  raise event['error']
87
134
  end
@@ -98,43 +145,63 @@ module Playwright
98
145
  request&.response
99
146
  end
100
147
 
148
+ def wait_for_url(url, timeout: nil, waitUntil: nil)
149
+ matcher = UrlMatcher.new(url, base_url: @page.context.send(:base_url))
150
+ if matcher.match?(@url)
151
+ wait_for_load_state(state: waitUntil, timeout: timeout)
152
+ else
153
+ expect_navigation(timeout: timeout, url: url, waitUntil: waitUntil)
154
+ end
155
+ end
156
+
101
157
  def wait_for_load_state(state: nil, timeout: nil)
102
158
  option_state = state || 'load'
103
- option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
104
- unless %w(load domcontentloaded networkidle).include?(option_state)
105
- raise ArgumentError.new('state: expected one of (load|domcontentloaded|networkidle)')
159
+ option_timeout = _navigation_timeout(timeout)
160
+ unless %w(load domcontentloaded networkidle commit).include?(option_state)
161
+ raise ArgumentError.new('state: expected one of (load|domcontentloaded|networkidle|commit)')
106
162
  end
107
163
  if @load_states.include?(option_state)
108
164
  return
109
165
  end
110
166
 
111
- wait_helper = setup_navigation_wait_helper(timeout: option_timeout)
167
+ waiter = setup_navigation_waiter(wait_name: :wait_for_load_state, timeout_value: option_timeout)
112
168
 
113
- predicate = ->(state) { state == option_state }
114
- wait_helper.wait_for_event(@event_emitter, 'loadstate', predicate: predicate)
115
- wait_helper.promise.value!
169
+ predicate = ->(actual_state) {
170
+ waiter.log("\"#{actual_state}\" event fired")
171
+ actual_state == option_state
172
+ }
173
+ waiter.wait_for_event(@event_emitter, 'loadstate', predicate: predicate)
174
+
175
+ # Sometimes event is already fired durting setup.
176
+ if @load_states.include?(option_state)
177
+ waiter.force_fulfill(option_state)
178
+ return
179
+ end
180
+
181
+ waiter.result.value!
116
182
 
117
183
  nil
118
184
  end
119
185
 
186
+ def frame_element
187
+ resp = @channel.send_message_to_server('frameElement')
188
+ ChannelOwners::ElementHandle.from(resp)
189
+ end
190
+
120
191
  def evaluate(pageFunction, arg: nil)
121
- if JavaScript.function?(pageFunction)
122
- JavaScript::Function.new(pageFunction, arg).evaluate(@channel)
123
- else
124
- JavaScript::Expression.new(pageFunction).evaluate(@channel)
125
- end
192
+ JavaScript::Expression.new(pageFunction, arg).evaluate(@channel)
126
193
  end
127
194
 
128
195
  def evaluate_handle(pageFunction, arg: nil)
129
- if JavaScript.function?(pageFunction)
130
- JavaScript::Function.new(pageFunction, arg).evaluate_handle(@channel)
131
- else
132
- JavaScript::Expression.new(pageFunction).evaluate_handle(@channel)
133
- end
196
+ JavaScript::Expression.new(pageFunction, arg).evaluate_handle(@channel)
134
197
  end
135
198
 
136
- def query_selector(selector)
137
- resp = @channel.send_message_to_server('querySelector', selector: selector)
199
+ def query_selector(selector, strict: nil)
200
+ params = {
201
+ selector: selector,
202
+ strict: strict,
203
+ }.compact
204
+ resp = @channel.send_message_to_server('querySelector', params)
138
205
  ChannelOwners::ElementHandle.from_nullable(resp)
139
206
  end
140
207
 
@@ -144,62 +211,62 @@ module Playwright
144
211
  end
145
212
  end
146
213
 
147
- def checked?(selector, timeout: nil)
148
- params = { selector: selector, timeout: timeout }.compact
214
+ def wait_for_selector(selector, state: nil, strict: nil, timeout: nil)
215
+ params = { selector: selector, state: state, strict: strict, timeout: _timeout(timeout) }.compact
216
+ resp = @channel.send_message_to_server('waitForSelector', params)
217
+
218
+ ChannelOwners::ElementHandle.from_nullable(resp)
219
+ end
220
+
221
+ def checked?(selector, strict: nil, timeout: nil)
222
+ params = { selector: selector, strict: strict, timeout: _timeout(timeout) }.compact
149
223
  @channel.send_message_to_server('isChecked', params)
150
224
  end
151
225
 
152
- def disabled?(selector, timeout: nil)
153
- params = { selector: selector, timeout: timeout }.compact
226
+ def disabled?(selector, strict: nil, timeout: nil)
227
+ params = { selector: selector, strict: strict, timeout: _timeout(timeout) }.compact
154
228
  @channel.send_message_to_server('isDisabled', params)
155
229
  end
156
230
 
157
- def editable?(selector, timeout: nil)
158
- params = { selector: selector, timeout: timeout }.compact
231
+ def editable?(selector, strict: nil, timeout: nil)
232
+ params = { selector: selector, strict: strict, timeout: _timeout(timeout) }.compact
159
233
  @channel.send_message_to_server('isEditable', params)
160
234
  end
161
235
 
162
- def enabled?(selector, timeout: nil)
163
- params = { selector: selector, timeout: timeout }.compact
236
+ def enabled?(selector, strict: nil, timeout: nil)
237
+ params = { selector: selector, strict: strict, timeout: _timeout(timeout) }.compact
164
238
  @channel.send_message_to_server('isEnabled', params)
165
239
  end
166
240
 
167
- def hidden?(selector, timeout: nil)
168
- params = { selector: selector, timeout: timeout }.compact
241
+ def hidden?(selector, strict: nil, timeout: nil)
242
+ params = { selector: selector, strict: strict, timeout: _timeout(timeout) }.compact
169
243
  @channel.send_message_to_server('isHidden', params)
170
244
  end
171
245
 
172
- def visible?(selector, timeout: nil)
173
- params = { selector: selector, timeout: timeout }.compact
246
+ def visible?(selector, strict: nil, timeout: nil)
247
+ params = { selector: selector, strict: strict, timeout: _timeout(timeout) }.compact
174
248
  @channel.send_message_to_server('isVisible', params)
175
249
  end
176
250
 
177
- def dispatch_event(selector, type, eventInit: nil, timeout: nil)
251
+ def dispatch_event(selector, type, eventInit: nil, strict: nil, timeout: nil)
178
252
  params = {
179
253
  selector: selector,
180
254
  type: type,
181
255
  eventInit: JavaScript::ValueSerializer.new(eventInit).serialize,
182
- timeout: timeout,
256
+ strict: strict,
257
+ timeout: _timeout(timeout),
183
258
  }.compact
184
259
  @channel.send_message_to_server('dispatchEvent', params)
185
260
 
186
261
  nil
187
262
  end
188
263
 
189
- def eval_on_selector(selector, pageFunction, arg: nil)
190
- if JavaScript.function?(pageFunction)
191
- JavaScript::Function.new(pageFunction, arg).eval_on_selector(@channel, selector)
192
- else
193
- JavaScript::Expression.new(pageFunction).eval_on_selector(@channel, selector)
194
- end
264
+ def eval_on_selector(selector, pageFunction, arg: nil, strict: nil)
265
+ JavaScript::Expression.new(pageFunction, arg).eval_on_selector(@channel, selector, strict: strict)
195
266
  end
196
267
 
197
268
  def eval_on_selector_all(selector, pageFunction, arg: nil)
198
- if JavaScript.function?(pageFunction)
199
- JavaScript::Function.new(pageFunction, arg).eval_on_selector_all(@channel, selector)
200
- else
201
- JavaScript::Expression.new(pageFunction).eval_on_selector_all(@channel, selector)
202
- end
269
+ JavaScript::Expression.new(pageFunction, arg).eval_on_selector_all(@channel, selector)
203
270
  end
204
271
 
205
272
  def content
@@ -209,7 +276,7 @@ module Playwright
209
276
  def set_content(html, timeout: nil, waitUntil: nil)
210
277
  params = {
211
278
  html: html,
212
- timeout: timeout,
279
+ timeout: _navigation_timeout(timeout),
213
280
  waitUntil: waitUntil,
214
281
  }.compact
215
282
 
@@ -241,7 +308,7 @@ module Playwright
241
308
  url: url,
242
309
  }.compact
243
310
  if path
244
- params[:content] = "#{File.read(path)}\n//# sourceURL=#{path}"
311
+ params[:content] = JavaScript::SourceUrl.new(File.read(path), path).to_s
245
312
  end
246
313
  resp = @channel.send_message_to_server('addScriptTag', params)
247
314
  ChannelOwners::ElementHandle.from(resp)
@@ -268,7 +335,10 @@ module Playwright
268
335
  modifiers: nil,
269
336
  noWaitAfter: nil,
270
337
  position: nil,
271
- timeout: nil)
338
+ strict: nil,
339
+ timeout: nil,
340
+ trial: nil,
341
+ steps: nil)
272
342
 
273
343
  params = {
274
344
  selector: selector,
@@ -279,13 +349,45 @@ module Playwright
279
349
  modifiers: modifiers,
280
350
  noWaitAfter: noWaitAfter,
281
351
  position: position,
282
- timeout: timeout,
352
+ strict: strict,
353
+ timeout: _timeout(timeout),
354
+ trial: trial,
355
+ steps: steps,
283
356
  }.compact
284
357
  @channel.send_message_to_server('click', params)
285
358
 
286
359
  nil
287
360
  end
288
361
 
362
+ def drag_and_drop(
363
+ source,
364
+ target,
365
+ force: nil,
366
+ noWaitAfter: nil,
367
+ sourcePosition: nil,
368
+ strict: nil,
369
+ targetPosition: nil,
370
+ timeout: nil,
371
+ trial: nil,
372
+ steps: nil)
373
+
374
+ params = {
375
+ source: source,
376
+ target: target,
377
+ force: force,
378
+ noWaitAfter: noWaitAfter,
379
+ sourcePosition: sourcePosition,
380
+ strict: strict,
381
+ targetPosition: targetPosition,
382
+ timeout: _timeout(timeout),
383
+ trial: trial,
384
+ steps: steps,
385
+ }.compact
386
+ @channel.send_message_to_server('dragAndDrop', params)
387
+
388
+ nil
389
+ end
390
+
289
391
  def dblclick(
290
392
  selector,
291
393
  button: nil,
@@ -294,7 +396,10 @@ module Playwright
294
396
  modifiers: nil,
295
397
  noWaitAfter: nil,
296
398
  position: nil,
297
- timeout: nil)
399
+ strict: nil,
400
+ timeout: nil,
401
+ trial: nil,
402
+ steps: nil)
298
403
 
299
404
  params = {
300
405
  selector: selector,
@@ -304,7 +409,10 @@ module Playwright
304
409
  modifiers: modifiers,
305
410
  noWaitAfter: noWaitAfter,
306
411
  position: position,
307
- timeout: timeout,
412
+ strict: strict,
413
+ timeout: _timeout(timeout),
414
+ trial: trial,
415
+ steps: steps,
308
416
  }.compact
309
417
  @channel.send_message_to_server('dblclick', params)
310
418
 
@@ -317,58 +425,90 @@ module Playwright
317
425
  modifiers: nil,
318
426
  noWaitAfter: nil,
319
427
  position: nil,
320
- timeout: nil)
428
+ strict: nil,
429
+ timeout: nil,
430
+ trial: nil)
321
431
  params = {
322
432
  selector: selector,
323
433
  force: force,
324
434
  modifiers: modifiers,
325
435
  noWaitAfter: noWaitAfter,
326
436
  position: position,
327
- timeout: timeout,
437
+ strict: strict,
438
+ timeout: _timeout(timeout),
439
+ trial: trial,
328
440
  }.compact
329
441
  @channel.send_message_to_server('tap', params)
330
442
 
331
443
  nil
332
444
  end
333
445
 
334
- def fill(selector, value, noWaitAfter: nil, timeout: nil)
446
+ def fill(
447
+ selector,
448
+ value,
449
+ force: nil,
450
+ noWaitAfter: nil,
451
+ strict: nil,
452
+ timeout: nil)
335
453
  params = {
336
454
  selector: selector,
337
455
  value: value,
456
+ force: force,
338
457
  noWaitAfter: noWaitAfter,
339
- timeout: timeout,
458
+ strict: strict,
459
+ timeout: _timeout(timeout),
340
460
  }.compact
341
461
  @channel.send_message_to_server('fill', params)
342
462
 
343
463
  nil
344
464
  end
345
465
 
346
- def focus(selector, timeout: nil)
347
- params = { selector: selector, timeout: timeout }.compact
466
+ def locator(
467
+ selector,
468
+ has: nil,
469
+ hasNot: nil,
470
+ hasNotText: nil,
471
+ hasText: nil)
472
+ LocatorImpl.new(
473
+ frame: self,
474
+ selector: selector,
475
+ has: has,
476
+ hasNot: hasNot,
477
+ hasNotText: hasNotText,
478
+ hasText: hasText)
479
+ end
480
+
481
+ def frame_locator(selector)
482
+ FrameLocatorImpl.new(frame: self, frame_selector: selector)
483
+ end
484
+
485
+ def focus(selector, strict: nil, timeout: nil)
486
+ params = { selector: selector, strict: strict, timeout: _timeout(timeout) }.compact
348
487
  @channel.send_message_to_server('focus', params)
349
488
  nil
350
489
  end
351
490
 
352
- def text_content(selector, timeout: nil)
353
- params = { selector: selector, timeout: timeout }.compact
491
+ def text_content(selector, strict: nil, timeout: nil)
492
+ params = { selector: selector, strict: strict, timeout: _timeout(timeout) }.compact
354
493
  @channel.send_message_to_server('textContent', params)
355
494
  end
356
495
 
357
- def inner_text(selector, timeout: nil)
358
- params = { selector: selector, timeout: timeout }.compact
496
+ def inner_text(selector, strict: nil, timeout: nil)
497
+ params = { selector: selector, strict: strict, timeout: _timeout(timeout) }.compact
359
498
  @channel.send_message_to_server('innerText', params)
360
499
  end
361
500
 
362
- def inner_html(selector, timeout: nil)
363
- params = { selector: selector, timeout: timeout }.compact
501
+ def inner_html(selector, strict: nil, timeout: nil)
502
+ params = { selector: selector, strict: strict, timeout: _timeout(timeout) }.compact
364
503
  @channel.send_message_to_server('innerHTML', params)
365
504
  end
366
505
 
367
- def get_attribute(selector, name, timeout: nil)
506
+ def get_attribute(selector, name, strict: nil, timeout: nil)
368
507
  params = {
369
508
  selector: selector,
370
509
  name: name,
371
- timeout: timeout,
510
+ strict: strict,
511
+ timeout: _timeout(timeout),
372
512
  }.compact
373
513
  @channel.send_message_to_server('getAttribute', params)
374
514
  end
@@ -377,32 +517,60 @@ module Playwright
377
517
  selector,
378
518
  force: nil,
379
519
  modifiers: nil,
520
+ noWaitAfter: nil,
380
521
  position: nil,
381
- timeout: nil)
522
+ strict: nil,
523
+ timeout: nil,
524
+ trial: nil)
382
525
  params = {
383
526
  selector: selector,
384
527
  force: force,
385
528
  modifiers: modifiers,
529
+ noWaitAfter: noWaitAfter,
386
530
  position: position,
387
- timeout: timeout,
531
+ strict: strict,
532
+ timeout: _timeout(timeout),
533
+ trial: trial,
388
534
  }.compact
389
535
  @channel.send_message_to_server('hover', params)
390
536
 
391
537
  nil
392
538
  end
393
539
 
394
- def select_option(selector, values, noWaitAfter: nil, timeout: nil)
395
- base_params = SelectOptionValues.new(values).as_params
396
- params = base_params + { selector: selector, noWaitAfter: noWaitAfter, timeout: timeout }.compact
540
+ def select_option(
541
+ selector,
542
+ element: nil,
543
+ index: nil,
544
+ value: nil,
545
+ label: nil,
546
+ force: nil,
547
+ noWaitAfter: nil,
548
+ strict: nil,
549
+ timeout: nil)
550
+ base_params = SelectOptionValues.new(
551
+ element: element,
552
+ index: index,
553
+ value: value,
554
+ label: label,
555
+ ).as_params
556
+ params = base_params.merge({ selector: selector, force: force, noWaitAfter: noWaitAfter, strict: strict, timeout: _timeout(timeout) }.compact)
397
557
  @channel.send_message_to_server('selectOption', params)
558
+ end
398
559
 
399
- nil
560
+ def input_value(selector, strict: nil, timeout: nil)
561
+ params = { selector: selector, strict: strict, timeout: _timeout(timeout) }.compact
562
+ @channel.send_message_to_server('inputValue', params)
400
563
  end
401
564
 
402
- def set_input_files(selector, files, noWaitAfter: nil, timeout: nil)
403
- base_params = InputFiles.new(values).as_params
404
- params = base_params + { selector: selector, noWaitAfter: noWaitAfter, timeout: timeout }.compact
405
- @channel.send_message_to_server('setInputFiles', params)
565
+ def set_input_files(selector, files, noWaitAfter: nil, strict: nil, timeout: nil)
566
+ method_name, params = InputFiles.new(page.context, files).as_method_and_params
567
+ params.merge!({
568
+ selector: selector,
569
+ noWaitAfter: noWaitAfter,
570
+ strict: strict,
571
+ timeout: _timeout(timeout),
572
+ }.compact)
573
+ @channel.send_message_to_server(method_name, params)
406
574
 
407
575
  nil
408
576
  end
@@ -412,6 +580,7 @@ module Playwright
412
580
  text,
413
581
  delay: nil,
414
582
  noWaitAfter: nil,
583
+ strict: nil,
415
584
  timeout: nil)
416
585
 
417
586
  params = {
@@ -419,7 +588,8 @@ module Playwright
419
588
  text: text,
420
589
  delay: delay,
421
590
  noWaitAfter: noWaitAfter,
422
- timeout: timeout,
591
+ strict: strict,
592
+ timeout: _timeout(timeout),
423
593
  }.compact
424
594
  @channel.send_message_to_server('type', params)
425
595
 
@@ -431,6 +601,7 @@ module Playwright
431
601
  key,
432
602
  delay: nil,
433
603
  noWaitAfter: nil,
604
+ strict: nil,
434
605
  timeout: nil)
435
606
 
436
607
  params = {
@@ -438,56 +609,115 @@ module Playwright
438
609
  key: key,
439
610
  delay: delay,
440
611
  noWaitAfter: noWaitAfter,
441
- timeout: timeout,
612
+ strict: strict,
613
+ timeout: _timeout(timeout),
442
614
  }.compact
443
615
  @channel.send_message_to_server('press', params)
444
616
 
445
617
  nil
446
618
  end
447
619
 
448
- def check(selector, force: nil, noWaitAfter: nil, timeout: nil)
620
+ def check(
621
+ selector,
622
+ force: nil,
623
+ noWaitAfter: nil,
624
+ position: nil,
625
+ strict: nil,
626
+ timeout: nil,
627
+ trial: nil)
628
+
449
629
  params = {
450
630
  selector: selector,
451
631
  force: force,
452
632
  noWaitAfter: noWaitAfter,
453
- timeout: timeout,
633
+ position: position,
634
+ strict: strict,
635
+ timeout: _timeout(timeout),
636
+ trial: trial,
454
637
  }.compact
455
638
  @channel.send_message_to_server('check', params)
456
639
 
457
640
  nil
458
641
  end
459
642
 
460
- def uncheck(selector, force: nil, noWaitAfter: nil, timeout: nil)
643
+ def uncheck(
644
+ selector,
645
+ force: nil,
646
+ noWaitAfter: nil,
647
+ position: nil,
648
+ strict: nil,
649
+ timeout: nil,
650
+ trial: nil)
651
+
461
652
  params = {
462
653
  selector: selector,
463
654
  force: force,
464
655
  noWaitAfter: noWaitAfter,
465
- timeout: timeout,
656
+ position: position,
657
+ strict: strict,
658
+ timeout: _timeout(timeout),
659
+ trial: trial,
466
660
  }.compact
467
661
  @channel.send_message_to_server('uncheck', params)
468
662
 
469
663
  nil
470
664
  end
471
665
 
666
+ def set_checked(selector, checked, **options)
667
+ if checked
668
+ check(selector, **options)
669
+ else
670
+ uncheck(selector, **options)
671
+ end
672
+ end
673
+
674
+ def wait_for_timeout(timeout)
675
+ @channel.send_message_to_server('waitForTimeout', waitTimeout: timeout)
676
+
677
+ nil
678
+ end
679
+
472
680
  def wait_for_function(pageFunction, arg: nil, polling: nil, timeout: nil)
473
681
  if polling.is_a?(String) && polling != 'raf'
474
682
  raise ArgumentError.new("Unknown polling option: #{polling}")
475
683
  end
476
684
 
477
- function_or_expression =
478
- if JavaScript.function?(pageFunction)
479
- JavaScript::Function.new(pageFunction, arg)
480
- else
481
- JavaScript::Expression.new(pageFunction)
482
- end
483
-
484
- function_or_expression.wait_for_function(@channel, polling: polling, timeout: timeout)
685
+ expression = JavaScript::Expression.new(pageFunction, arg)
686
+ expression.wait_for_function(@channel, polling: polling, timeout: _timeout(timeout))
485
687
  end
486
688
 
487
689
  def title
488
690
  @channel.send_message_to_server('title')
489
691
  end
490
692
 
693
+ def highlight(selector)
694
+ @channel.send_message_to_server('highlight', selector: selector)
695
+ end
696
+
697
+ def expect(selector, expression, options, title)
698
+ if options.key?(:expectedValue)
699
+ options[:expectedValue] = JavaScript::ValueSerializer
700
+ .new(options[:expectedValue])
701
+ .serialize
702
+ end
703
+
704
+ result = @channel.send_message_to_server_result(
705
+ title, # title
706
+ "expect", # method
707
+ { # params
708
+ selector: selector,
709
+ expression: expression,
710
+ **options,
711
+ }.compact
712
+ )
713
+
714
+ if result.key?('received')
715
+ result['received'] = JavaScript::ValueParser.new(result['received']).parse
716
+ end
717
+
718
+ result
719
+ end
720
+
491
721
  # @param page [Page]
492
722
  # @note This method should be used internally. Accessed via .send method, so keep private!
493
723
  private def update_page_from_page(page)