playwright-ruby-client 0.0.4 → 0.0.5

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -5
  3. data/lib/playwright.rb +4 -0
  4. data/lib/playwright/channel_owner.rb +13 -0
  5. data/lib/playwright/channel_owners/browser.rb +7 -14
  6. data/lib/playwright/channel_owners/browser_context.rb +43 -0
  7. data/lib/playwright/channel_owners/console_message.rb +0 -4
  8. data/lib/playwright/channel_owners/frame.rb +121 -7
  9. data/lib/playwright/channel_owners/page.rb +218 -0
  10. data/lib/playwright/connection.rb +6 -8
  11. data/lib/playwright/event_emitter.rb +13 -0
  12. data/lib/playwright/javascript.rb +13 -0
  13. data/lib/playwright/javascript/expression.rb +30 -0
  14. data/lib/playwright/javascript/function.rb +30 -0
  15. data/lib/playwright/javascript/value_parser.rb +75 -0
  16. data/lib/playwright/javascript/value_serializer.rb +54 -0
  17. data/lib/playwright/playwright_api.rb +8 -4
  18. data/lib/playwright/timeout_settings.rb +19 -0
  19. data/lib/playwright/utils.rb +19 -0
  20. data/lib/playwright/version.rb +1 -1
  21. data/lib/playwright/wait_helper.rb +73 -0
  22. data/lib/playwright_api/binding_call.rb +18 -0
  23. data/lib/playwright_api/browser.rb +18 -0
  24. data/lib/playwright_api/browser_context.rb +27 -4
  25. data/lib/playwright_api/browser_type.rb +18 -0
  26. data/lib/playwright_api/chromium_browser_context.rb +18 -0
  27. data/lib/playwright_api/console_message.rb +18 -0
  28. data/lib/playwright_api/element_handle.rb +19 -0
  29. data/lib/playwright_api/file_chooser.rb +1 -0
  30. data/lib/playwright_api/frame.rb +36 -12
  31. data/lib/playwright_api/js_handle.rb +18 -0
  32. data/lib/playwright_api/page.rb +40 -21
  33. data/lib/playwright_api/playwright.rb +18 -0
  34. data/lib/playwright_api/request.rb +18 -0
  35. data/lib/playwright_api/response.rb +18 -0
  36. data/lib/playwright_api/selectors.rb +18 -0
  37. data/playwright.gemspec +2 -0
  38. metadata +38 -2
@@ -192,12 +192,6 @@ module Playwright
192
192
  end
193
193
  initializer = replace_guids_with_channels(initializer)
194
194
 
195
- params = [
196
- parent,
197
- type,
198
- guid,
199
- initializer,
200
- ]
201
195
  class_name = case type
202
196
  when 'Browser'
203
197
  case initializer['name']
@@ -215,7 +209,6 @@ module Playwright
215
209
  if browser_name == 'chromium'
216
210
  'ChromiumBrowserContext'
217
211
  else
218
- params << browser_name
219
212
  'BrowserContext'
220
213
  end
221
214
  else
@@ -224,7 +217,12 @@ module Playwright
224
217
 
225
218
  result =
226
219
  begin
227
- ChannelOwners.const_get(class_name).new(*params)
220
+ ChannelOwners.const_get(class_name).new(
221
+ parent,
222
+ type,
223
+ guid,
224
+ initializer,
225
+ )
228
226
  rescue NameError
229
227
  raise "Missing type #{type}"
230
228
  end
