appium_failure_helper 1.1.7 → 1.2.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/README.md +72 -71
- data/lib/appium_failure_helper/handler.rb +10 -1
- data/lib/appium_failure_helper/report_generator.rb +9 -16
- data/lib/appium_failure_helper/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 58d069ff8c960c6fc6f012dc6990bce28cffce23f50e4152dae22cabfdb27deb
|
4
|
+
data.tar.gz: '06659b00652b380f0e8f77a10d8d6e024cb80df00e01b2b59a80f8921865819f'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '09ec8522e2732b4c8c9a044b551aabe0ba5890af4b8494c0872ccf680dd10909abcb4ad0e550f9bcfa2eeb600fb4a2a913d0494393b58c05e5c7b2254cded9a1'
|
7
|
+
data.tar.gz: 022c5386083b49e772e4a1d6147980ef1786e3a039ed882fe0f9f1300ffd8ff310259f55062d2a1820003a3a93efa8d2ab43bf48c840f4c9442454b2dcd44d89
|
data/README.md
CHANGED
@@ -1,28 +1,28 @@
|
|
1
|
-
# Diagnóstico Inteligente de Falhas
|
1
|
+
# Appium Failure Helper: Diagnóstico Inteligente de Falhas
|
2
2
|
|
3
3
|

|
4
|
-

|
5
5
|

