appium_failure_helper 0.6.0 → 0.6.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: cb96de86c564a4b997d4d1a4320e831e3d4f28a3f8d7e97623b347d855588303
4
- data.tar.gz: 2462b34e6f3c01ea63a8a68d9c1814fd4138dc2c0dbcdbef0aecb8d6ba82fece
3
+ metadata.gz: c0e272f2150ce88e42b3b414d0afeb9d4e2309311bd271d77bf912ac3967ad03
4
+ data.tar.gz: 982e97ea6c9a43da40ea9315f554bc734b076b20aaf68b61f1cef83bf3455349
5
5
  SHA512:
6
- metadata.gz: ac755fa2ec04c3038c27c9ded178be9b6499de3c7a9cc70c96a5dfebd2fd07d6304eb236720cc4e4dfb1f31807b731c50512973af875596925eef72a9290f1b0
7
- data.tar.gz: 410501dec6ffa017effc75796cafb04b83a371bcd6f4b5b466b60f70339193b64f743112870e88cade771ed0ef70f4b0626fd4a5fc845173225d7cb4952052a5
6
+ metadata.gz: 8d431703babb06ffde65176ec6b16233738790261efbee46376f2b6bb90f1fd42717505d4ab6c78b454bf61bc3e656c2f8e9e1ef3936ea8841b585ec152e51f3
7
+ data.tar.gz: d7df2cb2f3757ae8298ce7b8057e11ea66b0112ba38fb15e01e6e8bcb73b3dad26035bcfdeba8dc97e823bf608de787b2b47c3dceb28d0b988525812b4730ebd
@@ -39,7 +39,6 @@ module AppiumFailureHelper
39
39
  begin
40
40
  self.setup_logger unless @@logger
41
41
 
42
- # Remove a pasta reports_failure ao iniciar uma nova execução
43
42
  FileUtils.rm_rf("reports_failure")
44
43
  @@logger.info("Pasta 'reports_failure' removida para uma nova execução.")
45
44
 
@@ -49,7 +48,6 @@ module AppiumFailureHelper
49
48
  FileUtils.mkdir_p(output_folder)
50
49
  @@logger.info("Pasta de saída criada: #{output_folder}")
51
50
 
52
- # Captura o Base64 e salva o PNG
53
51
  screenshot_base64 = driver.screenshot_as(:base64)
54
52
  screenshot_path = "#{output_folder}/screenshot_#{timestamp}.png"
55
53
  File.open(screenshot_path, 'wb') do |f|
@@ -67,7 +65,6 @@ module AppiumFailureHelper
67
65
 
68
66
  failed_element_info = self.extract_info_from_exception(exception)
69
67
 
70
- # --- Processamento de todos os elementos ---
71
68
  seen_elements = {}
72
69
  all_elements_suggestions = []
73
70
  doc.xpath('//*').each do |node|
@@ -85,7 +82,6 @@ module AppiumFailureHelper
85
82
  end
86
83
  end
87
84
 
