appium_lib_core 13.1.4 → 13.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fce67854333b7c62714d35b70fe102dad1bbab1309cf872ac82d03f1f1acd6c2
4
- data.tar.gz: 551d97c4086fa641e12b735575caef458ab8aa5d06d4aa00ecbf4f6231a01eab
3
+ metadata.gz: a7cf3e87d71c8019b4e82f93fb85e53eac1c608ec974ab58caebe5ce6f686b7a
4
+ data.tar.gz: e9a86bced562076144dd592f92b5ea213c788a0ef9f42d14c4668b472a17d704
5
5
  SHA512:
6
- metadata.gz: 59c9920b5d951cc2205fc5968605a266f7db2bc14059bd77b4fea890d49785e0c30892b2703ddfed554fe24cf3463b10efd1caaf0d5e2b60acfd37ce9a01996c
7
- data.tar.gz: d0521ff1a53d38efaaea992138a6ca41f2bc586073e28f024854f7ce7fbea85105c9a927a007d44228b5f08b792224cbb067556fd1f705513ebd0b1229443937
6
+ metadata.gz: 9000c5f967b6ea90ef5f616e1edf8cd3d8075cd832f84d9493e11c1c8382c666be7b634e840d3c3eec58a1b0cd5823272ea0663e21133345a90ea21292c2fae3
7
+ data.tar.gz: 1acbfe6da6a953865497c429d2b37c5b50d9f802557b3a1cc0d2df1c2b08ce63a1a0abfb57d42943396a5e12dcdb2251d1591135dcbb607e28ccd3cfe91eba8f
data/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
  All notable changes to this project will be documented in this file.
3
3
  New entries are generated by Release Please. Historical commit-level details through 13.1.0 remain in `release_notes.md`.
4
4
 
