playwright-ruby-client 0.0.3 → 0.0.8

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +119 -12
  3. data/docs/api_coverage.md +354 -0
  4. data/lib/playwright.rb +8 -0
  5. data/lib/playwright/channel_owner.rb +16 -2
  6. data/lib/playwright/channel_owners/android.rb +10 -1
  7. data/lib/playwright/channel_owners/android_device.rb +163 -0
  8. data/lib/playwright/channel_owners/browser.rb +22 -29
  9. data/lib/playwright/channel_owners/browser_context.rb +43 -0
  10. data/lib/playwright/channel_owners/console_message.rb +21 -0
  11. data/lib/playwright/channel_owners/element_handle.rb +314 -0
  12. data/lib/playwright/channel_owners/frame.rb +466 -7
  13. data/lib/playwright/channel_owners/js_handle.rb +55 -0
  14. data/lib/playwright/channel_owners/page.rb +353 -5
  15. data/lib/playwright/channel_owners/request.rb +90 -0
  16. data/lib/playwright/channel_owners/webkit_browser.rb +1 -1
  17. data/lib/playwright/connection.rb +15 -14
  18. data/lib/playwright/errors.rb +1 -1
  19. data/lib/playwright/event_emitter.rb +13 -0
  20. data/lib/playwright/input_files.rb +42 -0
  21. data/lib/playwright/input_type.rb +19 -0
  22. data/lib/playwright/input_types/android_input.rb +19 -0
  23. data/lib/playwright/input_types/keyboard.rb +32 -0
  24. data/lib/playwright/input_types/mouse.rb +4 -0
  25. data/lib/playwright/input_types/touchscreen.rb +4 -0
  26. data/lib/playwright/javascript.rb +13 -0
  27. data/lib/playwright/javascript/expression.rb +67 -0
  28. data/lib/playwright/javascript/function.rb +67 -0
  29. data/lib/playwright/javascript/value_parser.rb +75 -0
  30. data/lib/playwright/javascript/value_serializer.rb +54 -0
  31. data/lib/playwright/playwright_api.rb +45 -25
  32. data/lib/playwright/select_option_values.rb +32 -0
  33. data/lib/playwright/timeout_settings.rb +19 -0
  34. data/lib/playwright/url_matcher.rb +19 -0
  35. data/lib/playwright/utils.rb +37 -0
  36. data/lib/playwright/version.rb +1 -1
  37. data/lib/playwright/wait_helper.rb +73 -0
  38. data/lib/playwright_api/accessibility.rb +60 -6
  39. data/lib/playwright_api/android.rb +33 -0
  40. data/lib/playwright_api/android_device.rb +78 -0
  41. data/lib/playwright_api/android_input.rb +25 -0
  42. data/lib/playwright_api/binding_call.rb +18 -0
  43. data/lib/playwright_api/browser.rb +136 -44
  44. data/lib/playwright_api/browser_context.rb +378 -51
  45. data/lib/playwright_api/browser_type.rb +137 -55
  46. data/lib/playwright_api/cdp_session.rb +32 -7
  47. data/lib/playwright_api/chromium_browser_context.rb +31 -0
  48. data/lib/playwright_api/console_message.rb +27 -7
  49. data/lib/playwright_api/dialog.rb +47 -3
  50. data/lib/playwright_api/download.rb +29 -5
  51. data/lib/playwright_api/element_handle.rb +429 -143
  52. data/lib/playwright_api/file_chooser.rb +13 -2
  53. data/lib/playwright_api/frame.rb +633 -179
  54. data/lib/playwright_api/js_handle.rb +97 -17
  55. data/lib/playwright_api/keyboard.rb +152 -24
  56. data/lib/playwright_api/mouse.rb +28 -3
  57. data/lib/playwright_api/page.rb +1183 -317
  58. data/lib/playwright_api/playwright.rb +174 -13
  59. data/lib/playwright_api/request.rb +115 -30
  60. data/lib/playwright_api/response.rb +22 -3
  61. data/lib/playwright_api/route.rb +63 -4
  62. data/lib/playwright_api/selectors.rb +29 -7
  63. data/lib/playwright_api/touchscreen.rb +2 -1
  64. data/lib/playwright_api/video.rb +11 -1
  65. data/lib/playwright_api/web_socket.rb +5 -5
  66. data/lib/playwright_api/worker.rb +29 -5
  67. data/playwright.gemspec +3 -0
  68. metadata +68 -2
@@ -2,7 +2,7 @@ require_relative './browser'
2
2
 
3
3
  module Playwright
4
4
  module ChannelOwners
5
- class WebkitBrowser < Browser
5
+ class WebKitBrowser < Browser
6
6
  end
7
7
  end