88
- # --- Geração do Relatório FOCADO (1) ---
89
85
  targeted_report = {
90
86
  failed_element: failed_element_info,
91
87
  similar_elements: [],
@@ -101,14 +97,12 @@ module AppiumFailureHelper
101
97
  end
102
98
  @@logger.info("Análise direcionada salva em #{targeted_yaml_path}")
103
99
 
104
- # --- Geração do Relatório COMPLETO (2) ---
105
100
  full_dump_yaml_path = "#{output_folder}/all_elements_dump_#{timestamp}.yaml"
106
101
  File.open(full_dump_yaml_path, 'w') do |f|
107
102
  f.write(YAML.dump(all_elements_suggestions))
108
103
  end
109
104
  @@logger.info("Dump completo da página salvo em #{full_dump_yaml_path}")
110
105
 
111
- # --- Geração do Relatório HTML (3) ---
112
106
  html_report_path = "#{output_folder}/report_#{timestamp}.html"
113
107
  html_content = self.generate_html_report(targeted_report, all_elements_suggestions, screenshot_base64, platform, timestamp)
114
108
  File.write(html_report_path, html_content)
@@ -122,47 +116,98 @@ module AppiumFailureHelper
122
116
  private
123
117
 
124
118
  def self.setup_logger
125
- @@logger = Logger.new(STDOUT)
119
+ @@logger ||= Logger.new(STDOUT)
126
120
  @@logger.level = Logger::INFO
127
121
  @@logger.formatter = proc do |severity, datetime, progname, msg|
128
122
  "#{datetime.strftime('%Y-%m-%d %H:%M:%S')} [#{severity}] #{msg}\n"
129
123
  end
130
124
  end
131
125
 
132
- def self.extract_info_from_exception(exception)
133
- message = exception.message
134
- info = {}
135
-
136
- # Corrigido: Usando múltiplos padrões para extração robusta (resolvendo o problema do YAML vazio)
137
- patterns = [
138
- /(?:could not be found|cannot find element) using (.+)=['"]?(.+)['"]?/i,
139
- /no such element: Unable to locate element: {"method":"([^"]+)","selector":"([^"]+)"}/i,
140
- /(?:An element with the selector |element with the selector |selector |element with the |element identified by )(.+?) (?:could not be found|was not found|not found|not be located)/i,
141
- /(?:with the resource-id|with the accessibility-id) ['"](.+?)['"]/i
142
- ]
143
-
144
- patterns.each do |pattern|
145
- match = message.match(pattern)
146
- if match
147
- selector_value = match.captures.last.strip
148
- selector_type = match[1]&.strip || 'Unknown'
149
-
150
- info[:selector_type] = selector_type
151
- info[:selector_value] = selector_value.gsub(/['"]/, '')
152
- return info
126
+ def self.extract_info_from_exception(exception)
127
+ message = exception.to_s
128
+ info = {}
129
+
130
+ # normaliza tipos capturados para nomes previsíveis
131
+ normalize_type = lambda do |t|
132
+ return 'unknown' unless t
133
+ t = t.to_s.downcase.strip
134
+ t = t.gsub(/["']/, '')
135
+ case t
136
+ when 'cssselector', 'css selector' then 'css'
137
+ when 'classname', 'class name' then 'class name'
138
+ when 'accessibilityid', 'accessibility-id', 'accessibility id' then 'accessibility-id'
139
+ when 'resourceid', 'resource-id', 'resource id', 'id' then 'resource-id'
140
+ when 'contentdesc', 'content-desc', 'content desc' then 'content-desc'
141
+ else
142
+ t.gsub(/\s+/, '_').gsub(/[^a-z0-9_\-]/, '')
143
+ end
144
+ end
145
+
146
+ patterns = [
147
+ # ChromeDriver/Selenium JSON style:
148
+ # no such element: Unable to locate element: {"method":"xpath","selector":"//..."}
149
+ /no such element: Unable to locate element:\s*\{\s*["']?method["']?\s*:\s*["']?([^"'\}]+)["']?\s*,\s*["']?selector["']?\s*:\s*["']?([^"']+)["']?\s*\}/i,
150
+
151
+ # By.xpath: //..., By.id: "foo"
152
+ /By\.(xpath|id|css selector|cssSelector|name|class name|className):\s*['"]?(.+?)['"]?(?:\s|$)/i,
153
+
154
+ # Generic "using <type>=<value>" or using <type>: '<value>'
155
+ /using\s+([a-zA-Z0-9_\-:]+)\s*[=:]\s*['"]?(.+?)['"]?(?:\s|$)/i,
156
+
157
+ # "An element with the selector '...' was not found"
158
+ /An element with (?:the )?selector ['"](.+?)['"] (?:could not be found|was not found|not found|not be located)/i,
159
+
160
+ # "with the resource-id 'xyz'" or "with the accessibility-id 'abc'"
161
+ /with the (resource-id|accessibility[- ]?id|content-?desc|label|name)\s*[:=]?\s*['"](.+?)['"]/i,
162
+
163
+ # "Unable to find element by: id 'xyz'"
164
+ /Unable to find element by:\s*([a-zA-Z0-9_\- ]+)\s*[:=]?\s*['"]?(.+?)['"]?(?:\s|$)/i,
165
+
166
+ # Fallback to any "selector: '...'" occurence
167
+ /selector['"]?\s*[:=]?\s*['"](.+?)['"]/i
168
+ ]
169
+
170
+ patterns.each do |pattern|
171
+ if (m = message.match(pattern))
172
+ caps = m.captures.compact
173
+ if caps.length >= 2
174
+ raw_type = caps[0].to_s.strip
175
+ raw_value = caps[1].to_s.strip
176
+ info[:selector_type] = normalize_type.call(raw_type)
177
+ info[:selector_value] = raw_value.gsub(/\A['"]|['"]\z/, '')
178
+ else
179
+ info[:selector_type] = 'unknown'
180
+ info[:selector_value] = caps[0].to_s.strip.gsub(/\A['"]|['"]\z/, '')
153
181
  end
182
+ info[:raw_message] = message[0, 1000]
183
+ return info
154
184
  end
155
- info
156
185
  end
157
186
 
187
+ # tentativa extra: By.<tipo>:<valor> em qualquer lugar da mensagem
188
+ if (m = message.match(/By\.([a-zA-Z0-9_\- ]+):\s*['"]?(.+?)['"]?/i))
189
+ info[:selector_type] = normalize_type.call(m[1])
190
+ info[:selector_value] = m[2].to_s.strip
191
+ info[:raw_message] = message[0,1000]
192
+ return info
193
+ end
194
+
195
+ # fallback final: retorna a mensagem inteira recortada (útil para debug)
196
+ info[:selector_type] = 'unknown'
197
+ info[:selector_value] = message.strip[0, 500]
198
+ info[:raw_message] = message[0,1000]
199
+ info
200
+ end
201
+
202
+
158
203
  def self.find_similar_elements(doc, failed_info, platform)
159
204
  similar_elements = []
160
205
  doc.xpath('//*').each do |node|
161
206
  next if node.name == 'hierarchy'
162
207
  attrs = node.attributes.transform_values(&:value)
163
208
 
164
- # Lógica aprimorada para comparação insensível a maiúsculas/minúsculas
165
- selector_value = failed_info[:selector_value].to_s.downcase
209
+ selector_value = failed_info[:selector_value].to_s.downcase.strip
210
+
166
211
  is_similar = case platform
167
212
  when 'android'
168
213
  (attrs['resource-id']&.downcase&.include?(selector_value) ||
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppiumFailureHelper
4
- VERSION = "0.6.0"
4
+ VERSION = "0.6.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appium_failure_helper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Nascimento
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-09-23 00:00:00.000000000 Z
11
+ date: 2025-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri