playwright-ruby-client 0.0.7 → 0.2.1

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +38 -3
  3. data/docs/api_coverage.md +89 -84
  4. data/lib/playwright.rb +4 -2
  5. data/lib/playwright/android_input_impl.rb +23 -0
  6. data/lib/playwright/api_implementation.rb +18 -0
  7. data/lib/playwright/channel.rb +7 -0
  8. data/lib/playwright/channel_owner.rb +3 -5
  9. data/lib/playwright/channel_owners/android.rb +1 -1
  10. data/lib/playwright/channel_owners/android_device.rb +83 -13
  11. data/lib/playwright/channel_owners/browser.rb +1 -1
  12. data/lib/playwright/channel_owners/browser_context.rb +10 -2
  13. data/lib/playwright/channel_owners/download.rb +27 -0
  14. data/lib/playwright/channel_owners/element_handle.rb +15 -4
  15. data/lib/playwright/channel_owners/frame.rb +228 -19
  16. data/lib/playwright/channel_owners/js_handle.rb +1 -1
  17. data/lib/playwright/channel_owners/page.rb +350 -27
  18. data/lib/playwright/channel_owners/request.rb +9 -1
  19. data/lib/playwright/errors.rb +1 -1
  20. data/lib/playwright/event_emitter.rb +8 -1
  21. data/lib/playwright/event_emitter_proxy.rb +49 -0
  22. data/lib/playwright/file_chooser_impl.rb +23 -0
  23. data/lib/playwright/http_headers.rb +20 -0
  24. data/lib/playwright/input_files.rb +1 -1
  25. data/lib/playwright/javascript/expression.rb +15 -0
  26. data/lib/playwright/javascript/function.rb +15 -0
  27. data/lib/playwright/javascript/value_parser.rb +1 -1
  28. data/lib/playwright/javascript/value_serializer.rb +1 -1
  29. data/lib/playwright/{input_types/keyboard.rb → keyboard_impl.rb} +5 -1
  30. data/lib/playwright/mouse_impl.rb +7 -0
  31. data/lib/playwright/playwright_api.rb +59 -20
  32. data/lib/playwright/select_option_values.rb +14 -4
  33. data/lib/playwright/timeout_settings.rb +1 -1
  34. data/lib/playwright/touchscreen_impl.rb +7 -0
  35. data/lib/playwright/utils.rb +3 -3
  36. data/lib/playwright/version.rb +1 -1
  37. data/lib/playwright/wait_helper.rb +1 -1
  38. data/lib/playwright_api/android.rb +9 -10
  39. data/lib/playwright_api/android_device.rb +43 -14
  40. data/lib/playwright_api/android_input.rb +25 -0
  41. data/lib/playwright_api/binding_call.rb +10 -6
  42. data/lib/playwright_api/browser.rb +20 -21
  43. data/lib/playwright_api/browser_context.rb +29 -20
  44. data/lib/playwright_api/browser_type.rb +16 -56
  45. data/lib/playwright_api/chromium_browser_context.rb +10 -8
  46. data/lib/playwright_api/console_message.rb +10 -6
  47. data/lib/playwright_api/dialog.rb +5 -1
  48. data/lib/playwright_api/download.rb +28 -11
  49. data/lib/playwright_api/element_handle.rb +107 -96
  50. data/lib/playwright_api/file_chooser.rb +17 -9
  51. data/lib/playwright_api/frame.rb +136 -132
  52. data/lib/playwright_api/js_handle.rb +18 -20
  53. data/lib/playwright_api/keyboard.rb +5 -5
  54. data/lib/playwright_api/page.rb +204 -149
  55. data/lib/playwright_api/playwright.rb +32 -44
  56. data/lib/playwright_api/request.rb +7 -8
  57. data/lib/playwright_api/response.rb +10 -6
  58. data/lib/playwright_api/selectors.rb +13 -9
  59. data/lib/playwright_api/web_socket.rb +10 -1
  60. data/lib/playwright_api/worker.rb +13 -13
  61. metadata +12 -6
  62. data/lib/playwright/input_type.rb +0 -19
  63. data/lib/playwright/input_types/mouse.rb +0 -4
  64. data/lib/playwright/input_types/touchscreen.rb +0 -4
@@ -3,7 +3,7 @@ require 'base64'
3
3
  module Playwright
4
4
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_network.py
5
5
  define_channel_owner :Request do
6
- def after_initialize
6
+ private def after_initialize
7
7
  @redirected_from = ChannelOwners::Request.from_nullable(@initializer['redirectedFrom'])
