playwright-ruby-client 0.0.7 → 0.2.1

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