selenium-webdriver 4.45.0 → 4.46.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.
- checksums.yaml +4 -4
- data/CHANGES +7 -0
- data/bin/linux/selenium-manager +0 -0
- data/bin/macos/selenium-manager +0 -0
- data/bin/windows/selenium-manager.exe +0 -0
- data/lib/selenium/webdriver/bidi/protocol/bluetooth.rb +465 -0
- data/lib/selenium/webdriver/bidi/protocol/browser.rb +222 -0
- data/lib/selenium/webdriver/bidi/protocol/browsing_context.rb +694 -0
- data/lib/selenium/webdriver/bidi/protocol/domain.rb +40 -0
- data/lib/selenium/webdriver/bidi/protocol/emulation.rb +334 -0
- data/lib/selenium/webdriver/bidi/protocol/input.rb +281 -0
- data/lib/selenium/webdriver/bidi/protocol/log.rb +104 -0
- data/lib/selenium/webdriver/bidi/protocol/network.rb +637 -0
- data/lib/selenium/webdriver/bidi/protocol/permissions.rb +73 -0
- data/lib/selenium/webdriver/bidi/protocol/script.rb +874 -0
- data/lib/selenium/webdriver/bidi/protocol/session.rb +241 -0
- data/lib/selenium/webdriver/bidi/protocol/speculation.rb +56 -0
- data/lib/selenium/webdriver/bidi/protocol/storage.rb +157 -0
- data/lib/selenium/webdriver/bidi/protocol/user_agent_client_hints.rb +83 -0
- data/lib/selenium/webdriver/bidi/protocol/web_extension.rb +93 -0
- data/lib/selenium/webdriver/bidi/protocol.rb +39 -0
- data/lib/selenium/webdriver/bidi/serialization/record.rb +226 -0
- data/lib/selenium/webdriver/bidi/serialization/union.rb +114 -0
- data/lib/selenium/webdriver/bidi/serialization.rb +78 -0
- data/lib/selenium/webdriver/bidi/support/bidi_generate.rb +936 -0
- data/lib/selenium/webdriver/bidi/support/check_generated.rb +55 -0
- data/lib/selenium/webdriver/bidi/transport.rb +52 -0
- data/lib/selenium/webdriver/bidi.rb +3 -0
- data/lib/selenium/webdriver/chrome/driver.rb +3 -3
- data/lib/selenium/webdriver/common/client_config.rb +97 -0
- data/lib/selenium/webdriver/common/driver.rb +2 -2
- data/lib/selenium/webdriver/common/local_driver.rb +15 -4
- data/lib/selenium/webdriver/common/proxy.rb +1 -1
- data/lib/selenium/webdriver/common.rb +1 -0
- data/lib/selenium/webdriver/edge/driver.rb +3 -3
- data/lib/selenium/webdriver/firefox/driver.rb +3 -3
- data/lib/selenium/webdriver/ie/driver.rb +3 -3
- data/lib/selenium/webdriver/remote/bidi_bridge.rb +6 -1
- data/lib/selenium/webdriver/remote/bridge.rb +8 -10
- data/lib/selenium/webdriver/remote/driver.rb +12 -4
- data/lib/selenium/webdriver/remote/http/common.rb +25 -12
- data/lib/selenium/webdriver/remote/http/default.rb +56 -39
- data/lib/selenium/webdriver/safari/driver.rb +3 -3
- data/lib/selenium/webdriver/version.rb +1 -1
- metadata +25 -2
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
|
+
# or more contributor license agreements. See the NOTICE file
|
|
5
|
+
# distributed with this work for additional information
|
|
6
|
+
# regarding copyright ownership. The SFC licenses this file
|
|
7
|
+
# to you under the Apache License, Version 2.0 (the
|
|
8
|
+
# "License"); you may not use this file except in compliance
|
|
9
|
+
# with the License. You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing,
|
|
14
|
+
# software distributed under the License is distributed on an
|
|
15
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
+
# KIND, either express or implied. See the License for the
|
|
17
|
+
# specific language governing permissions and limitations
|
|
18
|
+
# under the License.
|
|
19
|
+
|
|
20
|
+
module Selenium
|
|
21
|
+
module WebDriver
|
|
22
|
+
class BiDi
|
|
23
|
+
module Serialization
|
|
24
|
+
# Immutable value type for the generated protocol classes. +Record.define(spec)+
|
|
25
|
+
# bakes each field's wire facts and returns a +::Data+ subclass with serialization.
|
|
26
|
+
#
|
|
27
|
+
# Cookie = Record.define(name: 'name', value: {wire_key: 'value', ref: 'Network::BytesValue'})
|
|
28
|
+
#
|
|
29
|
+
# @api private
|
|
30
|
+
class Record < ::Data
|
|
31
|
+
# Named Field, not Member, to avoid colliding with +::Data#members+.
|
|
32
|
+
Field = ::Data.define(:name, :wire_key, :nullable, :ref, :list, :fixed, :enum, :required, :primitive)
|
|
33
|
+
|
|
34
|
+
def self.define(**spec)
|
|
35
|
+
extensible = spec.delete(:extensible) || false
|
|
36
|
+
fields = spec.map { |name, meta| field(name, meta) }
|
|
37
|
+
names = fields.map(&:name)
|
|
38
|
+
names << :extensions if extensible
|
|
39
|
+
|
|
40
|
+
klass = super(*names)
|
|
41
|
+
fields.freeze
|
|
42
|
+
# Singleton methods are inherited by `X = Record.define(…)`; ivars would not.
|
|
43
|
+
klass.define_singleton_method(:fields) { fields }
|
|
44
|
+
klass.define_singleton_method(:extensible?) { extensible }
|
|
45
|
+
klass.include(Serializable)
|
|
46
|
+
# Capture ::Data's generated +new+, then prepend (not include) Deserializer so
|
|
47
|
+
# its +new+ overrides it — outbound +new+ adds validation, while inbound
|
|
48
|
+
# +from_json+ builds directly via the captured constructor. Bound to +self+ so a
|
|
49
|
+
# subclass builds itself, not the base.
|
|
50
|
+
data_new = klass.singleton_class.instance_method(:new)
|
|
51
|
+
klass.singleton_class.prepend(Deserializer)
|
|
52
|
+
klass.define_singleton_method(:construct) { |**attributes| data_new.bind_call(self, **attributes) }
|
|
53
|
+
klass.singleton_class.send(:private, :construct)
|
|
54
|
+
klass
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.field(name, meta)
|
|
58
|
+
meta = {wire_key: meta} if meta.is_a?(::String)
|
|
59
|
+
Field.new(name: name.to_sym, wire_key: meta.fetch(:wire_key, name.to_s),
|
|
60
|
+
nullable: meta[:nullable] || false, ref: meta[:ref],
|
|
61
|
+
list: meta[:list] || false, fixed: meta.fetch(:fixed, UNSET), enum: meta[:enum],
|
|
62
|
+
required: meta.fetch(:required, true), primitive: meta[:primitive])
|
|
63
|
+
end
|
|
64
|
+
private_class_method :field
|
|
65
|
+
|
|
66
|
+
# Inbound construction: the keyword +new+ (validated) and the wire +from_json+.
|
|
67
|
+
#
|
|
68
|
+
# @api private
|
|
69
|
+
module Deserializer
|
|
70
|
+
def new(**kwargs)
|
|
71
|
+
# Start from what was passed so ::Data's constructor rejects an unknown key, then fill
|
|
72
|
+
# each field with its value or UNSET (omitted), forcing fixed discriminators.
|
|
73
|
+
attributes = kwargs.dup
|
|
74
|
+
fields.each { |f| attributes[f.name] = fixed?(f) ? f.fixed : attributes.fetch(f.name, UNSET) }
|
|
75
|
+
attributes[:extensions] = kwargs.fetch(:extensions, {}) if extensible?
|
|
76
|
+
validate_values(attributes)
|
|
77
|
+
construct(**attributes)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Inbound: builds from the wire. A missing required field raises (in +wire_value+),
|
|
81
|
+
# enum tokens are mapped back to symbols and an unrecognized one raises (in +read+), and
|
|
82
|
+
# extra keys are captured (extensible) or ignored (closed) — strict on shape, lenient on extras.
|
|
83
|
+
def from_json(json_payload)
|
|
84
|
+
unless json_payload.is_a?(::Hash)
|
|
85
|
+
raise Error::WebDriverError, "#{name} expected an object on the wire, got #{json_payload.inspect}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
attributes = fields.to_h do |f|
|
|
89
|
+
[f.name, wire_value(f, json_payload)]
|
|
90
|
+
end
|
|
91
|
+
attributes[:extensions] = extra(json_payload) if extensible?
|
|
92
|
+
construct(**attributes)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
# Checks each field's value: a required field cannot be omitted (UNSET), a non-nullable
|
|
98
|
+
# field cannot be nil (nil is neither a value nor the UNSET omit-sentinel, so it would be
|
|
99
|
+
# silently dropped on the wire), and an enum field must be in its allowed set. The enum
|
|
100
|
+
# constant is resolved lazily so a cross-domain enum need not be loaded first. Outbound
|
|
101
|
+
# only (from +new+); inbound presence/enum are checked separately in +wire_value+/+read+.
|
|
102
|
+
def validate_values(attributes)
|
|
103
|
+
fields.each do |f|
|
|
104
|
+
value = attributes[f.name]
|
|
105
|
+
raise ::ArgumentError, "#{name}##{f.name} is required" if UNSET.equal?(value) && f.required
|
|
106
|
+
raise ::ArgumentError, "#{name}##{f.name} cannot be nil" if value.nil? && !f.nullable
|
|
107
|
+
next if value.nil? || UNSET.equal?(value)
|
|
108
|
+
|
|
109
|
+
check_outbound_shape(f, value)
|
|
110
|
+
Serialization.validate!("#{name}##{f.name}", value, Protocol.const_get(f.enum)) if f.enum
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Outbound mirror of check_shape: a list-typed arg must be an array, a scalar-shaped one
|
|
115
|
+
# (enum or ref, not a list) must not — a local ArgumentError, not a wire round-trip.
|
|
116
|
+
def check_outbound_shape(field, value)
|
|
117
|
+
return if field.list == value.is_a?(::Array)
|
|
118
|
+
return unless field.list || field.enum || field.ref
|
|
119
|
+
|
|
120
|
+
kind = field.list ? 'a list' : 'a single value'
|
|
121
|
+
raise ::ArgumentError, "#{name}##{field.name} expected #{kind}, got #{value.inspect}"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def fixed?(field)
|
|
125
|
+
!UNSET.equal?(field.fixed)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def wire_value(field, json_payload)
|
|
129
|
+
return field.fixed if fixed?(field)
|
|
130
|
+
return read(field, json_payload[field.wire_key]) if json_payload.key?(field.wire_key)
|
|
131
|
+
return UNSET unless field.required
|
|
132
|
+
|
|
133
|
+
raise Error::WebDriverError, "#{name}##{field.name} is required but was missing from the response"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def read(field, raw)
|
|
137
|
+
if raw.nil?
|
|
138
|
+
return raw if field.nullable
|
|
139
|
+
|
|
140
|
+
raise Error::WebDriverError, "#{name}##{field.name} received null but is not nullable"
|
|
141
|
+
end
|
|
142
|
+
check_shape(field, raw)
|
|
143
|
+
return Serialization.to_symbol("#{name}##{field.name}", raw, enum_hash(field)) if field.enum
|
|
144
|
+
|
|
145
|
+
if field.ref.nil?
|
|
146
|
+
check_primitive(field, raw) unless field.list
|
|
147
|
+
return raw
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
klass = (@refs ||= {})[field.name] ||= Protocol.const_get(field.ref)
|
|
151
|
+
field.list ? read_list(raw, klass) : klass.from_json(raw)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# A declared list must arrive as an array; a scalar-shaped field (enum or ref, not a
|
|
155
|
+
# list) must not. An opaque field carries no shape descriptor, so it passes through.
|
|
156
|
+
def check_shape(field, raw)
|
|
157
|
+
return if field.list == raw.is_a?(::Array)
|
|
158
|
+
return unless field.list || field.enum || field.ref
|
|
159
|
+
|
|
160
|
+
raise Error::WebDriverError,
|
|
161
|
+
"#{name}##{field.name} expected #{field.list ? 'a list' : 'a single value'}, got #{raw.inspect}"
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Ruby classes a checkable primitive admits. `number` is any Numeric (JSON has one
|
|
165
|
+
# number type); `integer` requires an Integer — a browser emits `5`, not `5.0`, for an
|
|
166
|
+
# integer (JS has no int/float split), so this rarely false-positives yet still rejects
|
|
167
|
+
# a genuine non-integer like 1.5. A field with no primitive descriptor is left unchecked.
|
|
168
|
+
PRIMITIVE_TYPES = {
|
|
169
|
+
'string' => [::String], 'boolean' => [::TrueClass, ::FalseClass],
|
|
170
|
+
'number' => [::Numeric], 'integer' => [::Integer]
|
|
171
|
+
}.freeze
|
|
172
|
+
|
|
173
|
+
def check_primitive(field, raw)
|
|
174
|
+
expected = PRIMITIVE_TYPES[field.primitive]
|
|
175
|
+
return if expected.nil? || expected.any? { |type| raw.is_a?(type) }
|
|
176
|
+
|
|
177
|
+
raise Error::WebDriverError, "#{name}##{field.name} expected #{field.primitive}, got #{raw.inspect}"
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def enum_hash(field)
|
|
181
|
+
(@enums ||= {})[field.name] ||= Protocol.const_get(field.enum)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Parses each element, recursing into nested lists (e.g. a map's [key, value] pairs)
|
|
185
|
+
# so their entries become typed too.
|
|
186
|
+
def read_list(raw, klass)
|
|
187
|
+
raw.map { |element| element.is_a?(::Array) ? read_list(element, klass) : klass.from_json(element) }
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def extra(json_payload)
|
|
191
|
+
known = (@wire_keys ||= fields.map(&:wire_key))
|
|
192
|
+
json_payload.except(*known)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# @api private
|
|
197
|
+
module Serializable
|
|
198
|
+
def self.as_json(value)
|
|
199
|
+
case value
|
|
200
|
+
when Serializable then value.as_json
|
|
201
|
+
when ::Array then value.map { |element| as_json(element) }
|
|
202
|
+
when ::Hash then value.transform_values { |element| as_json(element) }
|
|
203
|
+
else value
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Omit UNSET fields; emit null only for nullable ones.
|
|
208
|
+
def as_json(*)
|
|
209
|
+
payload = {}
|
|
210
|
+
self.class.fields.each do |f|
|
|
211
|
+
value = public_send(f.name)
|
|
212
|
+
next if UNSET.equal?(value)
|
|
213
|
+
next if value.nil? && !f.nullable
|
|
214
|
+
|
|
215
|
+
value = Serialization.to_wire(value, Protocol.const_get(f.enum)) if f.enum
|
|
216
|
+
payload[f.wire_key] = Serializable.as_json(value)
|
|
217
|
+
end
|
|
218
|
+
payload.merge!(extensions) if self.class.extensible? && !extensions.empty?
|
|
219
|
+
payload
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end # BiDi
|
|
225
|
+
end # WebDriver
|
|
226
|
+
end # Selenium
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
|
+
# or more contributor license agreements. See the NOTICE file
|
|
5
|
+
# distributed with this work for additional information
|
|
6
|
+
# regarding copyright ownership. The SFC licenses this file
|
|
7
|
+
# to you under the Apache License, Version 2.0 (the
|
|
8
|
+
# "License"); you may not use this file except in compliance
|
|
9
|
+
# with the License. You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing,
|
|
14
|
+
# software distributed under the License is distributed on an
|
|
15
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
+
# KIND, either express or implied. See the License for the
|
|
17
|
+
# specific language governing permissions and limitations
|
|
18
|
+
# under the License.
|
|
19
|
+
|
|
20
|
+
module Selenium
|
|
21
|
+
module WebDriver
|
|
22
|
+
class BiDi
|
|
23
|
+
module Serialization
|
|
24
|
+
# Resolves a wire payload to the right Data variant: a shared discriminator gives
|
|
25
|
+
# table dispatch; presence rules and a no-tag fallback cover unions without one.
|
|
26
|
+
# Subclassed (never instantiated) — each union holds its own dispatch table.
|
|
27
|
+
#
|
|
28
|
+
# class Locator < Serialization::Union
|
|
29
|
+
# discriminator 'type'
|
|
30
|
+
# variants('css' => 'BrowsingContext::CssLocator')
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# @api private
|
|
34
|
+
class Union
|
|
35
|
+
class << self
|
|
36
|
+
# values maps each variant's discriminator symbol to its wire token, so an
|
|
37
|
+
# inbound payload tag (a wire string) can be matched to the symbol-keyed table.
|
|
38
|
+
def discriminator(wire_key, values = {})
|
|
39
|
+
@discriminator = wire_key
|
|
40
|
+
@discriminator_values = values
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def variants(table) = @variants = table
|
|
44
|
+
def presence(rules) = @presence = rules
|
|
45
|
+
def fallback(path) = @fallback = path
|
|
46
|
+
|
|
47
|
+
# A non-Hash payload is a bare scalar arm (e.g. input.Origin's "viewport") with
|
|
48
|
+
# no object to dispatch on, so it is returned unchanged.
|
|
49
|
+
def from_json(json_payload)
|
|
50
|
+
return json_payload unless json_payload.is_a?(::Hash)
|
|
51
|
+
|
|
52
|
+
variant = select(json_payload)
|
|
53
|
+
unless variant
|
|
54
|
+
raise Error::WebDriverError,
|
|
55
|
+
"#{name} received a variant not in this Selenium's BiDi schema: #{json_payload.inspect}"
|
|
56
|
+
end
|
|
57
|
+
Protocol.const_get(variant).from_json(json_payload)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Outbound mirror of from_json: build the variant the command's kwargs describe
|
|
61
|
+
# so its typed as_json drives null-vs-absent per field (a flat hash through
|
|
62
|
+
# Transport cannot). Dispatch keys are wire names equal to their ruby kwarg
|
|
63
|
+
# (asserted at generation), so they match the kwargs by symbol. A mismatch here
|
|
64
|
+
# is a caller error (unlike an unknown inbound value), so it fails loudly.
|
|
65
|
+
def build(**kwargs)
|
|
66
|
+
variant = outbound_variant(kwargs) ||
|
|
67
|
+
raise(::ArgumentError, "no #{name} variant matches #{kwargs.inspect}")
|
|
68
|
+
klass = Protocol.const_get(variant)
|
|
69
|
+
# An omitted optional arrives as UNSET; forward only what was provided. A provided
|
|
70
|
+
# key that isn't a field of the chosen variant is an invalid combination for this union.
|
|
71
|
+
provided = kwargs.reject { |_, value| UNSET.equal?(value) }
|
|
72
|
+
invalid = provided.keys - klass.fields.map(&:name)
|
|
73
|
+
return klass.new(**provided) if invalid.empty?
|
|
74
|
+
|
|
75
|
+
raise ::ArgumentError, "invalid combination for #{name}: #{invalid.join(', ')}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
# The discriminator value may legitimately be null (e.g. script.NullValue's
|
|
81
|
+
# "null" tag), so it is matched by key presence.
|
|
82
|
+
def select(json_payload)
|
|
83
|
+
variant_for(payload_tag(json_payload)) { |k| json_payload.key?(k) }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# An explicit nil kwarg still counts as supplied; a non-nullable field set to nil is
|
|
87
|
+
# rejected at construction (Data.new), not here.
|
|
88
|
+
def outbound_variant(kwargs)
|
|
89
|
+
tag = @discriminator ? kwargs.fetch(@discriminator.to_sym, UNSET) : UNSET
|
|
90
|
+
variant_for(tag) { |k| kwargs.key?(k.to_sym) && !UNSET.equal?(kwargs[k.to_sym]) }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# The matching variant's ref, or nil when none matches (the fallback if declared).
|
|
94
|
+
def variant_for(tag, &supplied)
|
|
95
|
+
return @variants[tag] if !UNSET.equal?(tag) && @variants&.key?(tag)
|
|
96
|
+
|
|
97
|
+
@presence&.each { |path, keys| return path if keys.all?(&supplied) }
|
|
98
|
+
@fallback
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# The wire tag mapped back to its variant symbol (the table's key); an
|
|
102
|
+
# unrecognized tag falls through as-is so select misses and from_json raises.
|
|
103
|
+
def payload_tag(json_payload)
|
|
104
|
+
return UNSET unless @discriminator && json_payload.key?(@discriminator)
|
|
105
|
+
|
|
106
|
+
wire = json_payload[@discriminator]
|
|
107
|
+
@discriminator_values.key(wire) || wire
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end # BiDi
|
|
113
|
+
end # WebDriver
|
|
114
|
+
end # Selenium
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
|
+
# or more contributor license agreements. See the NOTICE file
|
|
5
|
+
# distributed with this work for additional information
|
|
6
|
+
# regarding copyright ownership. The SFC licenses this file
|
|
7
|
+
# to you under the Apache License, Version 2.0 (the
|
|
8
|
+
# "License"); you may not use this file except in compliance
|
|
9
|
+
# with the License. You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing,
|
|
14
|
+
# software distributed under the License is distributed on an
|
|
15
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
+
# KIND, either express or implied. See the License for the
|
|
17
|
+
# specific language governing permissions and limitations
|
|
18
|
+
# under the License.
|
|
19
|
+
|
|
20
|
+
module Selenium
|
|
21
|
+
module WebDriver
|
|
22
|
+
class BiDi
|
|
23
|
+
# Wire round-trip runtime for the generated protocol layer: the value-type bases
|
|
24
|
+
# (Record, Union), the omit sentinel (UNSET), and outbound enum validation.
|
|
25
|
+
#
|
|
26
|
+
# @api private
|
|
27
|
+
module Serialization
|
|
28
|
+
# Sentinel for an omitted optional: dropped from the payload entirely, vs nil which
|
|
29
|
+
# a nullable field serializes as wire null.
|
|
30
|
+
#
|
|
31
|
+
# @api private
|
|
32
|
+
UNSET = ::Object.new
|
|
33
|
+
def UNSET.inspect = 'UNSET'
|
|
34
|
+
UNSET.freeze
|
|
35
|
+
|
|
36
|
+
# Validates an outbound enum argument: +value+ is a symbol (or list of symbols) that
|
|
37
|
+
# must be a key of the enum hash (+{symbol => wire_token}+), so a bad value fails
|
|
38
|
+
# locally with a clear error instead of a round-trip. Outbound only; inbound wire
|
|
39
|
+
# tokens are mapped and checked separately in +to_symbol+.
|
|
40
|
+
#
|
|
41
|
+
# @api private
|
|
42
|
+
def self.validate!(name, value, enum)
|
|
43
|
+
return if UNSET.equal?(value) || value.nil?
|
|
44
|
+
|
|
45
|
+
elements = value.is_a?(::Array) ? value : [value]
|
|
46
|
+
invalid = elements.reject { |element| enum.key?(element) }
|
|
47
|
+
return if invalid.empty?
|
|
48
|
+
|
|
49
|
+
raise ::ArgumentError, "#{name} must be one of #{enum.keys.inspect}, got #{invalid.inspect}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Outbound: map a validated enum symbol (or list) to the wire token(s) to serialize.
|
|
53
|
+
#
|
|
54
|
+
# @api private
|
|
55
|
+
def self.to_wire(value, enum)
|
|
56
|
+
return value if UNSET.equal?(value) || value.nil?
|
|
57
|
+
|
|
58
|
+
value.is_a?(::Array) ? value.map { |element| enum.fetch(element) } : enum.fetch(value)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Inbound: map a wire token (or list) back to its enum symbol, raising on a token
|
|
62
|
+
# outside our schema so a non-compliant (or newer-than-schema) browser value fails
|
|
63
|
+
# loud instead of silently passing through untyped.
|
|
64
|
+
#
|
|
65
|
+
# @api private
|
|
66
|
+
def self.to_symbol(name, value, enum)
|
|
67
|
+
return value if value.nil?
|
|
68
|
+
return value.map { |element| to_symbol(name, element, enum) } if value.is_a?(::Array)
|
|
69
|
+
|
|
70
|
+
enum.key(value) || raise(Error::WebDriverError, "#{name} received an unknown value: #{value.inspect}")
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end # BiDi
|
|
74
|
+
end # WebDriver
|
|
75
|
+
end # Selenium
|
|
76
|
+
|
|
77
|
+
require 'selenium/webdriver/bidi/serialization/record'
|
|
78
|
+
require 'selenium/webdriver/bidi/serialization/union'
|