playwright-ruby-client 0.0.9 → 0.1.0

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/docs/api_coverage.md +12 -12
  3. data/lib/playwright.rb +3 -2
  4. data/lib/playwright/{input_types/android_input.rb → android_input_impl.rb} +5 -1
  5. data/lib/playwright/api_implementation.rb +18 -0
  6. data/lib/playwright/channel.rb +7 -0
  7. data/lib/playwright/channel_owners/android_device.rb +1 -1
  8. data/lib/playwright/channel_owners/download.rb +27 -0
  9. data/lib/playwright/channel_owners/element_handle.rb +2 -2
  10. data/lib/playwright/channel_owners/frame.rb +2 -2
  11. data/lib/playwright/channel_owners/page.rb +33 -3
  12. data/lib/playwright/errors.rb +1 -1
  13. data/lib/playwright/event_emitter.rb +4 -0
  14. data/lib/playwright/event_emitter_proxy.rb +49 -0
  15. data/lib/playwright/file_chooser_impl.rb +23 -0
  16. data/lib/playwright/input_files.rb +1 -1
  17. data/lib/playwright/{input_types/keyboard.rb → keyboard_impl.rb} +5 -1
  18. data/lib/playwright/mouse_impl.rb +7 -0
  19. data/lib/playwright/playwright_api.rb +59 -20
  20. data/lib/playwright/touchscreen_impl.rb +7 -0
  21. data/lib/playwright/version.rb +1 -1
  22. data/lib/playwright/wait_helper.rb +1 -1
  23. data/lib/playwright_api/android.rb +12 -8
  24. data/lib/playwright_api/android_device.rb +15 -11
  25. data/lib/playwright_api/android_input.rb +5 -5
  26. data/lib/playwright_api/binding_call.rb +10 -6
  27. data/lib/playwright_api/browser.rb +10 -6
  28. data/lib/playwright_api/browser_context.rb +10 -6
  29. data/lib/playwright_api/browser_type.rb +10 -6
  30. data/lib/playwright_api/chromium_browser_context.rb +10 -6
  31. data/lib/playwright_api/console_message.rb +10 -6
  32. data/lib/playwright_api/download.rb +28 -6
  33. data/lib/playwright_api/element_handle.rb +10 -6
  34. data/lib/playwright_api/file_chooser.rb +4 -4
  35. data/lib/playwright_api/frame.rb +10 -6
  36. data/lib/playwright_api/js_handle.rb +10 -6
  37. data/lib/playwright_api/keyboard.rb +5 -5
  38. data/lib/playwright_api/page.rb +10 -6
  39. data/lib/playwright_api/playwright.rb +10 -6
  40. data/lib/playwright_api/request.rb +12 -8
  41. data/lib/playwright_api/response.rb +10 -6
  42. data/lib/playwright_api/selectors.rb +10 -6
  43. metadata +10 -7
  44. data/lib/playwright/input_type.rb +0 -19
  45. data/lib/playwright/input_types/mouse.rb +0 -4
  46. data/lib/playwright/input_types/touchscreen.rb +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eccb7fbb7a1fba3116ceaa61893b164850dc7f21eb45cac637da161bc137120a
4
- data.tar.gz: ceeab7bc64c7a743ef3b682dda05271ff49eb3727ad7b47ed722f527aedacba0
3
+ metadata.gz: a8e526e28e95751a08697bc93b14ec33f4f82e827b8adf4dc45aed4bf44dd1d2
4
+ data.tar.gz: 647596db5628a1014a7816bb43d2ca41a21eeb6cf8c7263dea701d8f9a6ba4e7
5
5
  SHA512:
6
- metadata.gz: 1414afc026223b47750708cd2f30dab6453aebb5b3ce06f97a7f4a51c793f6b2b57ef307ffc0b31df2f91158d7d7e40623f443846b1653ca8778b7a67bfe7a47
7
- data.tar.gz: 33cf15f92e23a118264627067f534d4379876090b6a3bf74fb8c67253e2905b575495c548247144f82ed282b8b86a1c891e89ffdf8e51423202bd38ce03a5bb2
6
+ metadata.gz: 0051320ee1e574680ebc6be4d5cf359a28bd8ada9a737fc60d734f017c43142b6f267553f15d0e1ac0ed368f12d57dac5a887a39695f59a11f774344405dcfd1
7
+ data.tar.gz: 14f97b51a9462a8bfb587c5752eb13b3db0c132d0d56d7bdaab829389f2cb7c17bb879b171773e1e21259bb6b352f6020a5b7165be577bb147b22b5b0155c28e
data/docs/api_coverage.md CHANGED
@@ -113,12 +113,12 @@
113
113
 