5
+ ## [13.2.1](https://github.com/appium/ruby_lib_core/compare/v13.2.0...v13.2.1) (2026-10-27)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * support symbol webSocketUrl capability for BiDiBridge and guard nil bidi on close ([#729](https://github.com/appium/ruby_lib_core/issues/729)) ([d104352](https://github.com/appium/ruby_lib_core/commit/d104352112de6b4a181f6ae652ace738856ae083))
11
+
12
+ ## [13.2.0](https://github.com/appium/ruby_lib_core/compare/v13.1.4...v13.2.0) (2026-10-26)
13
+
14
+
15
+ ### Features
16
+
17
+ * Implement Cached Element for shouldUseCompactResponses and elementResponseAttributes ([#727](https://github.com/appium/ruby_lib_core/issues/727)) ([a0b22c5](https://github.com/appium/ruby_lib_core/commit/a0b22c5d8d757b787b44ba0468e6a216a7c46725))
18
+
5
19
  ## [13.1.4](https://github.com/appium/ruby_lib_core/compare/v13.1.3...v13.1.4) (2026-10-03)
6
20
 
7
21
 
data/README.md CHANGED
@@ -151,6 +151,31 @@ assert attached_driver.session_id == @driver.session_id
151
151
  attached_driver.page_source
152
152
  ```
153
153
 
154
+ ### Element response attributes
155
+
156
+ When the server is configured with `shouldUseCompactResponses: false` and
157
+ `elementResponseAttributes`, returned attributes are available through `element.cached`:
158
+
159
+ ```ruby
160
+ element = @driver.find_element(:accessibility_id, 'some accessibility')
161
+ element.cached.text # text returned with this element
162
+ element.cached['content-desc'] # also accepts attribute/content-desc
163
+ element.cached.attributes # returned attributes, excluding element IDs
164
+ element.text # fetches current text; does not update cached
165
+ ```
166
+
167
+ `cached` only reads the response snapshot. It cannot find elements or send commands.
168
+ Standard boolean fields (`enabled`, `displayed`, `selected`) normalize `"true"` and
169
+ `"false"` to booleans. `cached.rect`, `cached.location`, and `cached.size` return
170
+ Selenium Rectangle, Point, and Dimension objects; `cached.tag_name` reads `name`.
171
+ These conversions also apply to `cached[name]`. Explicit `cached['attribute/...']`
172
+ lookups and custom attributes retain their server types. `cached.attributes`
173
+ exposes the original server values.
174
+ Use `cached[name]` for optional attributes; it returns `nil` when absent. Dynamic
175
+ methods such as `cached.text` are available only for attributes in the response.
176
+ Standard accessors such as `cached.enabled?` and `cached.rect` return `nil` when
177
+ their data is absent.
178
+
154
179
  ### Capabilities
155
180
 
156
181
  Read [Appium/Core/Driver](https://rubydoc.info/github/appium/ruby_lib_core/Appium/Core/Driver) to catch up with available capabilities.
@@ -80,11 +80,11 @@ module Appium
80
80
  def quit
81
81
  super
82
82
  ensure
83
- bidi.close
83
+ bidi&.close
84
84
  end
85
85
 
86
86
  def close
87
- execute(:close_window).tap { |handles| bidi.close if handles.empty? }
87
+ execute(:close_window).tap { |handles| bidi&.close if handles.empty? }
88
88
  end
89
89
 
90
90
  # steep:ignore:end
@@ -148,6 +148,13 @@ module Appium
148
148
 
149
149
  private
150
150
 
151
+ # Preserve the response until Element initialization, including for script results.
152
+ # Selenium calls this immediately before constructing an element. Keep nil for
153
+ # non-element hashes so ordinary script results and shadow roots stay intact.
154
+ def element_id_from(response)
155
+ response if super
156
+ end
157
+
151
158
  def selenium_bridge_accepts_url?
152
159
  ::Selenium::WebDriver::Remote::Bridge.instance_method(:initialize).parameters.any? do |_, name|
153
160
  name == :url
@@ -86,7 +86,7 @@ module Appium
86
86
 
87
87
  raise ::Appium::Core::Error::ArgumentError, "Unable to create a driver with parameters: #{opts}" unless opts.empty?
88
88
 
89
- @has_bidi = capabilities && capabilities['webSocketUrl'] ? true : false
89
+ @has_bidi = capabilities && (capabilities['webSocketUrl'] || capabilities[:webSocketUrl]) ? true : false
90
90
  bridge_clzz = @has_bidi ? ::Appium::Core::Base::BiDiBridge : ::Appium::Core::Base::Bridge
91
91
  # steep:ignore:start
92
92
  bridge = bridge_clzz.new(**bridge_opts)
@@ -17,6 +17,86 @@ module Appium
17
17
  # Implement useful features for element.
18
18
  # Patch for Selenium Webdriver.
19
19
  class Element < ::Selenium::WebDriver::Element
20
+ # Attributes returned with an element response. This object has no bridge:
21
+ # reading it never sends a command or refreshes the returned values.
22
+ class Cache
23
+ attr_reader :attributes
24
+
25
+ def initialize(attributes = {})
26
+ @attributes = attributes.dup.freeze
27
+ end
28
+
29
+ # Normalize standard response fields; arbitrary attribute/... values stay raw.
30
+ # Missing attributes return nil. The original values remain in attributes.
31
+ def attribute(name)
32
+ key = attribute_key(name)
33
+ value = @attributes[key]
34
+ case name.to_s
35
+ when 'enabled', 'displayed', 'selected'
36
+ return true if value == 'true'
37
+ return false if value == 'false'
38
+ when 'rect'
39
+ return ::Selenium::WebDriver::Rectangle.new(value['x'], value['y'], value['width'], value['height']) if value.is_a?(Hash)
40
+ end
41
+ value
42
+ end
43
+
44
+ alias [] attribute
45
+
46
+ def enabled?
47
+ attribute('enabled')
48
+ end
49
+
50
+ alias enabled enabled?
51
+
52
+ def displayed?
53
+ attribute('displayed')
54
+ end
55
+
56
+ alias displayed displayed?
57
+
58
+ def selected?
59
+ attribute('selected')
60
+ end
61
+
62
+ alias selected selected?
63
+
64
+ def rect
65
+ attribute('rect')
66
+ end
67
+
68
+ def location
69
+ rectangle = rect
70
+ ::Selenium::WebDriver::Point.new(rectangle.x, rectangle.y) if rectangle
71
+ end
72
+
73
+ def size
74
+ rectangle = rect
75
+ ::Selenium::WebDriver::Dimension.new(rectangle.width, rectangle.height) if rectangle
76
+ end
77
+
78
+ def tag_name
79
+ attribute('name')
80
+ end
81
+
82
+ def method_missing(name, *args, &block)
83
+ return super unless args.empty? && block.nil? && respond_to_missing?(name)
84
+
85
+ attribute(name.to_s.delete_suffix('?').tr('_', '-'))
86
+ end
87
+
88
+ def respond_to_missing?(name, include_private = false)
89
+ @attributes.key?(attribute_key(name.to_s.delete_suffix('?').tr('_', '-'))) || super
90
+ end
91
+
92
+ private
93
+
94
+ def attribute_key(name)
95
+ key = name.to_s
96
+ @attributes.key?("attribute/#{key}") ? "attribute/#{key}" : key
97
+ end
98
+ end
99
+
20
100
  include ::Appium::Core::Base::TakesScreenshot
21
101
 
22
102
  # steep:ignore:start
@@ -32,6 +112,24 @@ module Appium
32
112
  #
33
113
  attr_reader :id
34
114
 
115
+ def initialize(bridge, id, cached = nil)
116
+ if id.is_a?(Hash)
117
+ # Preserve any extra attributes included in the element response.
118
+ cached = Cache.new(id.except('ELEMENT', ELEMENT_KEY))
119
+ id = id['ELEMENT'] || id[ELEMENT_KEY]
120
+ end
121
+ super(bridge, id)
122
+ @cached = cached
123
+ end
124
+
125
+ # Snapshot from elementResponseAttributes with shouldUseCompactResponses=false.
126
+ # Normal element calls still query the server and do not update this snapshot.
127
+ # Standard fields use Element API types; cached.attributes holds server values.
128
+ # @return [Appium::Core::Element::Cache]
129
+ def cached
130
+ @cached ||= Cache.new
131
+ end
132
+
35
133
  # Returns the value of attributes like below. Read each platform to know more details.
36
134
  #
37
135
  # uiautomator2: https://github.com/appium/appium-uiautomator2-server/blob/203cc7e57ce477f3cff5d95b135d1b3450a6033a/app/src/main/java/io/appium/uiautomator2/utils/Attribute.java#L19
@@ -29,7 +29,7 @@ module Appium
29
29
  @delegate.find_element_by how, what, parent
30
30
  end
31
31
 
32
- ::Appium::Core::Element.new self, e.ref.last
32
+ ::Appium::Core::Element.new self, e.ref.last, e.cached
33
33
  end
34
34
 
35
35
  def find_elements_by(how, what, parent = nil)
@@ -37,7 +37,7 @@ module Appium
37
37
  @delegate.find_elements_by(how, what, parent)
38
38
  end
39
39
 
40
- es.map { |e| ::Appium::Core::Element.new self, e.ref.last }
40
+ es.map { |e| ::Appium::Core::Element.new self, e.ref.last, e.cached }
41
41
  end
42
42
 
43
43
  private
@@ -14,7 +14,7 @@
14
14
 
15
15
  module Appium
16
16
  module Core
17
- VERSION = '13.1.4' unless defined? ::Appium::Core::VERSION
18
- DATE = '2026-10-03' unless defined? ::Appium::Core::DATE # x-release-please-date
17
+ VERSION = '13.2.1' unless defined? ::Appium::Core::VERSION
18
+ DATE = '2026-10-27' unless defined? ::Appium::Core::DATE # x-release-please-date
19
19
  end
20
20
  end
@@ -3,6 +3,8 @@ module Selenium
3
3
  class Element
4
4
  ELEMENT_KEY: ::String
5
5
 
6
+ def initialize: (untyped bridge, untyped id) -> void
7
+
6
8
  def inspect: () -> untyped
7
9
 
8
10
  def ==: (untyped other) -> bool
@@ -105,6 +105,8 @@ module Appium
105
105
 
106
106
  private
107
107
 
108
+ def element_id_from: (untyped response) -> untyped
109
+
108
110
  def selenium_bridge_accepts_url?: () -> bool
109
111
 
110
112
  def camel_case: (untyped str_or_sym) -> untyped
@@ -2,9 +2,37 @@ module Appium
2
2
  module Core
3
3
  # Element class inheriting from Selenium::WebDriver::Element
4
4
  class Element < ::Selenium::WebDriver::Element
5
+ class Cache
6
+ attr_reader attributes: Hash[String, untyped]
7
+
8
+ def initialize: (?Hash[String, untyped] attributes) -> void
9
+ def attribute: (String | Symbol name) -> untyped
10
+ alias [] attribute
11
+ def enabled?: () -> bool?
12
+ alias enabled enabled?
13
+ def displayed?: () -> bool?
14
+ alias displayed displayed?
15
+ def selected?: () -> bool?
16
+ alias selected selected?
17
+ def rect: () -> untyped
18
+ def location: () -> untyped
19
+ def size: () -> untyped
20
+ def tag_name: () -> String?
21
+ def method_missing: (Symbol name, *untyped args) ?{ (?) -> untyped } -> untyped
22
+ def respond_to_missing?: (Symbol name, ?bool include_private) -> bool
23
+
24
+ private
25
+
26
+ def attribute_key: (String | Symbol name) -> String
27
+ end
28
+
5
29
  include ::Appium::Core::Base::TakesScreenshot
6
30
 
7
- def initialize: (untyped bridge, untyped id) -> void
31
+ @cached: Cache?
32
+
33
+ def initialize: (untyped bridge, untyped id, ?Cache? cached) -> void
34
+
35
+ def cached: () -> Cache
8
36
 
9
37
  attr_reader id: Array[untyped]
10
38
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appium_lib_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 13.1.4
4
+ version: 13.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kazuaki MATSUO