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.
- checksums.yaml +4 -4
- data/README.md +7 -5
- data/lib/playwright.rb +4 -0
- data/lib/playwright/channel_owner.rb +13 -0
- data/lib/playwright/channel_owners/browser.rb +7 -14
- data/lib/playwright/channel_owners/browser_context.rb +43 -0
- data/lib/playwright/channel_owners/console_message.rb +0 -4
- data/lib/playwright/channel_owners/frame.rb +121 -7
- data/lib/playwright/channel_owners/page.rb +218 -0
- data/lib/playwright/connection.rb +6 -8
- data/lib/playwright/event_emitter.rb +13 -0
- data/lib/playwright/javascript.rb +13 -0
- data/lib/playwright/javascript/expression.rb +30 -0
- data/lib/playwright/javascript/function.rb +30 -0
- data/lib/playwright/javascript/value_parser.rb +75 -0
- data/lib/playwright/javascript/value_serializer.rb +54 -0
- data/lib/playwright/playwright_api.rb +8 -4
- data/lib/playwright/timeout_settings.rb +19 -0
- data/lib/playwright/utils.rb +19 -0
- data/lib/playwright/version.rb +1 -1
- data/lib/playwright/wait_helper.rb +73 -0
- data/lib/playwright_api/binding_call.rb +18 -0
- data/lib/playwright_api/browser.rb +18 -0
- data/lib/playwright_api/browser_context.rb +27 -4
- data/lib/playwright_api/browser_type.rb +18 -0
- data/lib/playwright_api/chromium_browser_context.rb +18 -0
- data/lib/playwright_api/console_message.rb +18 -0
- data/lib/playwright_api/element_handle.rb +19 -0
- data/lib/playwright_api/file_chooser.rb +1 -0
- data/lib/playwright_api/frame.rb +36 -12
- data/lib/playwright_api/js_handle.rb +18 -0
- data/lib/playwright_api/page.rb +40 -21
- data/lib/playwright_api/playwright.rb +18 -0
- data/lib/playwright_api/request.rb +18 -0
- data/lib/playwright_api/response.rb +18 -0
- data/lib/playwright_api/selectors.rb +18 -0
- data/playwright.gemspec +2 -0
- 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(
|
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
|
data/lib/playwright/version.rb
CHANGED
@@ -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
|