8
8
  @redirected_from&.send(:update_redirected_to, self)
9
9
  @timing = {
@@ -86,6 +86,14 @@ module Playwright
86
86
  @redirected_to = request
87
87
  end
88
88
 
89
+ private def update_failure_text(failure_text)
90
+ @failure_text = failure_text
91
+ end
92
+
93
+ private def update_response_end_timing(response_end_timing)
94
+ @timing[:responseEnd] = response_end_timing
95
+ end
96
+
89
97
  private def parse_headers(headers)
90
98
  headers.map do |header|
91
99
  [header['name'].downcase, header['value']]
@@ -28,7 +28,7 @@ module Playwright
28
28
  end
29
29
 
30
30
  class TimeoutError < Error
31
- def initialize(message:, stack:)
31
+ def initialize(message:, stack: [])
32
32
  super(name: 'TimeoutError', message: message, stack: stack)
33
33
  end
34
34
  end
@@ -34,11 +34,18 @@ module Playwright
34
34
  # A subset of Events/EventEmitter in Node.js
35
35
  module EventEmitter
36
36
  # @param event [String]
37
+ # @returns [Boolean]
37
38
  def emit(event, *args)
39
+ handled = false
38
40
  (@__event_emitter ||= {})[event.to_s]&.each do |callback|
39
41
  callback.call(*args)
42
+ handled = true
40
43
  end
41
- self
44
+ handled
45
+ end
46
+
47
+ private def listener_count(event)
48
+ ((@__event_emitter ||= {})[event.to_s] ||= Set.new).count
42
49
  end
43
50
 
44
51
  # @param event [String]
@@ -0,0 +1,49 @@
1
+ module Playwright
2
+ class EventEmitterProxy
3
+ include EventEmitter
4
+
5
+ # @param src [PlaywrightApi]
6
+ # @param dest [EventEmitter]
7
+ def initialize(api, impl)
8
+ @api = api
9
+ @impl = impl
10
+ @listeners = {}
11
+ end
12
+
13
+ def on(event, callback)
14
+ if listener_count(event) == 0
15
+ subscribe(event)
16
+ end
17
+ super
18
+ end
19
+
20
+ def once(event, callback)
21
+ if listener_count(event) == 0
22
+ subscribe(event)
23
+ end
24
+ super
25
+ end
26
+
27
+ def off(event, callback)
28
+ super
29
+ if listener_count(event) == 0
30
+ unsubscribe(event)
31
+ end
32
+ end
33
+
34
+ private def subscribe(event)
35
+ @listeners[event] = ->(*args) {
36
+ wrapped_args = args.map { |arg| ::Playwright::PlaywrightApi.wrap(arg) }
37
+ emit(event, *wrapped_args)
38
+ }
39
+ @impl.on(event, @listeners[event])
40
+ end
41
+
42
+ private def unsubscribe(event)
43
+ listener = @listeners.delete(event)
44
+ if listener
45
+ @impl.off(event, listener)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,23 @@
1
+ module Playwright
2
+ define_api_implementation :FileChooserImpl do
3
+ def initialize(page:, element_handle:, is_multiple:)
4
+ @page = page
5
+ @element_handle = element_handle
6
+ @is_multiple = is_multiple
7
+ end
8
+
9
+ attr_reader :page
10
+
11
+ def element
12
+ @element_handle
13
+ end
14
+
15
+ def multiple?
16
+ @is_multiple
17
+ end
18
+
19
+ def set_files(files, noWaitAfter: nil, timeout: nil)
20
+ @element_handle.set_input_files(files, noWaitAfter: noWaitAfter, timeout: timeout)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ module Playwright
2
+ class HttpHeaders
3
+ # @param headers [Hash]
4
+ def initialize(headers)
5
+ @headers = headers
6
+ end
7
+
8
+ def as_serialized
9
+ @headers.map do |key, value|
10
+ { name: key, value: value }
11
+ end
12
+ end
13
+
14
+ def self.parse_serialized(serialized_headers)
15
+ new(serialized_headers.map do |header|
16
+ [header['name'].downcase, header['value']]
17
+ end.to_h)
18
+ end
19
+ end
20
+ end
@@ -36,7 +36,7 @@ module Playwright
36
36
 
37
37
  private def mime_type_for(filepath)
38
38
  mime_types = MIME::Types.type_for(filepath)
39
- mime_types.first || 'application/octet-stream'
39
+ mime_types.first.to_s || 'application/octet-stream'
40
40
  end
41
41
  end
42
42
  end
@@ -47,6 +47,21 @@ module Playwright
47
47
  )