@@ -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,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,30 @@
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
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
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
+ end
29
+ end
30
+ 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 { |key, value| [key, parse_hash(value)].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
@@ -0,0 +1,54 @@
1
+ module Playwright
2
+ module JavaScript
3
+ class ValueSerializer
4
+ def initialize(ruby_value)
5
+ @value = ruby_value
6
+ end
7
+
8
+ # @return [Hash]
9
+ def serialize
10
+ @handles = []
11
+ { value: serialize_value(@value), handles: @handles }
12
+ end
13
+
14
+ # ref: https://github.com/microsoft/playwright/blob/b45905ae3f1a066a8ecb358035ce745ddd21cf3a/src/protocol/serializers.ts#L84
15
+ # ref: https://github.com/microsoft/playwright-python/blob/25a99d53e00e35365cf5113b9525272628c0e65f/playwright/_impl/_js_handle.py#L99
16
+ private def serialize_value(value)
17
+ case value
18
+ when JSHandle
19
+ index = @handles.count
20
+ @handles << v
21
+ { h: index }
22
+ when nil
23
+ { v: 'undefined' }
24
+ when Float::NAN
25
+ { v: 'NaN'}
26
+ when Float::INFINITY
27
+ { v: 'Infinity' }
28
+ when -Float::INFINITY
29
+ { v: '-Infinity' }
30
+ when true, false
31
+ { b: v }
32
+ when Numeric
33
+ { n: v }
34
+ when String
35
+ { s: v }
36
+ when Time
37
+ require 'time'
38
+ { d: v.utc.iso8601 }
39
+ when Regexp
40
+ flags = []
41
+ flags << 'ms' if (v.options & Regexp::MULTILINE) != 0
42
+ flags << 'i' if (v.options & Regexp::IGNORECASE) != 0
43
+ { r: { p: v.source, f: flags.join('') } }
44
+ when Array
45
+ { a: v.map { |value| serialize_value(value) } }
46
+ when Hash
47
+ { o: v.map { |key, value| [key, serialize_value(value)] }.to_h }
48
+ else
49
+ raise ArgumentError.new("Unexpected value: #{value}")
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -10,8 +10,6 @@ module Playwright
10
10
  Factory.new(channel_owner).create
11
11
  end
12
12
 
13
- private
14
-
15
13
  class Factory
16
14
  def initialize(channel_owner)
17
15
  channel_owner_class_name = channel_owner.class.name
@@ -54,15 +52,21 @@ module Playwright
54
52
  @channel_owner = channel_owner
55
53
  end
56
54
 
55
+ def ==(other)
56
+ @channel_owner.to_s == other.instance_variable_get(:'@channel_owner').to_s
57
+ end
58
+
57
59
  # @param block [Proc]
58
- def wrap_block_call(block)
60
+ private def wrap_block_call(block)
61
+ return nil unless block.is_a?(Proc)
62
+
59
63
  -> (*args) {
60
64
  wrapped_args = args.map { |arg| wrap_channel_owner(arg) }
61
65
  block.call(*wrapped_args)
62
66
  }
63
67
  end
64
68
 
65
- def wrap_channel_owner(object)
69
+ private def wrap_channel_owner(object)
66
70
  if object.is_a?(ChannelOwner)
67
71
  PlaywrightApi.from_channel_owner(object)
68
72
  elsif object.is_a?(Array)
@@ -0,0 +1,19 @@
1
+ module Playwright
2
+ class TimeoutSettings
3
+ DEFAULT_TIMEOUT = 30000
4
+
5
+ def initialize(parent = nil)
6
+ @parent = parent
7
+ end
8
+
9
+ attr_writer :default_timeout, :default_navigation_timeout
10
+
11
+ def navigation_timeout
12
+ @default_navigation_timeout || @default_timeout || @parent&.navigation_timeout || EFAULT_TIMEOUT
13
+ end
14
+
15
+ def timeout
16
+ @default_timeout || @parent&.navigation_timeout || DEFAULT_TIMEOUT
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module Playwright
2
+ module Utils
3
+ module Errors
4
+ module SafeCloseError
5
+ # @param err [Exception]
6
+ private def safe_close_error?(err)
7
+ return true if err.is_a?(Transport::AlreadyDisconnectedError)
8
+
9
+ [
10
+ 'Browser has been closed',
11
+ 'Target page, context or browser has been closed',
12
+ ].any? do |closed_message|
13
+ err.message.end_with?(closed_message)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Playwright
4
- VERSION = '0.0.4'
4
+ VERSION = '0.0.5'
5
5
  end
@@ -0,0 +1,73 @@
1
+ module Playwright
2
+ # ref: https://github.com/microsoft/playwright-python/blob/30946ae3099d51f9b7f355f9ae7e8c04d748ce36/playwright/_impl/_wait_helper.py
3
+ # ref: https://github.com/microsoft/playwright/blob/01fb3a6045cbdb4b5bcba0809faed85bd917ab87/src/client/waiter.ts#L21
4
+ class WaitHelper
5
+ def initialize
6
+ @promise = Concurrent::Promises.resolvable_future
7
+ @registered_listeners = Set.new
8
+ end
9
+
10
+ def reject_on_event(emitter, event, error, predicate: nil)
11
+ listener = -> (*args) {
12
+ if !predicate || predicate.call(*args)
13
+ reject(error)
14
+ end
15
+ }
16
+ emitter.on(event, listener)
17
+ @registered_listeners << [emitter, event, listener]
18
+
19
+ self
20
+ end
21
+
22
+ def reject_on_timeout(timeout_ms, message)
23
+ return if timeout_ms <= 0
24
+
25
+ Concurrent::Promises.schedule(timeout_ms / 1000.0) {
26
+ reject(TimeoutError.new(message))
27
+ }
28
+
29
+ self
30
+ end
31
+
32
+ # @param [Playwright::EventEmitter]
33
+ # @param
34
+ def wait_for_event(emitter, event, predicate: nil)
35
+ listener = -> (*args) {
36
+ begin
37
+ if !predicate || predicate.call(*args)
38
+ fulfill(*args)
39
+ end
40
+ rescue => err
41
+ reject(err)
42
+ end
43
+ }
44
+ emitter.on(event, listener)
45
+ @registered_listeners << [emitter, event, listener]
46
+
47
+ self
48
+ end
49
+
50
+ attr_reader :promise
51
+
52
+ private def cleanup
53
+ @registered_listeners.each do |emitter, event, listener|
54
+ emitter.off(event, listener)
55
+ end
56
+ @registered_listeners.clear
57
+ end
58
+
59
+ private def fulfill(*args)
60
+ cleanup
61
+ unless @promise.resolved?
62
+ @promise.fulfill(args.first)
63
+ end
64
+ end
65
+
66
+ private def reject(error)
67
+ cleanup
68
+ unless @promise.resolved?
69
+ @promise.reject(error)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,5 +1,23 @@
1
1
  module Playwright
2
2
  # @nodoc
3
3
  class BindingCall < PlaywrightApi
4
+
5
+ # -- inherited from EventEmitter --
6
+ # @nodoc
7
+ def off(event, callback)
8
+ wrap_channel_owner(@channel_owner.off(event, callback))
9
+ end
10
+
11
+ # -- inherited from EventEmitter --
12
+ # @nodoc
13
+ def once(event, callback)
14
+ wrap_channel_owner(@channel_owner.once(event, callback))
15
+ end
16
+
17
+ # -- inherited from EventEmitter --
18
+ # @nodoc
19
+ def on(event, callback)
20
+ wrap_channel_owner(@channel_owner.on(event, callback))
21
+ end
4
22
  end
5
23
  end
@@ -132,5 +132,23 @@ module Playwright
132
132
  def after_initialize
133
133
  wrap_channel_owner(@channel_owner.after_initialize)
134
134
  end
135
+
136
+ # -- inherited from EventEmitter --
137
+ # @nodoc
138
+ def off(event, callback)
139
+ wrap_channel_owner(@channel_owner.off(event, callback))
140
+ end
141
+
142
+ # -- inherited from EventEmitter --
143
+ # @nodoc
144
+ def once(event, callback)
145
+ wrap_channel_owner(@channel_owner.once(event, callback))
146
+ end
147
+
148
+ # -- inherited from EventEmitter --
149
+ # @nodoc
150
+ def on(event, callback)
151
+ wrap_channel_owner(@channel_owner.on(event, callback))
152
+ end
135
153
  end
136
154
  end