playwright-ruby-client 0.0.5 → 0.0.6

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/playwright.rb +2 -0
  3. data/lib/playwright/channel_owners/element_handle.rb +75 -0
  4. data/lib/playwright/channel_owners/frame.rb +117 -0
  5. data/lib/playwright/channel_owners/page.rb +68 -11
  6. data/lib/playwright/channel_owners/request.rb +90 -0
  7. data/lib/playwright/input_type.rb +19 -0
  8. data/lib/playwright/input_types/keyboard.rb +32 -0
  9. data/lib/playwright/input_types/mouse.rb +4 -0
  10. data/lib/playwright/input_types/touchscreen.rb +4 -0
  11. data/lib/playwright/javascript/expression.rb +22 -0
  12. data/lib/playwright/javascript/function.rb +22 -0
  13. data/lib/playwright/playwright_api.rb +31 -23
  14. data/lib/playwright/timeout_settings.rb +1 -1
  15. data/lib/playwright/url_matcher.rb +19 -0
  16. data/lib/playwright/version.rb +1 -1
  17. data/lib/playwright_api/accessibility.rb +46 -6
  18. data/lib/playwright_api/binding_call.rb +6 -6
  19. data/lib/playwright_api/browser.rb +76 -16
  20. data/lib/playwright_api/browser_context.rb +284 -30
  21. data/lib/playwright_api/browser_type.rb +54 -9
  22. data/lib/playwright_api/cdp_session.rb +23 -1
  23. data/lib/playwright_api/chromium_browser_context.rb +16 -6
  24. data/lib/playwright_api/console_message.rb +10 -10
  25. data/lib/playwright_api/dialog.rb +42 -0
  26. data/lib/playwright_api/download.rb +19 -4
  27. data/lib/playwright_api/element_handle.rb +174 -21
  28. data/lib/playwright_api/file_chooser.rb +8 -0
  29. data/lib/playwright_api/frame.rb +355 -68
  30. data/lib/playwright_api/js_handle.rb +45 -9
  31. data/lib/playwright_api/keyboard.rb +98 -8
  32. data/lib/playwright_api/mouse.rb +22 -0
  33. data/lib/playwright_api/page.rb +779 -127
  34. data/lib/playwright_api/playwright.rb +98 -19
  35. data/lib/playwright_api/request.rb +70 -23
  36. data/lib/playwright_api/response.rb +6 -6
  37. data/lib/playwright_api/route.rb +48 -0
  38. data/lib/playwright_api/selectors.rb +14 -6
  39. data/lib/playwright_api/video.rb +8 -0
  40. data/lib/playwright_api/web_socket.rb +3 -5
  41. data/lib/playwright_api/worker.rb +12 -0
  42. metadata +7 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c54a8690be4b1b2e33cecb6cda92071cd1d6867647fe939b004bf0434c31c74e
4
- data.tar.gz: 1c2c784a5970c7a89bffc6bdb12cf7c8e1353a676881a99862bb9bbe30dd3dea
3
+ metadata.gz: 243ef6d83fffde209cb8effe291c49b80fb90f7fd1fab7dacd451a524793f78a
4
+ data.tar.gz: 877db73a65179e2409423c73aa1fdf72b0da40ebe2d44bf22bce34533c438c98
5
5
  SHA512:
6
- metadata.gz: e9ca66a5edc3d168e16b552f3c84e4b4a939a7e8610da439fdea8350a87fed0656a9b5c715e4ac384d23b1d089ab23fa61ccd06288b1f11045aa83a3351a4f94
7
- data.tar.gz: cad563aa4e2f81a026f08a04a7fd3caf1295f6419b01f1601d324583243dd839bb4b7f3aec73967555c4dbb6b8ffb8b65e7607836ec2c11231512c6e45fbf526
6
+ metadata.gz: 01ea7a14404c07a54acd5112ad3704ce0d00ccd562061cbf46b0ec190c26ff0df850c613cddde6ff2d922de390063d6398a02a33f44fad88dd6d5d6ab1918893
7
+ data.tar.gz: 5e965b61082503371431d72a8b243c6212d5d2f3b6a232d62faf6fed2c85a920c041e259ea8f2d646722bf9e14eb4fcefebc9f40e86dca3a8ec2aa931c63d95b
@@ -15,9 +15,11 @@ require 'playwright/utils'
15
15
 