|
6
6
|
|
7
|
-
Uma
|
7
|
+
Uma GEM de diagnóstico para testes Appium em Ruby, projetada para transformar falhas de automação em insights acionáveis. Quando um teste falha por não encontrar um elemento, esta ferramenta gera um relatório HTML detalhado, identificando a causa provável do erro e acelerando drasticamente o tempo de depuração.
|
8
8
|
|
9
9
|
## ✨ Principais Funcionalidades
|
10
10
|
|
11
|
-
* **
|
12
|
-
* **Análise de
|
13
|
-
|
14
|
-
|
15
|
-
*
|
16
|
-
*
|
17
|
-
*
|
18
|
-
* **
|
11
|
+
* **Diagnóstico por Triagem de Erros:** Identifica inteligentemente o *tipo* de falha (`NoSuchElementError`, `TimeoutError`, `Erro de Código Ruby`, `Falha de Asserção`, etc.) e gera um relatório específico e útil para cada cenário.
|
12
|
+
* **Análise de Código-Fonte:** Para erros "silenciosos" (onde a mensagem não contém o seletor), a GEM inspeciona o `stack trace` para encontrar o arquivo e a linha exatos do erro, extraindo o seletor diretamente do código-fonte.
|
13
|
+
* **Análise de Atributos Ponderados:** Em vez de uma simples comparação de strings, a GEM "desmonta" o seletor que falhou e o compara atributo por atributo com os elementos na tela, dando pesos diferentes para `resource-id`, `text`, etc., para encontrar o "candidato mais provável".
|
14
|
+
* **Relatórios Ricos e Interativos:** Gera um relatório HTML completo com:
|
15
|
+
* Screenshot da falha.
|
16
|
+
* Diagnóstico claro da causa provável, com sugestões acionáveis.
|
17
|
+
* Abas com "Análise Avançada" e um "Dump Completo" de todos os elementos da tela.
|
18
|
+
* **Altamente Configurável:** Permite a customização de caminhos para se adaptar a diferentes estruturas de projeto.
|
19
19
|
|
20
20
|
## 🚀 Instalação
|
21
21
|
|
22
22
|
Adicione esta linha ao `Gemfile` do seu projeto de automação:
|
23
23
|
|
24
24
|
```ruby
|
25
|
-
gem 'appium_failure_helper'
|
25
|
+
gem 'appium_failure_helper', git: 'URL_DO_SEU_REPOSITORIO_GIT' # Exemplo de instalação via Git
|
26
26
|
```
|
27
27
|
|
28
28
|
E então execute no seu terminal:
|
@@ -31,106 +31,107 @@ E então execute no seu terminal:
|
|
31
31
|
bundle install
|
32
32
|
```
|
33
33
|
|
34
|
-
##
|
34
|
+
## 🛠️ Uso e Configuração
|
35
35
|
|
36
|
-
|
36
|
+
A integração ideal envolve 3 passos:
|
37
37
|
|
38
|
-
|
38
|
+
### Passo 1: Configurar a GEM (Opcional)
|
39
|
+
|
40
|
+
No seu arquivo de inicialização (ex: `features/support/env.rb`), carregue a GEM e, se necessário, configure os caminhos onde seus elementos estão mapeados. Se nenhuma configuração for fornecida, a ferramenta usará os valores padrão.
|
39
41
|
|
40
42
|
```ruby
|
41
|
-
#
|
43
|
+
# features/support/env.rb
|
44
|
+
require 'appium_failure_helper'
|
42
45
|
|
43
46
|
AppiumFailureHelper.configure do |config|
|
44
|
-
# Caminho para a pasta que contém os arquivos de elementos.
|
45
47
|
# Padrão: 'features/elements'
|
46
48
|
config.elements_path = 'caminho/para/sua/pasta/de/elementos'
|
47
49
|
|
48
|
-
# Nome do arquivo principal de elementos Ruby.
|
49
50
|
# Padrão: 'elementLists.rb'
|
50
51
|
config.elements_ruby_file = 'meu_arquivo_de_elementos.rb'
|
51
52
|
end
|
52
53
|
```
|
53
54
|
|
54
|
-
###
|
55
|
-
|
56
|
-
| Parâmetro | Descrição | Valor Padrão |
|
57
|
-
| --------------------- | ------------------------------------------------------------------------------ | ------------------------ |
|
58
|
-
| `elements_path` | Path relativo à raiz do projeto para a pasta que contém os arquivos de elementos. | `'features/elements'` |
|
59
|
-
| `elements_ruby_file` | Nome do arquivo Ruby principal que define os elementos dentro da `elements_path`. | `'elementLists.rb'` |
|
60
|
-
|
61
|
-
## 🛠️ Uso no Cucumber
|
55
|
+
### Passo 2: Enriquecer as Exceções (Altamente Recomendado)
|
62
56
|
|
63
|
-
|
57
|
+
Para que a GEM consiga extrair o máximo de detalhes de uma falha (especialmente de erros genéricos como `TimeoutError` ou `NoSuchElementError` sem detalhes), é crucial que a exceção que ela recebe seja rica em informações. A melhor maneira de garantir isso é ajustar seus métodos de busca de elementos.
|
64
58
|
|
65
|
-
|
59
|
+
Crie ou ajuste um arquivo de helpers (ex: `features/support/appiumCustom.rb`) com a seguinte estrutura:
|
66
60
|
|
67
61
|
```ruby
|
68
|
-
# features/support/
|
62
|
+
# features/support/appiumCustom.rb
|
69
63
|
|
70
|
-
|
71
|
-
|
64
|
+
# Métodos públicos que seus Page Objects irão chamar
|
65
|
+
def find(el)
|
66
|
+
find_element_with_enriched_error(el)
|
67
|
+
end
|
72
68
|
|
73
|
-
|
74
|
-
|
69
|
+
def clickElement(el)
|
70
|
+
find_element_with_enriched_error(el).click
|
71
|
+
end
|
75
72
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
73
|
+
def waitForElementExist(el, timeout = 10)
|
74
|
+
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
75
|
+
begin
|
76
|
+
wait.until { $driver.find_elements(el['tipoBusca'], el['value']).size > 0 }
|
77
|
+
rescue Selenium::WebDriver::Error::TimeoutError => e
|
78
|
+
# Relança o erro com uma mensagem rica que a GEM entende
|
79
|
+
new_message = "Timeout de #{timeout}s esperando pelo elemento: using \"#{el['tipoBusca']}\" with value \"#{el['value']}\""
|
80
|
+
raise e.class, new_message
|
81
|
+
end
|
80
82
|
end
|
81
83
|
|
82
|
-
#
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
84
|
+
private # --- Helper Interno ---
|
85
|
+
|
86
|
+
# Este método é o coração da solução. Ele captura erros e os enriquece.
|
87
|
+
def find_element_with_enriched_error(el)
|
88
|
+
begin
|
89
|
+
return $driver.find_element(el['tipoBusca'], el['value'])
|
90
|
+
rescue Selenium::WebDriver::Error::NoSuchElementError => e
|
91
|
+
# Cria uma nova mensagem explícita no formato "using... with value..."
|
92
|
+
new_message = "using \"#{el['tipoBusca']}\" with value \"#{el['value']}\""
|
90
93
|
|
91
|
-
|
94
|
+
# Recria a exceção original com a nova mensagem.
|
95
|
+
new_exception = e.class.new(new_message)
|
96
|
+
new_exception.set_backtrace(e.backtrace) # Preserva o stack trace
|
97
|
+
raise new_exception
|
92
98
|
end
|
93
99
|
end
|
94
100
|
```
|
95
101
|
|
96
|
-
|
102
|
+
### Passo 3: Integrar com o Cucumber
|
97
103
|
|
98
|
-
|
104
|
+
Finalmente, no seu `hooks.rb`, acione a GEM no hook `After` em caso de falha.
|
99
105
|
|
100
|
-
|
106
|
+
```ruby
|
107
|
+
# features/support/hooks.rb
|
101
108
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
109
|
+
After do |scenario|
|
110
|
+
if scenario.failed? && $driver&.session_id
|
111
|
+
AppiumFailureHelper.handler_failure($driver, scenario.exception)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
```
|
106
115
|
|
107
|
-
|
108
|
-
Mostra exatamente qual `Tipo de Seletor` e `Valor Buscado` o Appium usou quando a falha ocorreu.
|
116
|
+
## 📄 O Relatório Gerado
|
109
117
|
|
110
|
-
|
111
|
-
Uma imagem exata da tela no momento do erro.
|
118
|
+
A cada falha, uma nova pasta é criada em `reports_failure/`, contendo o relatório `.html` e outros artefatos. O relatório pode ter dois formatos principais:
|
112
119
|
|
113
|
-
|
114
|
-
Lista os elementos na tela com localizadores parecidos com o que falhou, com uma pontuação de similaridade. Ideal para corrigir erros de digitação nos seletores.
|
120
|
+
1. **Relatório de Diagnóstico Simples:** Gerado para erros que não são de seletor (ex: falha de conexão, erro de código Ruby, falha de asserção). Ele mostra um diagnóstico direto, a mensagem de erro original e o `stack trace`.
|
115
121
|
|
116
|
-
|
117
|
-
|
122
|
+
2. **Relatório Detalhado (para problemas de seletor):**
|
123
|
+
* **Coluna da Esquerda:** Mostra o "Elemento com Falha" (extraído da exceção ou do código), "Sugestões Encontradas no Código" e o "Screenshot".
|
124
|
+
* **Coluna da Direita:** Contém abas interativas:
|
125
|
+
* **Análise Avançada:** Apresenta o "candidato mais provável" encontrado na tela e uma análise comparativa de seus atributos (`resource-id`, `text`, etc.), com uma sugestão acionável.
|
126
|
+
* **Dump Completo:** Uma lista de todos os elementos da tela e seus possíveis seletores.
|
118
127
|
|
119
128
|
## 🏛️ Arquitetura do Código
|
120
129
|
|
121
|
-
|
122
|
-
|
123
|
-
* `configuration.rb`: Classe que armazena as opções configuráveis e seus valores padrão.
|
124
|
-
* `handler.rb`: O **Maestro**. Orquestra as chamadas para os outros módulos.
|
125
|
-
* `analyzer.rb`: O **Analista**. Processa a mensagem de erro e calcula a similaridade.
|
126
|
-
* `element_repository.rb`: O **Repositório**. Encontra e carrega as definições de elementos de arquivos `.yaml` e `.rb` usando os caminhos configurados.
|
127
|
-
* `page_analyzer.rb`: O **Leitor de Tela**. Processa o XML da página para extrair elementos e sugerir nomes/localizadores.
|
128
|
-
* `report_generator.rb`: O **Gerador**. Consolida todos os dados e cria os arquivos de relatório.
|
129
|
-
* `utils.rb`: Funções auxiliares (Logger, etc.).
|
130
|
+
A GEM é dividida em módulos com responsabilidades únicas para facilitar a manutenção e a extensibilidade (Handler, Analyzer, ReportGenerator, XPathFactory, etc.).
|
130
131
|
|
131
132
|
## 🤝 Como Contribuir
|
132
133
|
|
133
|
-
|
134
|
+
Pull Requests são bem-vindos. Para bugs ou sugestões, por favor, abra uma *Issue* no repositório.
|
134
135
|
|
135
136
|
## 📜 Licença
|
136
137
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# lib/appium_failure_helper/handler.rb
|
1
2
|
module AppiumFailureHelper
|
2
3
|
class Handler
|
3
4
|
def self.call(driver, exception)
|
@@ -20,13 +21,21 @@ module AppiumFailureHelper
|
|
20
21
|
|
21
22
|
FileUtils.mkdir_p(@output_folder)
|
22
23
|
|
24
|
+
# --- DIAGNÓSTICO ADICIONADO AQUI ---
|
25
|
+
puts "\n--- DEBUG DE CAPABILITIES ---"
|
26
|
+
puts "Classe do Driver: #{@driver.class}"
|
27
|
+
puts "Conteúdo de @driver.capabilities:"
|
28
|
+
puts @driver.capabilities.inspect
|
29
|
+
puts "-----------------------------\n"
|
30
|
+
# ------------------------------------
|
31
|
+
|
23
32
|
triage_result = Analyzer.triage_error(@exception)
|
24
33
|
|
25
34
|
report_data = {
|
26
35
|
exception: @exception,
|
27
36
|
triage_result: triage_result,
|
28
37
|
timestamp: @timestamp,
|
29
|
-
platform: @driver.capabilities['platformName'] || @driver.capabilities[:
|
38
|
+
platform: @driver.capabilities['platformName'] || @driver.capabilities[:platform_name] || 'unknown',
|
30
39
|
screenshot_base64: @driver.screenshot_as(:base64)
|
31
40
|
}
|
32
41
|
|
@@ -59,14 +59,18 @@ module AppiumFailureHelper
|
|
59
59
|
screenshot_base64 = @data[:screenshot_base64]
|
60
60
|
|
61
61
|
locators_html = lambda do |locators|
|
62
|
-
(locators || []).map
|
62
|
+
(locators || []).map do |loc|
|
63
|
+
strategy_text = loc[:strategy].to_s.upcase.gsub('_', ' ')
|
64
|
+
"<li class='flex justify-between items-center bg-gray-50 p-2 rounded-md mb-1 text-xs font-mono'><span class='font-bold text-indigo-600'>#{CGI.escapeHTML(strategy_text)}:</span><span class='text-gray-700 ml-2 overflow-auto max-w-[70%]'>#{CGI.escapeHTML(loc[:locator])}</span></li>"
|
65
|
+
end.join
|
63
66
|
end
|
64
67
|
|
65
68
|
all_elements_html = lambda do |elements|
|
66
69
|
(elements || []).map { |el| "<details class='border-b border-gray-200 py-3'><summary class='font-semibold text-sm text-gray-800 cursor-pointer'>#{CGI.escapeHTML(el[:name])}</summary><ul class='text-xs space-y-1 mt-2'>#{locators_html.call(el[:locators])}</ul></details>" }.join
|
67
70
|
end
|
68
71
|
|
69
|
-
|
72
|
+
failed_info_content = "<p class='text-sm text-gray-700 font-medium mb-2'>Tipo de Seletor: <span class='font-mono text-xs bg-red-100 p-1 rounded'>#{CGI.escapeHTML(failed_info[:selector_type].to_s)}</span></p><p class='text-sm text-gray-700 font-medium'>Valor Buscado: <span class='font-mono text-xs bg-red-100 p-1 rounded break-all'>#{CGI.escapeHTML(failed_info[:selector_value].to_s)}</span></p>"
|
73
|
+
|
70
74
|
code_search_html = "" # (Sua lógica code_search_html)
|
71
75
|
failed_info_content = if failed_info && !failed_info.empty?
|
72
76
|
"<p class='text-sm text-gray-700 font-medium mb-2'>Tipo de Seletor: <span class='font-mono text-xs bg-red-100 p-1 rounded'>#{CGI.escapeHTML(failed_info[:selector_type].to_s)}</span></p><p class='text-sm text-gray-700 font-medium'>Valor Buscado: <span class='font-mono text-xs bg-red-100 p-1 rounded break-all'>#{CGI.escapeHTML(failed_info[:selector_value].to_s)}</span></p>"
|
@@ -78,20 +82,9 @@ module AppiumFailureHelper
|
|
78
82
|
unless code_search_results.empty?
|
79
83
|
suggestions_list = code_search_results.map do |match|
|
80
84
|
score_percent = (match[:score] * 100).round(1)
|
81
|
-
|
82
|
-
<div class='border border-sky-200 bg-sky-50 p-3 rounded-lg mb-2'>
|
83
|
-
<p class='text-sm text-gray-600'>Encontrado em: <strong class='font-mono'>#{match[:file]}:#{match[:line_number]}</strong></p>
|
84
|
-
<pre class='bg-gray-800 text-white p-2 rounded mt-2 text-xs overflow-auto'><code>#{CGI.escapeHTML(match[:code])}</code></pre>
|
85
|
-
<p class='text-xs text-green-600 mt-1'>Similaridade: #{score_percent}%</p>
|
86
|
-
</div>
|
87
|
-
SUGGESTION
|
85
|
+
"<div class='border border-sky-200 bg-sky-50 p-3 rounded-lg mb-2'><p class='text-sm text-gray-600'>Encontrado em: <strong class='font-mono'>#{match[:file]}:#{match[:line_number]}</strong></p><pre class='bg-gray-800 text-white p-2 rounded mt-2 text-xs overflow-auto'><code>#{CGI.escapeHTML(match[:code])}</code></pre><p class='text-xs text-green-600 mt-1'>Similaridade: #{score_percent}%</p></div>"
|
88
86
|
end.join
|
89
|
-
code_search_html =
|
90
|
-
<div class="bg-white p-4 rounded-lg shadow-md">
|
91
|
-
<h2 class="text-xl font-bold text-sky-700 mb-4">Sugestões Encontradas no Código</h2>
|
92
|
-
#{suggestions_list}
|
93
|
-
</div>
|
94
|
-
HTML
|
87
|
+
code_search_html = "<div class='bg-white p-4 rounded-lg shadow-md'><h2 class='text-xl font-bold text-sky-700 mb-4'>Sugestões Encontradas no Código</h2>#{suggestions_list}</div>"
|
95
88
|
end
|
96
89
|
|
97
90
|
# --- LÓGICA RESTAURADA: ELEMENTO COM FALHA ---
|
@@ -211,7 +204,7 @@ module AppiumFailureHelper
|
|
211
204
|
<div class="p-6">
|
212
205
|
<div id="strategies" class="tab-content active">
|
213
206
|
<h3 class="text-lg font-semibold text-indigo-700 mb-4">Estratégias de Localização Alternativas</h3>
|
214
|
-
#{
|
207
|
+
#{repair_suggestions_content}
|
215
208
|
</div>
|
216
209
|
<div id="all" class="tab-content">
|
217
210
|
<h3 class="text-lg font-semibold text-gray-700 mb-4">Dump de Todos os Elementos da Tela</h3>
|