48
48
  ValueParser.new(value).parse
49
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
50
65
  end
51
66
  end
52
67
  end
@@ -47,6 +47,21 @@ module Playwright
47
47
  )
48
48
  ValueParser.new(value).parse
49
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
50
65
  end
51
66
  end
52
67
  end
@@ -61,7 +61,7 @@ module Playwright
61
61
  end
62
62
 
63
63
  if hash.key?('o')
64
- return hash['o'].map { |key, value| [key, parse_hash(value)].to_h }
64
+ return hash['o'].map { |obj| [obj['k'], parse_hash(obj['v'])] }.to_h
65
65
  end
66
66
 
67
67
  if hash.key?('h')
@@ -44,7 +44,7 @@ module Playwright
44
44
  when Array
45
45
  { a: value.map { |v| serialize_value(v) } }
46
46
  when Hash
47
- { o: value.map { |key, v| [key, serialize_value(v)] }.to_h }
47
+ { o: value.map { |key, v| { k: key, v: serialize_value(v) } } }
48
48
  else
49
49
  raise ArgumentError.new("Unexpected value: #{value}")
50
50
  end
@@ -1,5 +1,9 @@
1
1
  module Playwright
2
- define_input_type :Keyboard do
2
+ define_api_implementation :KeyboardImpl do
3
+ def initialize(channel)
4
+ @channel = channel
5
+ end
6
+
3
7
  def down(key)
4
8
  @channel.send_message_to_server('keyboardDown', key: key)
5
9
  nil
@@ -0,0 +1,7 @@
1
+ module Playwright
2
+ define_api_implementation :MouseImpl do
3
+ def initialize(channel)
4
+ @channel = channel
5
+ end
6
+ end
7
+ end
@@ -1,27 +1,34 @@
1
1
  module Playwright
2
2
  class PlaywrightApi
3
- # Wrap ChannelOwner.
3
+ # Wrap ChannelOwner / ApiImplementation.
4
4
  # Playwright::ChannelOwners::XXXXX will be wrapped as Playwright::XXXXX
5
+ # Playwright::YYYYImpl will be wrapped as Playwright::YYYY
5
6
  # Playwright::XXXXX is automatically generated by development/generate_api
6
7
  #
7
- # @param channel_owner [ChannelOwner]
8
+ # @param channel_owner [ChannelOwner|ApiImplementation]
8
9
  # @note Intended for internal use only.
9
- def self.from_channel_owner(channel_owner)
10
- Factory.new(channel_owner, 'ChannelOwners').create
10
+ def self.wrap(channel_owner_or_api_implementation)
11
+ case channel_owner_or_api_implementation
12
+ when ChannelOwner
13
+ ChannelOwnerWrapper.new(channel_owner_or_api_implementation).wrap
14
+ when ApiImplementation
15
+ ApiImplementationWrapper.new(channel_owner_or_api_implementation).wrap
16
+ else
17
+ nil
18
+ end
11
19
  end
12
20
 
13
- class Factory
14
- def initialize(impl, module_name)
21
+ class ChannelOwnerWrapper
22
+ def initialize(impl)
15
23
  impl_class_name = impl.class.name
16
- unless impl_class_name.include?("::#{module_name}::")
17
- raise "#{impl_class_name} is not #{module_name}"
24
+ unless impl_class_name.include?("::ChannelOwners::")
25
+ raise "#{impl_class_name} is not ChannelOwners"
18
26
  end
19
27
 
20
28
  @impl = impl
21
- @module_name = module_name
22
29
  end
23
30
 
24
- def create
31
+ def wrap
25
32
  api_class = detect_class_for(@impl.class)
26
33
  if api_class
27
34
  api_class.new(@impl)
@@ -33,11 +40,11 @@ module Playwright
33
40
  private
34
41
 
35
42
  def expected_class_name_for(klass)
36
- klass.name.split("::#{@module_name}::").last
43
+ klass.name.split("::ChannelOwners::").last
37
44
  end
38
45
 
39
46
  def superclass_exist?(klass)
40
- ![::Playwright::ChannelOwner, ::Playwright::InputType, Object].include?(klass.superclass)
47
+ ![::Playwright::ChannelOwner, Object].include?(klass.superclass)
41
48
  end
42
49
 
43
50
  def detect_class_for(klass)
@@ -52,7 +59,44 @@ module Playwright
52
59
  end
53
60
  end