16
16
  require 'playwright/channel'
17
17
  require 'playwright/channel_owner'
18
+ require 'playwright/input_type'
18
19
  require 'playwright/connection'
19
20
  require 'playwright/timeout_settings'
20
21
  require 'playwright/transport'
22
+ require 'playwright/url_matcher'
21
23
  require 'playwright/version'
22
24
  require 'playwright/wait_helper'
23
25
 
@@ -3,6 +3,81 @@ require_relative './js_handle'
3
3
  module Playwright
4
4
  module ChannelOwners
5
5
  class ElementHandle < JSHandle
6
+ def click(
7
+ button: nil,
8
+ clickCount: nil,
9
+ delay: nil,
10
+ force: nil,
11
+ modifiers: nil,
12
+ noWaitAfter: nil,
13
+ position: nil,
14
+ timeout: nil)
15
+
16
+ params = {
17
+ button: button,
18
+ clickCount: clickCount,
19
+ delay: delay,
20
+ force: force,
21
+ modifiers: modifiers,
22
+ noWaitAfter: noWaitAfter,
23
+ position: position,
24
+ timeout: timeout,
25
+ }.compact
26
+ @channel.send_message_to_server('click', params)
27
+
28
+ nil
29
+ end
30
+
31
+ def type_text(text, delay: nil, noWaitAfter: nil, timeout: nil)
32
+ params = {
33
+ text: text,
34
+ delay: delay,
35
+ noWaitAfter: noWaitAfter,
36
+ timeout: timeout,
37
+ }.compact
38
+ @channel.send_message_to_server('type', params)
39
+
40
+ nil
41
+ end
42
+
43
+ def press(key, delay: nil, noWaitAfter: nil, timeout: nil)
44
+ params = {
45
+ key: key,
46
+ delay: delay,
47
+ noWaitAfter: noWaitAfter,
48
+ timeout: timeout,
49
+ }.compact
50
+ @channel.send_message_to_server('press', params)
51
+
52
+ nil
53
+ end
54
+
55
+ def query_selector(selector)
56
+ resp = @channel.send_message_to_server('querySelector', selector: selector)
57
+ ChannelOwners::ElementHandle.from_nullable(resp)
58
+ end
59
+
60
+ def query_selector_all(selector)
61
+ @channel.send_message_to_server('querySelectorAll', selector: selector).map do |el|
62
+ ChannelOwners::ElementHandle.from(el)
63
+ end
64
+ end
65
+
66
+ def eval_on_selector(selector, pageFunction, arg: nil)
67
+ if JavaScript.function?(pageFunction)
68
+ JavaScript::Function.new(pageFunction, arg).eval_on_selector(@channel, selector)
69
+ else
70
+ JavaScript::Expression.new(pageFunction).eval_on_selector(@channel, selector)
71
+ end
72
+ end
73
+
74
+ def eval_on_selector_all(selector, pageFunction, arg: nil)
75
+ if JavaScript.function?(pageFunction)
76
+ JavaScript::Function.new(pageFunction, arg).eval_on_selector_all(@channel, selector)
77
+ else
78
+ JavaScript::Expression.new(pageFunction).eval_on_selector_all(@channel, selector)
79
+ end
80
+ end
6
81
  end
7
82
  end
8
83
  end
@@ -53,6 +53,69 @@ module Playwright
53
53
  ChannelOwners::Response.from_nullable(resp)
54
54
  end