8
8
  end
@@ -55,10 +55,13 @@ module Playwright
55
55
  method: method,
56
56
  params: replace_channels_with_guids(params),
57
57
  }
58
- @transport.send_message(message)
59
- rescue Transport::AlreadyDisconnectedError => err
60
- @callbacks.delete(id)
61
- callback.reject(err)
58
+ begin
59
+ @transport.send_message(message)
60
+ rescue => err
61
+ @callbacks.delete(id)
62
+ callback.reject(err)
63
+ raise unless err.is_a?(Transport::AlreadyDisconnectedError)
64
+ end
62
65
  end
63
66
 
64
67
  callback
@@ -132,7 +135,7 @@ module Playwright
132
135
  unless object
133
136
  raise "Cannot find object to dispose: #{guid}"
134
137
  end
135
- object.dispose
138
+ object.send(:dispose!)
136
139
  return
137
140
  end
138
141
 
@@ -153,7 +156,7 @@ module Playwright
153
156
  end
154
157
 
155
158
  if payload.is_a?(Channel)
156
- { guid: payload.guid }
159
+ return { guid: payload.guid }
157
160
  end
158
161
 
159
162
  if payload.is_a?(Hash)
@@ -192,12 +195,6 @@ module Playwright
192
195
  end
193
196
  initializer = replace_guids_with_channels(initializer)
194
197
 
195
- params = [
196
- parent,
197
- type,
198
- guid,
199
- initializer,
200
- ]
201
198
  class_name = case type
202
199
  when 'Browser'
203
200
  case initializer['name']
@@ -215,7 +212,6 @@ module Playwright
215
212
  if browser_name == 'chromium'
216
213
  'ChromiumBrowserContext'
217
214
  else
218
- params << browser_name
219
215
  'BrowserContext'
220
216
  end
221
217
  else
@@ -224,7 +220,12 @@ module Playwright
224
220
 
225
221
  result =
226
222
  begin
227
- ChannelOwners.const_get(class_name).new(*params)
223
+ ChannelOwners.const_get(class_name).new(
224
+ parent,
225
+ type,
226
+ guid,
227
+ initializer,
228
+ )
228
229
  rescue NameError
229
230
  raise "Missing type #{type}"
230
231
  end
@@ -3,7 +3,7 @@ module Playwright
3
3
  # ref: https://github.com/microsoft/playwright-python/blob/0b4a980fed366c4c1dee9bfcdd72662d629fdc8d/playwright/_impl/_helper.py#L155
4
4
  def self.parse(error_payload)
5
5
  if error_payload['name'] == 'TimeoutError'
