playwright-ruby-client 0.0.3 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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