55
55
 
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.")
62
+ end
63
+ end
64
+
65
+ def wait_for_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
66
+ option_wait_until = waitUntil || 'load'
67
+ option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
68
+ time_start = Time.now
69
+
70
+ wait_helper = setup_navigation_wait_helper(timeout: option_timeout)
71
+
72
+ predicate =
73
+ if url
74
+ matcher = UrlMatcher.new(url)
75
+ ->(event) { event['error'] || matcher.match?(event['url']) }
76
+ else
77
+ ->(_) { true }
78
+ end
79
+
80
+ wait_helper.wait_for_event(@event_emitter, 'navigated', predicate: predicate)
81
+
82
+ block&.call
83
+
84
+ event = wait_helper.promise.value!
85
+ if event['error']
86
+ raise event['error']
87
+ end
88
+
89
+ unless @load_states.include?(option_wait_until)
90
+ elapsed_time = Time.now - time_start
91
+ if elapsed_time < option_timeout
92
+ wait_for_load_state(state: option_wait_until, timeout: option_timeout - elapsed_time)
93
+ end
94
+ end
95
+
96
+ request_json = event.dig('newDocument', 'request')
97
+ request = ChannelOwners::Request.from_nullable(request_json)
98
+ request&.response
99
+ end
100
+
101
+ def wait_for_load_state(state: nil, timeout: nil)
102
+ option_state = state || 'load'
103
+ unless %w(load domcontentloaded networkidle).include?(option_state)
104
+ raise ArgumentError.new('state: expected one of (load|domcontentloaded|networkidle)')
105
+ end
106
+ if @load_states.include?(option_state)
107
+ return
108
+ end
109
+
110
+ wait_helper = setup_navigation_wait_helper(timeout: timeout)
111
+
112
+ predicate = ->(state) { state == option_state }
113
+ wait_helper.wait_for_event(@event_emitter, 'loadstate', predicate: predicate)
114
+ wait_helper.promise.value!
115
+
116
+ nil
117
+ end
118
+
56
119
  def evaluate(pageFunction, arg: nil)
57
120
  if JavaScript.function?(pageFunction)
58
121
  JavaScript::Function.new(pageFunction, arg).evaluate(@channel)
@@ -69,6 +132,33 @@ module Playwright
69
132
  end
70
133
  end
71
134
 
135
+ def query_selector(selector)
136
+ resp = @channel.send_message_to_server('querySelector', selector: selector)
137
+ ChannelOwners::ElementHandle.from_nullable(resp)
138
+ end
139
+
140
+ def query_selector_all(selector)
141
+ @channel.send_message_to_server('querySelectorAll', selector: selector).map do |el|
142
+ ChannelOwners::ElementHandle.from(el)
143
+ end
144
+ end
145
+
146
+ def eval_on_selector(selector, pageFunction, arg: nil)
147
+ if JavaScript.function?(pageFunction)
148
+ JavaScript::Function.new(pageFunction, arg).eval_on_selector(@channel, selector)
149
+ else
150
+ JavaScript::Expression.new(pageFunction).eval_on_selector(@channel, selector)
151
+ end
152
+ end
153
+
154
+ def eval_on_selector_all(selector, pageFunction, arg: nil)
155
+ if JavaScript.function?(pageFunction)
156
+ JavaScript::Function.new(pageFunction, arg).eval_on_selector_all(@channel, selector)
157
+ else
158
+ JavaScript::Expression.new(pageFunction).eval_on_selector_all(@channel, selector)
159
+ end
160
+ end
161
+
72
162
  def content
73
163
  @channel.send_message_to_server('content')
74
164
  end
@@ -85,6 +175,33 @@ module Playwright
85
175
  nil
86
176
  end
87
177
 
178
+ def click(
179
+ selector,
180
+ button: nil,
181
+ clickCount: nil,
182
+ delay: nil,
183
+ force: nil,
184
+ modifiers: nil,
185
+ noWaitAfter: nil,
186
+ position: nil,
187
+ timeout: nil)
188
+
189
+ params = {
190
+ selector: selector,
191
+ button: button,
192
+ clickCount: clickCount,
193
+ delay: delay,
194
+ force: force,
195
+ modifiers: modifiers,
196
+ noWaitAfter: noWaitAfter,
197
+ position: position,
198
+ timeout: timeout,
199
+ }.compact
200
+ @channel.send_message_to_server('click', params)
201
+
202
+ nil
203
+ end
204
+
88
205
  def focus(selector, timeout: nil)