6
- Playwright::TimeoutError.new(
6
+ TimeoutError.new(
7
7
  message: error_payload['message'],
8
8
  stack: error_payload['stack'].split("\n"),
9
9
  )
@@ -17,6 +17,19 @@ module Playwright
17
17
  end
18
18
  end
19
19
 
20
+ module EventListenerInterface
21
+ def on(event, callback)
22
+ raise NotImplementedError.new('NOT IMPLEMENTED')
23
+ end
24
+
25
+ def off(event, callback)
26
+ raise NotImplementedError.new('NOT IMPLEMENTED')
27
+ end
28
+
29
+ def once(event, callback)
30
+ raise NotImplementedError.new('NOT IMPLEMENTED')
31
+ end
32
+ end
20
33
 
21
34
  # A subset of Events/EventEmitter in Node.js
22
35
  module EventEmitter
@@ -0,0 +1,42 @@
1
+ require 'base64'
2
+ require 'mime/types'
3
+
4
+ module Playwright
5
+ class InputFiles
6
+ def initialize(files)
7
+ @params = convert(files)
8
+ end
9
+
10
+ def as_params
11
+ @params
12
+ end
13
+
14
+ private def convert(files)
15
+ return convert([files]) unless files.is_a?(Array)
16
+
17
+ files.map do |file|
18
+ case file
19
+ when String
20
+ {
21
+ name: File.basename(file),
22
+ mimeType: mime_type_for(file),
23
+ buffer: Base64.strict_encode64(File.read(file)),
24
+ }
25
+ when File
26
+ {
27
+ name: File.basename(file.path),
28
+ mimeType: mime_type_for(file.path),
29
+ buffer: Base64.strict_encode64(file.read),
30
+ }
31
+ else
32
+ raise ArgumentError.new('file must be a String or File.')
33
+ end
34
+ end
35
+ end
36
+
37
+ private def mime_type_for(filepath)
38
+ mime_types = MIME::Types.type_for(filepath)
39
+ mime_types.first || 'application/octet-stream'
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,19 @@
1
+ module Playwright
2
+ class InputType
3
+ def initialize(channel)
4
+ @channel = channel
5
+ end
6
+ end
7
+
8
+ # namespace declaration
9
+ module InputTypes ; end
10
+
11
+ def self.define_input_type(class_name, &block)
12
+ klass = Class.new(InputType)
13
+ klass.class_eval(&block) if block
14
+ InputTypes.const_set(class_name, klass)
15
+ end
16
+ end
17
+
18
+ # load subclasses
19
+ Dir[File.join(__dir__, 'input_types', '*.rb')].each { |f| require f }
@@ -0,0 +1,19 @@
1
+ module Playwright
2
+ define_input_type :AndroidInput do
3
+ def type(text)
4
+ @channel.send_message_to_server('inputType', text: text)
5
+ end
6
+
7
+ def press(key)
8
+ @channel.send_message_to_server('inputPress', key: key)
9
+ end
10
+
11
+ def tap_point(point)
12
+ @channel.send_message_to_server('inputTap', point: point)
13
+ end
14
+
15
+ def drag(from, to, steps)
16
+ @channel.send_message_to_server('inputDrag', from: from, to: to, steps: steps)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ module Playwright
2
+ define_input_type :Keyboard do
3
+ def down(key)
4
+ @channel.send_message_to_server('keyboardDown', key: key)
5
+ nil
6
+ end
7
+
8
+ def up(key)
9
+ @channel.send_message_to_server('keyboardDown', key: key)
10
+ end
11
+
12
+ def insert_text(text)
13
+ @channel.send_message_to_server('keyboardInsertText', text: text)
14
+ end
15
+
16
+ def type(text, delay: nil)
17
+ params = {
18
+ text: text,
19
+ delay: delay,
20
+ }.compact
21
+ @channel.send_message_to_server('keyboardType', params)
22
+ end
23
+
24
+ def press(key, delay: nil)
25
+ params = {
26
+ key: key,
27
+ delay: delay,
28
+ }.compact
29
+ @channel.send_message_to_server('keyboardPress', params)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,4 @@
1
+ module Playwright
2
+ define_input_type :Mouse do
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Playwright
2
+ define_input_type :Touchscreen do
3
+ end
4
+ end
@@ -0,0 +1,13 @@
1
+ require_relative './javascript/expression'
2
+ require_relative './javascript/function'
3
+ require_relative './javascript/value_parser'
4
+ require_relative './javascript/value_serializer'
5
+
6
+ module Playwright
7
+ module JavaScript
8
+ # Detect if str is likely to be a function
9
+ module_function def function?(str)
10
+ ['async', 'function'].any? { |key| str.strip.start_with?(key) } || str.include?('=>')
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,67 @@
1
+ module Playwright
2
+ module JavaScript
3
+ class Expression
4
+ def initialize(expression)
5
+ @expression = expression
6
+ @serialized_arg = ValueSerializer.new(nil).serialize
7
+ end
8
+
9
+ def evaluate(channel)
10
+ value = channel.send_message_to_server(
11
+ 'evaluateExpression',
12
+ expression: @expression,
13
+ isFunction: false,
14
+ arg: @serialized_arg,
15
+ )
16
+ ValueParser.new(value).parse
17
+ end
18
+
19
+ def evaluate_handle(channel)
20
+ resp = channel.send_message_to_server(
21
+ 'evaluateExpressionHandle',
22
+ expression: @expression,
23
+ isFunction: false,
24
+ arg: @serialized_arg,
25
+ )
26
+ ::Playwright::ChannelOwner.from(resp)
27
+ end
28
+
29
+ def eval_on_selector(channel, selector)
30
+ value = channel.send_message_to_server(
31
+ 'evalOnSelector',
32
+ selector: selector,
33
+ expression: @expression,
34
+ isFunction: false,
35
+ arg: @serialized_arg,
36
+ )
37
+ ValueParser.new(value).parse
38
+ end
39
+
40
+ def eval_on_selector_all(channel, selector)
41
+ value = channel.send_message_to_server(
42
+ 'evalOnSelectorAll',
43
+ selector: selector,
44
+ expression: @expression,
45
+ isFunction: false,
46
+ arg: @serialized_arg,
47
+ )
48
+ ValueParser.new(value).parse
49
+ end
50
+
51
+ def wait_for_function(channel, polling:, timeout:)
52
+ params = {
53
+ expression: @expression,
54
+ isFunction: false,
55
+ arg: @serialized_arg,
56
+ polling: polling,
57
+ timeout: timeout,
58
+ }.compact
59
+ if polling.is_a?(Numeric)
60
+ params[:pollingInterval] = polling
61
+ end
62
+ resp = channel.send_message_to_server('waitForFunction', params)
63
+ ChannelOwners::JSHandle.from(resp)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,67 @@
1
+ module Playwright
2
+ module JavaScript
3
+ class Function
4
+ def initialize(definition, arg)
5
+ @definition = definition
6
+ @serialized_arg = ValueSerializer.new(arg).serialize
7
+ end
8
+
9
+ def evaluate(channel)
10
+ value = channel.send_message_to_server(
11
+ 'evaluateExpression',
12
+ expression: @definition,
13
+ isFunction: true,
14
+ arg: @serialized_arg,
15
+ )
16
+ ValueParser.new(value).parse
17
+ end
18
+
19
+ def evaluate_handle(channel)
20
+ resp = channel.send_message_to_server(
21
+ 'evaluateExpressionHandle',
22
+ expression: @definition,
23
+ isFunction: true,
24
+ arg: @serialized_arg,
25
+ )
26
+ ::Playwright::ChannelOwner.from(resp)
27
+ end
28
+
29
+ def eval_on_selector(channel, selector)
30
+ value = channel.send_message_to_server(
31
+ 'evalOnSelector',
32
+ selector: selector,
33
+ expression: @definition,
34
+ isFunction: true,
35
+ arg: @serialized_arg,
36
+ )
37
+ ValueParser.new(value).parse
38
+ end
39
+
40
+ def eval_on_selector_all(channel, selector)
41
+ value = channel.send_message_to_server(
42
+ 'evalOnSelectorAll',
43
+ selector: selector,
44
+ expression: @definition,
45
+ isFunction: true,
46
+ arg: @serialized_arg,
47
+ )
48
+ ValueParser.new(value).parse
49
+ end
50
+
51
+ def wait_for_function(channel, polling:, timeout:)
52
+ params = {
53
+ expression: @definition,
54
+ isFunction: true,
55
+ arg: @serialized_arg,
56
+ polling: polling,
57
+ timeout: timeout,
58
+ }.compact
59
+ if polling.is_a?(Numeric)
60
+ params[:pollingInterval] = polling
61
+ end
62
+ resp = channel.send_message_to_server('waitForFunction', params)
63
+ ChannelOwners::JSHandle.from(resp)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,75 @@
1
+ require 'date'
2
+
3
+ module Playwright
4
+ module JavaScript
5
+ class ValueParser
6
+ def initialize(hash)
7
+ @hash = hash
8
+ end
9
+
10
+ # @return [Hash]
11
+ def parse
12
+ if @hash.nil?
13
+ nil
14
+ else
15
+ parse_hash(@hash)
16
+ end
17
+ end
18
+
19
+ # ref: https://github.com/microsoft/playwright/blob/b45905ae3f1a066a8ecb358035ce745ddd21cf3a/src/protocol/serializers.ts#L42
20
+ # ref: https://github.com/microsoft/playwright-python/blob/25a99d53e00e35365cf5113b9525272628c0e65f/playwright/_impl/_js_handle.py#L140
21
+ private def parse_hash(hash)
22
+ %w(n s b).each do |key|
23
+ return hash[key] if hash.key?(key)
24
+ end
25
+
26
+ if hash.key?('v')
27
+ return
28
+ case hash['v']
29
+ when 'undefined'
30
+ nil
31
+ when 'null'
32
+ nil
33
+ when 'NaN'
34
+ Float::NAN
35
+ when 'Infinity'
36
+ Float::INFINITY
37
+ when '-Infinity'
38
+ -Float::INFINITY
39
+ when '-0'
40
+ -0
41
+ end
42
+ end
43
+
44
+ if hash.key?('d')
45
+ return DateTime.parse(hash['d'])
46
+ end
47
+
48
+ if hash.key?('r')
49
+ # @see https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp
50
+ # @see https://docs.ruby-lang.org/ja/latest/class/Regexp.html
51
+ js_regex_flag = hash['r']['f']
52
+ flags = []
53
+ flags << Regexp::IGNORECASE if js_regex_flag.include?('i')
54
+ flags << Regexp::MULTILINE if js_regex_flag.include?('m') || js_regex_flag.include?('s')
55
+
56
+ return Regexp.compile(hash['r']['p'], flags.inject(:|))
57
+ end
58
+
59
+ if hash.key?('a')
60
+ return hash['a'].map { |value| parse_hash(value) }
61
+ end
62
+
63
+ if hash.key?('o')
64
+ return hash['o'].map { |obj| [obj['k'], parse_hash(obj['v'])] }.to_h
65
+ end
66
+
67
+ if hash.key?('h')
68
+ return @handles[hash['h']]
69
+ end
70
+
71
+ raise ArgumentError.new("Unexpected value: #{hash}")
72
+ end
73
+ end
74
+ end
75
+ end