54
61
 
55
- # @param impl [Playwright::ChannelOwner|Playwright::InputType]
62
+ class ApiImplementationWrapper
63
+ def initialize(impl)
64
+ impl_class_name = impl.class.name
65
+ unless impl_class_name.end_with?("Impl")
66
+ raise "#{impl_class_name} is not Impl"
67
+ end
68
+
69
+ @impl = impl
70
+ end
71
+
72
+ def wrap
73
+ api_class = detect_class_for(@impl.class)
74
+ if api_class
75
+ api_class.new(@impl)
76
+ else
77
+ raise NotImplementedError.new("Playwright::#{expected_class_name_for(@impl.class)} is not implemented")
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def expected_class_name_for(klass)
84
+ # KeyboardImpl -> Keyboard
85
+ # MouseImpl -> Mouse
86
+ klass.name[0...-4].split("::").last
87
+ end
88
+
89
+ def detect_class_for(klass)
90
+ class_name = expected_class_name_for(klass)
91
+ if ::Playwright.const_defined?(class_name)
92
+ ::Playwright.const_get(class_name)
93
+ else
94
+ nil
95
+ end
96
+ end
97
+ end
98
+
99
+ # @param impl [Playwright::ChannelOwner|Playwright::ApiImplementation]
56
100
  def initialize(impl)
57
101
  @impl = impl
58
102
  end
@@ -72,15 +116,10 @@ module Playwright
72
116
  end
73
117
 
74
118
  private def wrap_impl(object)
75
- case object
76
- when ChannelOwner
77
- PlaywrightApi.from_channel_owner(object)
78
- when InputType
79
- Factory.new(object, 'InputTypes').create
80
- when Array
119
+ if object.is_a?(Array)
81
120
  object.map { |obj| wrap_impl(obj) }
82
121
  else
83
- object
122
+ ::Playwright::PlaywrightApi.wrap(object) || object
84
123
  end
85
124
  end
86
125
 
@@ -1,7 +1,18 @@
1
1
  module Playwright
2
2
  class SelectOptionValues
3
- def initialize(values)
4
- @params = convert(values)
3
+ def initialize(element: nil, index: nil, value: nil, label: nil)
4
+ @params =
5
+ if element
6
+ convert(elmeent)
7
+ elsif index
8
+ convert(index)
9
+ elsif value
10
+ convert(value)
11
+ elsif label
12
+ convert(label)
13
+ else
14
+ {}
15
+ end
5
16
  end
6
17
 
7
18
  # @return [Hash]
@@ -10,8 +21,7 @@ module Playwright
10
21
  end
11
22
 
12
23
  private def convert(values)
13
- return {} unless values
14
- return convert([values]) unless values.is_a?('Array')
24
+ return convert([values]) unless values.is_a?(Enumerable)
15
25
  return {} if values.empty?
16
26
  values.each_with_index do |value, index|
17
27
  unless values
@@ -13,7 +13,7 @@ module Playwright
13
13
  end
14
14
 
15
15
  def timeout
16
- @default_timeout || @parent&.navigation_timeout || DEFAULT_TIMEOUT
16
+ @default_timeout || @parent&.timeout || DEFAULT_TIMEOUT
17
17
  end
18
18
  end
19
19
  end
@@ -0,0 +1,7 @@
1
+ module Playwright
2
+ define_api_implementation :TouchscreenImpl do
3
+ def initialize(channel)
4
+ @channel = channel
5
+ end
6
+ end
7
+ end
@@ -3,12 +3,12 @@ module Playwright
3
3
  module PrepareBrowserContextOptions
4
4
  # @see https://github.com/microsoft/playwright/blob/5a2cfdbd47ed3c3deff77bb73e5fac34241f649d/src/client/browserContext.ts#L265
5
5
  private def prepare_browser_context_options(params)
6
- if params[:viewport] == 0
7
- params.delete(:viewport)
6
+ if params[:noViewport] == 0
7
+ params.delete(:noViewport)
8
8
  params[:noDefaultViewport] = true
9
9
  end
10
10
  if params[:extraHTTPHeaders]
11
- # TODO
11
+ params[:extraHTTPHeaders] = ::Playwright::HttpHeaders.new(params[:extraHTTPHeaders]).as_serialized
12
12
  end
13
13
  if params[:storageState].is_a?(String)
14
14
  params[:storageState] = JSON.parse(File.read(params[:storageState]))
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Playwright
4
- VERSION = '0.0.7'
4
+ VERSION = '0.2.1'
5
5
  end