playwright-ruby-client 0.0.3 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +119 -12
- data/docs/api_coverage.md +354 -0
- data/lib/playwright.rb +8 -0
- data/lib/playwright/channel_owner.rb +16 -2
- data/lib/playwright/channel_owners/android.rb +10 -1
- data/lib/playwright/channel_owners/android_device.rb +163 -0
- data/lib/playwright/channel_owners/browser.rb +22 -29
- data/lib/playwright/channel_owners/browser_context.rb +43 -0
- data/lib/playwright/channel_owners/console_message.rb +21 -0
- data/lib/playwright/channel_owners/element_handle.rb +314 -0
- data/lib/playwright/channel_owners/frame.rb +466 -7
- data/lib/playwright/channel_owners/js_handle.rb +55 -0
- data/lib/playwright/channel_owners/page.rb +353 -5
- data/lib/playwright/channel_owners/request.rb +90 -0
- data/lib/playwright/channel_owners/webkit_browser.rb +1 -1
- data/lib/playwright/connection.rb +15 -14
- data/lib/playwright/errors.rb +1 -1
- data/lib/playwright/event_emitter.rb +13 -0
- data/lib/playwright/input_files.rb +42 -0
- data/lib/playwright/input_type.rb +19 -0
- data/lib/playwright/input_types/android_input.rb +19 -0
- data/lib/playwright/input_types/keyboard.rb +32 -0
- data/lib/playwright/input_types/mouse.rb +4 -0
- data/lib/playwright/input_types/touchscreen.rb +4 -0
- data/lib/playwright/javascript.rb +13 -0
- data/lib/playwright/javascript/expression.rb +67 -0
- data/lib/playwright/javascript/function.rb +67 -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 +45 -25
- data/lib/playwright/select_option_values.rb +32 -0
- data/lib/playwright/timeout_settings.rb +19 -0
- data/lib/playwright/url_matcher.rb +19 -0
- data/lib/playwright/utils.rb +37 -0
- data/lib/playwright/version.rb +1 -1
- data/lib/playwright/wait_helper.rb +73 -0
- data/lib/playwright_api/accessibility.rb +60 -6
- data/lib/playwright_api/android.rb +33 -0
- data/lib/playwright_api/android_device.rb +78 -0
- data/lib/playwright_api/android_input.rb +25 -0
- data/lib/playwright_api/binding_call.rb +18 -0
- data/lib/playwright_api/browser.rb +136 -44
- data/lib/playwright_api/browser_context.rb +378 -51
- data/lib/playwright_api/browser_type.rb +137 -55
- data/lib/playwright_api/cdp_session.rb +32 -7
- data/lib/playwright_api/chromium_browser_context.rb +31 -0
- data/lib/playwright_api/console_message.rb +27 -7
- data/lib/playwright_api/dialog.rb +47 -3
- data/lib/playwright_api/download.rb +29 -5
- data/lib/playwright_api/element_handle.rb +429 -143
- data/lib/playwright_api/file_chooser.rb +13 -2
- data/lib/playwright_api/frame.rb +633 -179
- data/lib/playwright_api/js_handle.rb +97 -17
- data/lib/playwright_api/keyboard.rb +152 -24
- data/lib/playwright_api/mouse.rb +28 -3
- data/lib/playwright_api/page.rb +1183 -317
- data/lib/playwright_api/playwright.rb +174 -13
- data/lib/playwright_api/request.rb +115 -30
- data/lib/playwright_api/response.rb +22 -3
- data/lib/playwright_api/route.rb +63 -4
- data/lib/playwright_api/selectors.rb +29 -7
- data/lib/playwright_api/touchscreen.rb +2 -1
- data/lib/playwright_api/video.rb +11 -1
- data/lib/playwright_api/web_socket.rb +5 -5
- data/lib/playwright_api/worker.rb +29 -5
- data/playwright.gemspec +3 -0
- metadata +68 -2
@@ -55,10 +55,13 @@ module Playwright
|
|
55
55
|
method: method,
|
56
56
|
params: replace_channels_with_guids(params),
|
57
57
|
}
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
begin
|
59
|
+
@transport.send_message(message)
|
60
|
+
rescue => err
|
61
|
+
@callbacks.delete(id)
|
62
|
+
callback.reject(err)
|
63
|
+
raise unless err.is_a?(Transport::AlreadyDisconnectedError)
|
64
|
+
end
|
62
65
|
end
|
63
66
|
|
64
67
|
callback
|
@@ -132,7 +135,7 @@ module Playwright
|
|
132
135
|
unless object
|
133
136
|
raise "Cannot find object to dispose: #{guid}"
|
134
137
|
end
|
135
|
-
object.dispose
|
138
|
+
object.send(:dispose!)
|
136
139
|
return
|
137
140
|
end
|
138
141
|
|
@@ -153,7 +156,7 @@ module Playwright
|
|
153
156
|
end
|
154
157
|
|
155
158
|
if payload.is_a?(Channel)
|
156
|
-
{ guid: payload.guid }
|
159
|
+
return { guid: payload.guid }
|
157
160
|
end
|
158
161
|
|
159
162
|
if payload.is_a?(Hash)
|
@@ -192,12 +195,6 @@ module Playwright
|
|
192
195
|
end
|
193
196
|
initializer = replace_guids_with_channels(initializer)
|
194
197
|
|
195
|
-
params = [
|
196
|
-
parent,
|
197
|
-
type,
|
198
|
-
guid,
|
199
|
-
initializer,
|
200
|
-
]
|
201
198
|
class_name = case type
|
202
199
|
when 'Browser'
|
203
200
|
case initializer['name']
|
@@ -215,7 +212,6 @@ module Playwright
|
|
215
212
|
if browser_name == 'chromium'
|
216
213
|
'ChromiumBrowserContext'
|
217
214
|
else
|
218
|
-
params << browser_name
|
219
215
|
'BrowserContext'
|
220
216
|
end
|
221
217
|
else
|
@@ -224,7 +220,12 @@ module Playwright
|
|
224
220
|
|
225
221
|
result =
|
226
222
|
begin
|
227
|
-
ChannelOwners.const_get(class_name).new(
|
223
|
+
ChannelOwners.const_get(class_name).new(
|
224
|
+
parent,
|
225
|
+
type,
|
226
|
+
guid,
|
227
|
+
initializer,
|
228
|
+
)
|
228
229
|
rescue NameError
|
229
230
|
raise "Missing type #{type}"
|
230
231
|
end
|
data/lib/playwright/errors.rb
CHANGED
@@ -3,7 +3,7 @@ module Playwright
|
|
3
3
|
# ref: https://github.com/microsoft/playwright-python/blob/0b4a980fed366c4c1dee9bfcdd72662d629fdc8d/playwright/_impl/_helper.py#L155
|
4
4
|
def self.parse(error_payload)
|
5
5
|
if error_payload['name'] == 'TimeoutError'
|
6
|
-
|
6
|
+
TimeoutError.new(
|
7
7
|
message: error_payload['message'],
|
8
8
|
stack: error_payload['stack'].split("\n"),
|
9
9
|
)
|
@@ -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,42 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'mime/types'
|
3
|
+
|
4
|
+
module Playwright
|
5
|
+
class InputFiles
|
6
|
+
def initialize(files)
|
7
|
+
@params = convert(files)
|
8
|
+
end
|
9
|
+
|
10
|
+
def as_params
|
11
|
+
@params
|
12
|
+
end
|
13
|
+
|
14
|
+
private def convert(files)
|
15
|
+
return convert([files]) unless files.is_a?(Array)
|
16
|
+
|
17
|
+
files.map do |file|
|
18
|
+
case file
|
19
|
+
when String
|
20
|
+
{
|
21
|
+
name: File.basename(file),
|
22
|
+
mimeType: mime_type_for(file),
|
23
|
+
buffer: Base64.strict_encode64(File.read(file)),
|
24
|
+
}
|
25
|
+
when File
|
26
|
+
{
|
27
|
+
name: File.basename(file.path),
|
28
|
+
mimeType: mime_type_for(file.path),
|
29
|
+
buffer: Base64.strict_encode64(file.read),
|
30
|
+
}
|
31
|
+
else
|
32
|
+
raise ArgumentError.new('file must be a String or File.')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private def mime_type_for(filepath)
|
38
|
+
mime_types = MIME::Types.type_for(filepath)
|
39
|
+
mime_types.first || 'application/octet-stream'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Playwright
|
2
|
+
class InputType
|
3
|
+
def initialize(channel)
|
4
|
+
@channel = channel
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
# namespace declaration
|
9
|
+
module InputTypes ; end
|
10
|
+
|
11
|
+
def self.define_input_type(class_name, &block)
|
12
|
+
klass = Class.new(InputType)
|
13
|
+
klass.class_eval(&block) if block
|
14
|
+
InputTypes.const_set(class_name, klass)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# load subclasses
|
19
|
+
Dir[File.join(__dir__, 'input_types', '*.rb')].each { |f| require f }
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Playwright
|
2
|
+
define_input_type :AndroidInput do
|
3
|
+
def type(text)
|
4
|
+
@channel.send_message_to_server('inputType', text: text)
|
5
|
+
end
|
6
|
+
|
7
|
+
def press(key)
|
8
|
+
@channel.send_message_to_server('inputPress', key: key)
|
9
|
+
end
|
10
|
+
|
11
|
+
def tap_point(point)
|
12
|
+
@channel.send_message_to_server('inputTap', point: point)
|
13
|
+
end
|
14
|
+
|
15
|
+
def drag(from, to, steps)
|
16
|
+
@channel.send_message_to_server('inputDrag', from: from, to: to, steps: steps)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Playwright
|
2
|
+
define_input_type :Keyboard do
|
3
|
+
def down(key)
|
4
|
+
@channel.send_message_to_server('keyboardDown', key: key)
|
5
|
+
nil
|
6
|
+
end
|
7
|
+
|
8
|
+
def up(key)
|
9
|
+
@channel.send_message_to_server('keyboardDown', key: key)
|
10
|
+
end
|
11
|
+
|
12
|
+
def insert_text(text)
|
13
|
+
@channel.send_message_to_server('keyboardInsertText', text: text)
|
14
|
+
end
|
15
|
+
|
16
|
+
def type(text, delay: nil)
|
17
|
+
params = {
|
18
|
+
text: text,
|
19
|
+
delay: delay,
|
20
|
+
}.compact
|
21
|
+
@channel.send_message_to_server('keyboardType', params)
|
22
|
+
end
|
23
|
+
|
24
|
+
def press(key, delay: nil)
|
25
|
+
params = {
|
26
|
+
key: key,
|
27
|
+
delay: delay,
|
28
|
+
}.compact
|
29
|
+
@channel.send_message_to_server('keyboardPress', params)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -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,67 @@
|
|
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
|
+
|
29
|
+
def eval_on_selector(channel, selector)
|
30
|
+
value = channel.send_message_to_server(
|
31
|
+
'evalOnSelector',
|
32
|
+
selector: selector,
|
33
|
+
expression: @expression,
|
34
|
+
isFunction: false,
|
35
|
+
arg: @serialized_arg,
|
36
|
+
)
|
37
|
+
ValueParser.new(value).parse
|
38
|
+
end
|
39
|
+
|
40
|
+
def eval_on_selector_all(channel, selector)
|
41
|
+
value = channel.send_message_to_server(
|
42
|
+
'evalOnSelectorAll',
|
43
|
+
selector: selector,
|
44
|
+
expression: @expression,
|
45
|
+
isFunction: false,
|
46
|
+
arg: @serialized_arg,
|
47
|
+
)
|
48
|
+
ValueParser.new(value).parse
|
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
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,67 @@
|
|
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
|
+
|
29
|
+
def eval_on_selector(channel, selector)
|
30
|
+
value = channel.send_message_to_server(
|
31
|
+
'evalOnSelector',
|
32
|
+
selector: selector,
|
33
|
+
expression: @definition,
|
34
|
+
isFunction: true,
|
35
|
+
arg: @serialized_arg,
|
36
|
+
)
|
37
|
+
ValueParser.new(value).parse
|
38
|
+
end
|
39
|
+
|
40
|
+
def eval_on_selector_all(channel, selector)
|
41
|
+
value = channel.send_message_to_server(
|
42
|
+
'evalOnSelectorAll',
|
43
|
+
selector: selector,
|
44
|
+
expression: @definition,
|
45
|
+
isFunction: true,
|
46
|
+
arg: @serialized_arg,
|
47
|
+
)
|
48
|
+
ValueParser.new(value).parse
|
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
|
65
|
+
end
|
66
|
+
end
|
67
|
+
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 { |obj| [obj['k'], parse_hash(obj['v'])] }.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
|