114
114
  * ~~snapshot~~
115
115
 
116
- ## ~~FileChooser~~
116
+ ## FileChooser
117
117
 
118
- * ~~element~~
119
- * ~~multiple?~~
120
- * ~~page~~
121
- * ~~set_files~~
118
+ * element
119
+ * multiple?
120
+ * page
121
+ * set_files
122
122
 
123
123
  ## Frame
124
124
 
@@ -195,15 +195,15 @@
195
195
  * ~~message~~
196
196
  * ~~type~~
197
197
 
198
- ## ~~Download~~
198
+ ## Download
199
199
 
200
200
  * ~~create_read_stream~~
201
- * ~~delete~~
202
- * ~~failure~~
203
- * ~~path~~
204
- * ~~save_as~~
205
- * ~~suggested_filename~~
206
- * ~~url~~
201
+ * delete
202
+ * failure
203
+ * path
204
+ * save_as
205
+ * suggested_filename
206
+ * url
207
207
 
208
208
  ## ~~Video~~
209
209
 
data/lib/playwright.rb CHANGED
@@ -10,14 +10,15 @@ require 'concurrent'
10
10
  require 'playwright/errors'
11
11
  require 'playwright/events'
12
12
  require 'playwright/event_emitter'
13
+ require 'playwright/event_emitter_proxy'
13
14
  require 'playwright/javascript'
14
15
  require 'playwright/utils'
15
16
 
17
+ require 'playwright/api_implementation'
16
18
  require 'playwright/channel'
17
19
  require 'playwright/channel_owner'
18
20
  require 'playwright/http_headers'
19
21
  require 'playwright/input_files'
20
- require 'playwright/input_type'
21
22
  require 'playwright/connection'
22
23
  require 'playwright/select_option_values'
23
24
  require 'playwright/timeout_settings'
@@ -38,7 +39,7 @@ module Playwright
38
39
 
39
40
  playwright_promise = connection.async_wait_for_object_with_known_name('Playwright')
40
41
  Thread.new { connection.run }
41
- playwright = PlaywrightApi.from_channel_owner(playwright_promise.value!)
42
+ playwright = PlaywrightApi.wrap(playwright_promise.value!)
42
43
  begin
43
44
  block.call(playwright)
44
45
  ensure
@@ -1,5 +1,9 @@
1
1
  module Playwright
2
- define_input_type :AndroidInput do
2
+ define_api_implementation :AndroidInputImpl do
3
+ def initialize(channel)
4
+ @channel = channel
5
+ end
6
+
3
7
  def type(text)
4
8
  @channel.send_message_to_server('inputType', text: text)
5
9
  end
@@ -0,0 +1,18 @@
1
+ module Playwright
2
+ # Each Impl class include this module.
3
+ # Used for detecting whether the object is a XXXXImpl or not.
4
+ module ApiImplementation ; end
5
+
6
+ def self.define_api_implementation(class_name, &block)
7
+ klass = Class.new
8
+ klass.include(ApiImplementation)
9
+ klass.class_eval(&block) if block
10
+ if ::Playwright.const_defined?(class_name)
11
+ raise ArgumentError.new("Playwright::#{class_name} already exist. Choose another class name.")
12
+ end
13
+ ::Playwright.const_set(class_name, klass)
14
+ end
15
+ end
16
+
17
+ # load subclasses
18
+ Dir[File.join(__dir__, '*_impl.rb')].each { |f| require f }
@@ -24,5 +24,12 @@ module Playwright
24
24
  nil
25
25
  end
26
26
  end
27
+
28
+ # @param method [String]
29
+ # @param params [Hash]
30
+ def send_no_reply(method, params)
31
+ @connection.async_send_message_to_server(@guid, method, params)
32
+ nil
33
+ end
27
34
  end
28
35
  end
@@ -3,7 +3,7 @@ module Playwright
3
3
  include Utils::PrepareBrowserContextOptions
4
4
 
5
5
  def after_initialize
6
- @input = InputTypes::AndroidInput.new(@channel)
6
+ @input = AndroidInputImpl.new(@channel)
7
7
  end
8
8
 
9
9
  attr_reader :input