89
206
  params = { selector: selector, timeout: timeout }.compact
90
207
  @channel.send_message_to_server('focus', params)
@@ -10,9 +10,9 @@ module Playwright
10
10
  @browser_context = @parent
11
11
  @timeout_settings = TimeoutSettings.new(@browser_context.send(:_timeout_settings))
12
12
  @accessibility = Accessibility.new(@channel)
13
- @keyboard = Keyboard.new(@channel)
14
- @mouse = Mouse.new(@channel)
15
- @touchscreen = Touchscreen.new(@channel)
13
+ @keyboard = InputTypes::Keyboard.new(@channel)
14
+ @mouse = InputTypes::Mouse.new(@channel)
15
+ @touchscreen = InputTypes::Touchscreen.new(@channel)
16
16
 
17
17
  @viewport_size = @initializer['viewportSize']
18
18
  @closed = false
@@ -103,6 +103,14 @@ module Playwright
103
103
  @frames.to_a
104
104
  end
105
105
 
106
+ def query_selector(selector)
107
+ @main_frame.query_selector(selector)
108
+ end
109
+
110
+ def query_selector_all(selector)
111
+ @main_frame.query_selector_all(selector)
112
+ end
113
+
106
114
  def evaluate(pageFunction, arg: nil)
107
115
  @main_frame.evaluate(pageFunction, arg: arg)
108
116
  end
@@ -111,6 +119,14 @@ module Playwright
111
119
  @main_frame.evaluate_handle(pageFunction, arg: arg)
112
120
  end
113
121
 
122
+ def eval_on_selector(selector, pageFunction, arg: nil)
123
+ @main_frame.eval_on_selector(selector, pageFunction, arg: arg)
124
+ end
125
+
126
+ def eval_on_selector_all(selector, pageFunction, arg: nil)
127
+ @main_frame.eval_on_selector_all(selector, pageFunction, arg: arg)
128
+ end
129
+
114
130
  def url
115
131
  @main_frame.url
116
132
  end
@@ -177,6 +193,30 @@ module Playwright
177
193
  @closed
178
194
  end
179
195
 
196
+ def click(
197
+ selector,
198
+ button: nil,
199
+ clickCount: nil,
200
+ delay: nil,
201
+ force: nil,
202
+ modifiers: nil,
203
+ noWaitAfter: nil,
204
+ position: nil,
205
+ timeout: nil)
206
+
207
+ @main_frame.click(
208
+ selector,
209
+ button: button,
210
+ clickCount: clickCount,
211
+ delay: delay,
212
+ force: force,
213
+ modifiers: modifiers,
214
+ noWaitAfter: noWaitAfter,
215
+ position: position,
216
+ timeout: timeout,
217
+ )
218
+ end
219
+
180
220
  def focus(selector, timeout: nil)
181
221
  @main_frame.focus(selector, timeout: timeout)
182
222
  end
@@ -213,6 +253,12 @@ module Playwright
213
253
  end
214
254
  end
215
255
 
256
+ class FrameAlreadyDetachedError < StandardError
257
+ def initialize
258
+ super('Navigating frame was detached!')
259
+ end
260
+ end
261
+
216
262
  def wait_for_event(event, optionsOrPredicate: nil, &block)
217
263
  predicate, timeout =
218
264
  case optionsOrPredicate
@@ -243,13 +289,20 @@ module Playwright
243
289
  wait_helper.promise.value!
244
290
  end
245
291
 
292
+ def wait_for_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
293
+ @main_frame.wait_for_navigation(
294
+ timeout: timeout,
295
+ url: url,
296
+ waitUntil: waitUntil,
297
+ &block)
298
+ end
299
+
246
300
  def wait_for_request(urlOrPredicate, timeout: nil)
247
301
  predicate =
248
302
  case urlOrPredicate