@@ -0,0 +1,27 @@
1
+ module Playwright
2
+ define_channel_owner :Download do
3
+ def url
4
+ @initializer['url']
5
+ end
6
+
7
+ def suggested_filename
8
+ @initializer['suggestedFilename']
9
+ end
10
+
11
+ def delete
12
+ @channel.send_message_to_server('delete')
13
+ end
14
+
15
+ def failure
16
+ @channel.send_message_to_server('failure')
17
+ end
18
+
19
+ def path
20
+ @channel.send_message_to_server('path')
21
+ end
22
+
23
+ def save_as(path)
24
+ @channel.send_message_to_server('saveAs', path: path)
25
+ end
26
+ end
27
+ end
@@ -182,8 +182,8 @@ module Playwright
182
182
  end
183
183
 
184
184
  def set_input_files(files, noWaitAfter: nil, timeout: nil)
185
- base_params = InputFiles.new(values).as_params
186
- params = base_params + { noWaitAfter: noWaitAfter, timeout: timeout }.compact
185
+ file_payloads = InputFiles.new(files).as_params
186
+ params = { files: file_payloads, noWaitAfter: noWaitAfter, timeout: timeout }.compact
187
187
  @channel.send_message_to_server('setInputFiles', params)
188
188
 
189
189
  nil
@@ -407,8 +407,8 @@ module Playwright
407
407
  end
408
408
 
409
409
  def set_input_files(selector, files, noWaitAfter: nil, timeout: nil)
410
- base_params = InputFiles.new(values).as_params
411
- params = base_params + { selector: selector, noWaitAfter: noWaitAfter, timeout: timeout }.compact
410
+ file_payloads = InputFiles.new(files).as_params
411
+ params = { files: file_payloads, selector: selector, noWaitAfter: noWaitAfter, timeout: timeout }.compact
412
412
  @channel.send_message_to_server('setInputFiles', params)
413
413
 
414
414
  nil
@@ -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 = InputTypes::Keyboard.new(@channel)
14
- @mouse = InputTypes::Mouse.new(@channel)
15
- @touchscreen = InputTypes::Touchscreen.new(@channel)
13
+ @keyboard = KeyboardImpl.new(@channel)
14
+ @mouse = MouseImpl.new(@channel)
15
+ @touchscreen = TouchscreenImpl.new(@channel)
16
16
 
17
17
  @viewport_size = @initializer['viewportSize']
18
18
  @closed = false
@@ -32,6 +32,13 @@ module Playwright
32
32
  @channel.on('download', ->(params) {
33
33
  emit(Events::Page::Download, ChannelOwners::Download.from(params['download']))
34
34
  })
35
+ @channel.on('fileChooser', ->(params) {
36
+ chooser = FileChooserImpl.new(
37
+ page: self,
38
+ element_handle: ChannelOwners::ElementHandle.from(params['element']),
39
+ is_multiple: params['isMultiple'])
40
+ emit(Events::Page::FileChooser, chooser)
41
+ })
35
42
  @channel.on('frameAttached', ->(params) {
36
43
  on_frame_attached(ChannelOwners::Frame.from(params['frame']))
37
44
  })
@@ -127,6 +134,29 @@ module Playwright
127
134
  end
128
135
  end
129
136
 
137
+ # @override
138
+ def on(event, callback)
139
+ if event == Events::Page::FileChooser && listener_count(event) == 0
140
+ @channel.send_no_reply('setFileChooserInterceptedNoReply', intercepted: true)
141
+ end
142
+ super
143
+ end
144
+
145
+ # @override
146
+ def once(event, callback)
147
+ if event == Events::Page::FileChooser && listener_count(event) == 0
148
+ @channel.send_no_reply('setFileChooserInterceptedNoReply', intercepted: true)
149
+ end
150
+ super
151
+ end
152
+
153
+ # @override
154
+ def off(event, callback)
155
+ super
156
+ if event == Events::Page::FileChooser && listener_count(event) == 0
157
+ @channel.send_no_reply('setFileChooserInterceptedNoReply', intercepted: false)
158
+ end
159
+ end
130
160
 
131
161
  def context
132
162
  @browser_context
@@ -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
@@ -44,6 +44,10 @@ module Playwright
44
44
  handled
45
45
  end
46
46
 
47
+ private def listener_count(event)
48
+ ((@__event_emitter ||= {})[event.to_s] ||= Set.new).count
49
+ end
50
+
47
51
  # @param event [String]
48
52
  # @param callback [Proc]
49
53
  def on(event, callback)
@@ -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
@@ -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
@@ -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