249
- when String
250
- -> (req){ req.url == urlOrPredicate }
251
- when Regexp
252
- -> (req){ urlOrPredicate.match?(req.url) }
303
+ when String, Regexp
304
+ url_matcher = UrlMatcher.new(urlOrPredicate)
305
+ -> (req){ url_matcher.match?(req.url) }
253
306
  when Proc
254
307
  urlOrPredicate
255
308
  else
@@ -262,10 +315,9 @@ module Playwright
262
315
  def wait_for_response(urlOrPredicate, timeout: nil)
263
316
  predicate =
264
317
  case urlOrPredicate
265
- when String
266
- -> (res){ res.url == urlOrPredicate }
267
- when Regexp
268
- -> (res){ urlOrPredicate.match?(res.url) }
318
+ when String, Regexp
319
+ url_matcher = UrlMatcher.new(urlOrPredicate)
320
+ -> (req){ url_matcher.match?(req.url) }
269
321
  when Proc
270
322
  urlOrPredicate
271
323
  else
@@ -280,5 +332,10 @@ module Playwright
280
332
  @browser_context = context
281
333
  @timeout_settings = TimeoutSettings.new(context.send(:_timeout_settings))
282
334
  end
335
+
336
+ # called from Frame with send(:timeout_settings)
337
+ private def timeout_settings
338
+ @timeout_settings
339
+ end
283
340
  end
284
341
  end
@@ -1,5 +1,95 @@
1
+ require 'base64'
2
+
1
3
  module Playwright
2
4
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_network.py
3
5
  define_channel_owner :Request do
6
+ def after_initialize
7
+ @redirected_from = ChannelOwners::Request.from_nullable(@initializer['redirectedFrom'])
8
+ @redirected_from&.send(:update_redirected_to, self)
9
+ @timing = {
10
+ startTime: 0,
11
+ domainLookupStart: -1,
12
+ domainLookupEnd: -1,
13
+ connectStart: -1,
14
+ secureConnectionStart: -1,
15
+ connectEnd: -1,
16
+ requestStart: -1,
17
+ responseStart: -1,
18
+ responseEnd: -1,
19
+ }
20
+ @headers = parse_headers(@initializer['headers'])
21
+ end
22
+
23
+ def url
24
+ @initializer['url']
25
+ end
26
+
27
+ def resource_type
28
+ @initializer['resourceType']
29
+ end
30
+
31
+ def method
32
+ @initialize['method']
33
+ end
34
+
35
+ def post_data
36
+ post_data_buffer
37
+ end
38
+
39
+ def post_data_json
40
+ data = post_data
41
+ return unless data
42
+
43
+ content_type = @headers['content-type']
44
+ return unless content_type
45
+
46
+ if content_type == "application/x-www-form-urlencoded"
47
+ URI.decode_www_form(data).to_h
48
+ else
49
+ JSON.parse(data)
50
+ end
51
+ end
52
+
53
+ def post_data_buffer
54
+ base64_content = @initializer['postData']
55
+ if base64_content
56
+ Base64.strict_decode64(base64_content)
57
+ else
58
+ nil
59
+ end
60
+ end
61
+
62
+ def headers
63
+ @headers
64
+ end
65
+
66
+ def response
67
+ resp = @channel.send_message_to_server('response')
68
+ ChannelOwners::Response.from_nullable(resp)
69
+ end
70
+
71
+ def frame
72
+ @initializer['frame']
73
+ end
74
+
75
+ def navigation_request?
76
+ @initializer['isNavigationRequest']
77
+ end
78
+
79
+ def failure
80
+ @failure_text
81
+ end
82
+
83
+ attr_reader :headers, :redirected_from, :redirected_to, :timing
84
+
85
+ private def update_redirected_to(request)
86
+ @redirected_to = request
87
+ end
88
+
89
+ private def parse_headers(headers)
90
+ headers.map do |header|
91
+ [header['name'].downcase, header['value']]
92
+ end.to_h
93
+ end
4
94
  end
5
95
  end