appium_failure_helper 0.6.5 → 0.6.7
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 +88 -241
- data/lib/appium_failure_helper/configuration.rb +13 -0
- data/lib/appium_failure_helper/element_repository.rb +40 -32
- data/lib/appium_failure_helper/report_generator.rb +23 -13
- data/lib/appium_failure_helper/version.rb +1 -1
- data/lib/appium_failure_helper.rb +14 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d5d4f2b54142887b1306f844740130498cf86d84a98146539e3c881ade117ad
|
4
|
+
data.tar.gz: f42ba2c9bc3c33005613702381b5974475064d22e69a0978bd3f63b0ccaa4dde
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aadc9e9fa50c1d543eac38ae98da69d63353f9824916fe5c03cacc48baa3bfa359a987213f9eb53f719eeff2457cc2e8d5a785d106066548eeb4213c1d2e45da
|
7
|
+
data.tar.gz: 37f7783e098caae647c356a089221c7f903871c485523f8e804afa86966b830394f6f1d54b6ee22b142d11f3e938f54c4fbe635e0fec788c8e90ae7e822c5361
|
data/README.md
CHANGED
@@ -1,290 +1,137 @@
|
|
1
|
-
#
|
1
|
+
# Diagnóstico Inteligente de Falhas Appium
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+

|
4
|
+

|
5
|
+

|
6
6
|
|
7
|
-
|
7
|
+
Uma ferramenta robusta para diagnosticar falhas em testes automatizados com Appium, transformando erros de `NoSuchElementException` em relatórios interativos e inteligentes. Chega de perder tempo depurando seletores quebrados; deixe que a análise automatizada faça o trabalho pesado por você.
|
8
8
|
|
9
|
-
|
9
|
+
## ✨ Principais Funcionalidades
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
- [Cucumber (hook After)](#cucumber-hook-after)
|
20
|
-
- [RSpec (after :each)](#rspec-after-each)
|
21
|
-
- [Formato dos Artefatos Gerados](#formato-dos-artefatos-gerados)
|
22
|
-
- [Lógica de Geração de XPaths (detalhada)](#lógica-de-geração-de-xpaths-detalhada)
|
23
|
-
- [Tratamento de Dados e Deduplicação](#tratamento-de-dados-e-deduplicação)
|
24
|
-
- [Relatório HTML Interativo](#relatório-html-interativo)
|
25
|
-
- [Logging e Observabilidade](#logging-e-observabilidade)
|
26
|
-
- [Testes e Qualidade](#testes-e-qualidade)
|
27
|
-
- [Roadmap e Contribuição](#roadmap-e-contribuição)
|
28
|
-
- [Licença](#licença)
|
11
|
+
* **Relatório HTML Interativo:** Gera um relatório visual completo a cada falha, com screenshot, análise detalhada e dump de todos os elementos da tela.
|
12
|
+
* **Análise de Mapeamento ("De/Para"):** Verifica automaticamente se o elemento que falhou está definido em alguma fonte de dados do projeto, como:
|
13
|
+
* **Arquivos `.yaml`** gerados dinamicamente.
|
14
|
+
* **Arquivos de elementos Ruby (`.rb`)** customizáveis.
|
15
|
+
* **Sugestão por Similaridade:** Utiliza o algoritmo de Levenshtein para encontrar elementos na tela que são "parecidos" com o localizador que falhou, sugerindo correções.
|
16
|
+
* **Altamente Configurável:** Permite que os projetos definam seus próprios caminhos e nomes de arquivos de elementos, tornando a ferramenta totalmente reutilizável.
|
17
|
+
* **Arquitetura Modular:** O código é limpo, organizado e fácil de estender, seguindo o Princípio da Responsabilidade Única.
|
18
|
+
* **Suporte Multiplataforma:** A lógica de análise e sugestão funciona tanto para **Android** quanto para **iOS**.
|
29
19
|
|
30
|
-
|
20
|
+
## 🚀 Instalação
|
31
21
|
|
32
|
-
|
33
|
-
|
34
|
-
No momento em que um teste falha, o módulo realiza, de forma atômica e thread-safe:
|
35
|
-
1. captura de screenshot,
|
36
|
-
2. extração do `page_source` completo (XML),
|
37
|
-
3. varredura da árvore de elementos para gerar localizadores sugeridos,
|
38
|
-
4. escrita de dois YAMLs (focado e completo) e um relatório HTML que agrega tudo.
|
39
|
-
|
40
|
-
Todos os artefatos são salvos em uma pasta timestamped (formato `YYYY_MM_DD_HHMMSS`) dentro de `reports_failure/`.
|
41
|
-
|
42
|
-
---
|
43
|
-
|
44
|
-
## Funcionalidades
|
45
|
-
|
46
|
-
- Captura automática de screenshot PNG.
|
47
|
-
- Export completo de `page_source` em XML.
|
48
|
-
- Geração de `failure_analysis_*.yaml` (focado no elemento que falhou).
|
49
|
-
- Geração de `all_elements_dump_*.yaml` (todos os elementos com localizadores sugeridos).
|
50
|
-
- Relatório HTML interativo que combine screenshot, XML formatado e lista de localizadores.
|
51
|
-
- Geração de XPaths otimizados para **Android** e **iOS**.
|
52
|
-
- Truncamento de atributos longos (configurável).
|
53
|
-
- Eliminação de elementos duplicados e normalização de atributos.
|
54
|
-
- Logging via `Logger` do Ruby (Níveis: DEBUG/INFO/WARN/ERROR).
|
55
|
-
- Configuração via bloco `configure` (opcional).
|
56
|
-
|
57
|
-
---
|
58
|
-
|
59
|
-
## Arquitetura e Fluxo
|
60
|
-
|
61
|
-
1. **Hook de Testes** (Cucumber/RSpec) → invoca `Capture.handler_failure(driver, exception)`
|
62
|
-
2. **Capture.handler_failure**:
|
63
|
-
- estabelece pasta de saída com timestamp;
|
64
|
-
- chama `driver.screenshot` (salva PNG);
|
65
|
-
- chama `driver.page_source` (salva XML);
|
66
|
-
- percorre XML e cria árvore de elementos;
|
67
|
-
- para cada elemento gera candidate XPaths aplicando regras por plataforma;
|
68
|
-
- grava `failure_analysis_*.yaml` (prioriza elemento indicado) e `all_elements_dump_*.yaml`;
|
69
|
-
- monta `report_*.html` agregando tudo.
|
70
|
-
3. Logs detalhados emitidos durante a execução.
|
71
|
-
|
72
|
-
---
|
73
|
-
|
74
|
-
## Instalação
|
75
|
-
|
76
|
-
**Como gem (exemplo):**
|
77
|
-
|
78
|
-
Adicione ao `Gemfile` do projeto:
|
22
|
+
Adicione esta linha ao `Gemfile` do seu projeto de automação:
|
79
23
|
|
80
24
|
```ruby
|
81
|
-
gem 'appium_failure_helper'
|
25
|
+
gem 'appium_failure_helper' # (ou o nome que você der para a sua gem)
|
82
26
|
```
|
83
27
|
|
84
|
-
|
28
|
+
E então execute no seu terminal:
|
85
29
|
|
86
|
-
```
|
30
|
+
```sh
|
87
31
|
bundle install
|
88
32
|
```
|
89
33
|
|
90
|
-
|
34
|
+
## ⚙️ Configuração (Opcional)
|
91
35
|
|
92
|
-
|
36
|
+
Para tornar a ferramenta flexível e adaptável a diferentes projetos, você pode configurar os caminhos onde os elementos são buscados. Crie um bloco de configuração no seu arquivo de inicialização (ex: `features/support/env.rb`).
|
93
37
|
|
94
|
-
|
95
|
-
require_relative 'lib/appium_failure_helper'
|
96
|
-
```
|
97
|
-
|
98
|
-
---
|
99
|
-
|
100
|
-
## API Pública / Integração
|
101
|
-
|
102
|
-
### `AppiumFailureHelper::Capture`
|
38
|
+
**Se nenhuma configuração for fornecida, a ferramenta usará os valores padrão.**
|
103
39
|
|
104
40
|
```ruby
|
105
|
-
#
|
106
|
-
# - driver: objeto de sessão Appium (Selenium::WebDriver / Appium::Driver)
|
107
|
-
# - exception: exceção capturada no momento da falha
|
108
|
-
# - options: hash com overrides (ex: output_dir:)
|
109
|
-
AppiumFailureHelper::Capture.handler_failure(appium_driver, scenario.exception)
|
110
|
-
```
|
41
|
+
# Em features/support/env.rb
|
111
42
|
|
112
|
-
|
43
|
+
AppiumFailureHelper.configure do |config|
|
44
|
+
# Caminho para a pasta que contém os arquivos de elementos.
|
45
|
+
# Padrão: 'features/elements'
|
46
|
+
config.elements_path = 'caminho/para/sua/pasta/de/elementos'
|
113
47
|
|
114
|
-
|
115
|
-
|
116
|
-
|
48
|
+
# Nome do arquivo principal de elementos Ruby.
|
49
|
+
# Padrão: 'elementLists.rb'
|
50
|
+
config.elements_ruby_file = 'meu_arquivo_de_elementos.rb'
|
117
51
|
end
|
118
52
|
```
|
119
53
|
|
120
|
-
|
54
|
+
### Opções Disponíveis
|
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
|
121
62
|
|
122
|
-
|
63
|
+
A integração é feita através de um hook `After` no seu ambiente de testes.
|
123
64
|
|
124
|
-
|
65
|
+
**Exemplo completo para `features/support/env.rb`:**
|
125
66
|
|
126
67
|
```ruby
|
127
|
-
# features/support/
|
68
|
+
# features/support/env.rb
|
69
|
+
|
70
|
+
require 'appium_lib'
|
71
|
+
require 'cucumber'
|
72
|
+
|
73
|
+
# 1. Carrega a sua ferramenta
|
128
74
|
require 'appium_failure_helper'
|
129
75
|
|
76
|
+
# 2. (Opcional) Configura os caminhos se forem diferentes do padrão
|
77
|
+
AppiumFailureHelper.configure do |config|
|
78
|
+
config.elements_path = 'features/elements'
|
79
|
+
config.elements_ruby_file = 'elementLists.rb'
|
80
|
+
end
|
81
|
+
|
82
|
+
# 3. Hook que executa após cada cenário de teste
|
130
83
|
After do |scenario|
|
84
|
+
# Se o cenário falhou, aciona o seu helper
|
131
85
|
if scenario.failed?
|
132
|
-
|
86
|
+
puts "\n--- CENÁRIO FALHOU! ACIONANDO O DIAGNÓSTICO INTELIGENTE ---"
|
87
|
+
|
88
|
+
# A chamada ao helper utiliza automaticamente as configurações definidas acima.
|
89
|
+
AppiumFailureHelper.handler_failure(@driver, scenario.exception)
|
90
|
+
|
91
|
+
puts "--- HELPER FINALIZOU. VERIFIQUE A PASTA 'reports_failure' ---"
|
133
92
|
end
|
134
93
|
end
|
135
94
|
```
|
136
95
|
|
137
|
-
|
138
|
-
|
139
|
-
## Formato dos Artefatos Gerados
|
140
|
-
|
141
|
-
**Pasta:** `reports_failure/<TIMESTAMP>/`
|
142
|
-
|
143
|
-
Arquivos gerados (ex.: TIMESTAMP = `2025_09_23_173045`):
|
144
|
-
|
145
|
-
```
|
146
|
-
screenshot_2025_09_23_173045.png
|
147
|
-
page_source_2025_09_23_173045.xml
|
148
|
-
failure_analysis_2025_09_23_173045.yaml
|
149
|
-
all_elements_dump_2025_09_23_173045.yaml
|
150
|
-
report_2025_09_23_173045.html
|
151
|
-
```
|
152
|
-
|
153
|
-
### Exemplo (simplificado) de `failure_analysis_*.yaml`
|
154
|
-
|
155
|
-
```yaml
|
156
|
-
failed_element:
|
157
|
-
platform: android
|
158
|
-
summary:
|
159
|
-
class: android.widget.Button
|
160
|
-
resource_id: com.example:id/submit
|
161
|
-
text: "Enviar"
|
162
|
-
suggested_xpaths:
|
163
|
-
- "//android.widget.Button[@resource-id='com.example:id/submit']"
|
164
|
-
- "//android.widget.Button[contains(@text,'Enviar')]"
|
165
|
-
capture_metadata:
|
166
|
-
screenshot: screenshot_2025_09_23_173045.png
|
167
|
-
page_source: page_source_2025_09_23_173045.xml
|
168
|
-
timestamp: "2025-09-23T17:30:45Z"
|
169
|
-
tips: "Priorize resource-id; se ausente, use accessibility id (content-desc) e class+text como fallback."
|
170
|
-
```
|
171
|
-
|
172
|
-
### Exemplo (simplificado) de `all_elements_dump_*.yaml`
|
173
|
-
|
174
|
-
```yaml
|
175
|
-
elements:
|
176
|
-
- id_hash: "a1b2c3..."
|
177
|
-
class: "android.widget.EditText"
|
178
|
-
resource_id: "com.example:id/input_email"
|
179
|
-
text: "example@example.com"
|
180
|
-
truncated_attributes:
|
181
|
-
hint: "Digite seu e-mail..."
|
182
|
-
suggested_xpaths:
|
183
|
-
- "//*[@resource-id='com.example:id/input_email']"
|
184
|
-
- "//android.widget.EditText[contains(@hint,'Digite seu e-mail')]"
|
185
|
-
```
|
186
|
-
|
187
|
-
---
|
188
|
-
|
189
|
-
## Lógica de Geração de XPaths (detalhada)
|
190
|
-
|
191
|
-
**Princípios gerais**
|
192
|
-
1. Priorizar identificadores estáveis (resource-id no Android / accessibility id no iOS).
|
193
|
-
2. Evitar XPaths com `index` como primeira opção (usado apenas como último recurso).
|
194
|
-
3. Combinar atributos quando necessário para aumentar a especificidade e evitar colisões.
|
195
|
-
4. Normalizar espaços e truncar textos longos.
|
196
|
-
|
197
|
-
**Estratégias por plataforma (ordem de preferência)**
|
198
|
-
|
199
|
-
- **Android**
|
200
|
-
1. `resource-id` → `//*[@resource-id='com.pkg:id/id']`
|
201
|
-
2. `content-desc` / `contentDescription` (accessibility) → `//*[@content-desc='x']`
|
202
|
-
3. `class` + `text` → `//android.widget.TextView[@class='...' and contains(normalize-space(@text),'...')]`
|
203
|
-
4. `class` + raça de atributos (combinações: enabled, clickable, package)
|
204
|
-
5. fallback: `//android.widget.Button[position()=n]` (último recurso)
|
205
|
-
|
206
|
-
- **iOS**
|
207
|
-
1. `accessibility id` (nome accessibility) → `//*[@name='Submit']`
|
208
|
-
2. `label` / `value` → `//*[contains(@label,'...')]`
|
209
|
-
3. `type` + `label` → `//XCUIElementTypeButton[@label='OK']`
|
210
|
-
4. fallback: hierarquia / indices
|
211
|
-
|
212
|
-
**Exemplo de XPath combinado (alta especificidade):**
|
213
|
-
|
214
|
-
```xpath
|
215
|
-
//android.widget.Button[@resource-id='com.example:id/submit' and contains(normalize-space(@text),'Enviar') and @clickable='true']
|
216
|
-
```
|
217
|
-
|
218
|
-
---
|
219
|
-
|
220
|
-
## Tratamento de Dados e Deduplicação
|
221
|
-
|
222
|
-
- **Truncamento**: atributos com comprimento acima de `attr_truncate_length` são truncados com sufixo `...` para evitar poluição do YAML.
|
223
|
-
- **Hash único por elemento**: é gerado um hash (sha1) baseado em conjunto de atributos relevantes (class+resource-id+content-desc+text) para identificar duplicados.
|
224
|
-
- **Remoção de nulos**: atributos vazios ou nulos são omitidos nos YAMLs.
|
225
|
-
- **Ordenação**: elementos no `all_elements_dump` são ordenados por prioridade de localizador (resource-id primeiro).
|
226
|
-
|
227
|
-
---
|
228
|
-
|
229
|
-
## Relatório HTML Interativo
|
230
|
-
|
231
|
-
O HTML gerado possui:
|
232
|
-
- Visualização inline do `screenshot` (img tag),
|
233
|
-
- Painel colapsável com o `page_source` (XML formatado e collapsible),
|
234
|
-
- Lista navegável de elementos com seus `suggested_xpaths` (botões para copiar),
|
235
|
-
- Ancoragem que permite focalizar: ao clicar em um XPath, realça o fragmento correspondente no XML (se possível),
|
236
|
-
- Metadados e link rápido para os YAMLs.
|
237
|
-
|
238
|
-
**Observação:** o HTML é gerado de forma estática — para realces dinâmicos é usado JavaScript simples embutido (sem dependências externas).
|
239
|
-
|
240
|
-
---
|
241
|
-
|
242
|
-
## Logging e Observabilidade
|
243
|
-
|
244
|
-
- Usa `Logger` padrão do Ruby:
|
245
|
-
- `DEBUG` para detalhamento completo (padrão em modo dev).
|
246
|
-
- `INFO` para resumo das ações realizadas.
|
247
|
-
- `WARN/ERROR` para problemas durante captura/escrita.
|
248
|
-
- Exemplos de mensagens:
|
249
|
-
- `[INFO] Creating failure report folder: reports_failure/2025_09_23_173045`
|
250
|
-
- `[DEBUG] Captured 4123 elements from page_source`
|
251
|
-
- `[ERROR] Failed to write screenshot: Permission denied`
|
96
|
+
## 📄 Entendendo o Relatório Gerado
|
252
97
|
|
253
|
-
|
98
|
+
Após uma falha, uma nova pasta será criada na raiz do seu projeto: `reports_failure/failure_[timestamp]`. Dentro dela, o arquivo mais importante é o `report_[...].html`.
|
254
99
|
|
255
|
-
|
100
|
+
O relatório HTML é dividido em seções claras para um diagnóstico rápido:
|
256
101
|
|
257
|
-
|
258
|
-
|
259
|
-
|
102
|
+
#### Análise de Mapeamento (Bloco Verde/Amarelo)
|
103
|
+
Informa se o elemento que falhou foi encontrado nos seus arquivos de mapeamento (`.rb` ou `.yaml`).
|
104
|
+
* **Bloco Verde (Sucesso):** Confirma que o elemento foi encontrado. Isso sugere que a definição está correta, e o problema pode ser de timing ou visibilidade na tela.
|
105
|
+
* **Bloco Amarelo (Aviso):** Informa que o elemento **não foi encontrado**. Isso geralmente aponta para um erro de digitação no nome do elemento no seu código de teste.
|
260
106
|
|
261
|
-
|
107
|
+
#### Elemento com Falha (Bloco Vermelho)
|
108
|
+
Mostra exatamente qual `Tipo de Seletor` e `Valor Buscado` o Appium usou quando a falha ocorreu.
|
262
109
|
|
263
|
-
|
110
|
+
#### Screenshot da Falha
|
111
|
+
Uma imagem exata da tela no momento do erro.
|
264
112
|
|
265
|
-
|
266
|
-
|
267
|
-
- Export para outros formatos (JSON/CSV).
|
268
|
-
- Integração com ferramentas de observabilidade (Sentry, Datadog).
|
269
|
-
- Modo headless para gerar relatórios offline em pipelines.
|
113
|
+
#### Sugestões de Reparo (Análise de Similaridade)
|
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.
|
270
115
|
|
271
|
-
|
272
|
-
|
273
|
-
2. Crie branch com feature/bugfix.
|
274
|
-
3. Abra PR com descrição técnica das mudanças e testes.
|
275
|
-
4. Mantenha o estilo Ruby (RuboCop) e documentação atualizada.
|
116
|
+
#### Dump Completo da Página
|
117
|
+
Uma lista interativa de **todos os elementos** visíveis na tela, com todos os seus possíveis localizadores.
|
276
118
|
|
277
|
-
|
119
|
+
## 🏛️ Arquitetura do Código
|
278
120
|
|
279
|
-
|
121
|
+
O código é modular para facilitar a manutenção e a extensibilidade.
|
280
122
|
|
281
|
-
|
282
|
-
|
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.).
|
283
130
|
|
284
|
-
|
131
|
+
## 🤝 Como Contribuir
|
285
132
|
|
286
|
-
|
133
|
+
Encontrou um bug ou tem uma ideia para uma nova funcionalidade? Abra uma *Issue* no repositório do projeto. Pull Requests são sempre bem-vindos!
|
287
134
|
|
288
|
-
|
135
|
+
## 📜 Licença
|
289
136
|
|
290
|
-
|
137
|
+
Este projeto é distribuído sob a licença MIT.
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module AppiumFailureHelper
|
2
|
+
class Configuration
|
3
|
+
# Define as opções que o usuário poderá configurar
|
4
|
+
attr_accessor :elements_path, :elements_ruby_file
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
# Define os valores PADRÃO.
|
8
|
+
# Se o usuário não configurar nada, a GEM usará estes valores.
|
9
|
+
@elements_path = 'features/elements'
|
10
|
+
@elements_ruby_file = 'elementLists.rb'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -1,49 +1,57 @@
|
|
1
1
|
module AppiumFailureHelper
|
2
2
|
module ElementRepository
|
3
|
+
|
4
|
+
def self.load_all
|
5
|
+
elements_map = load_from_ruby_file
|
6
|
+
elements_map.merge!(load_all_from_yaml)
|
7
|
+
elements_map
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def self.load_from_ruby_file
|
13
|
+
map = {}
|
14
|
+
# ALTERADO: Lê os caminhos a partir da configuração central.
|
15
|
+
config = AppiumFailureHelper.configuration
|
16
|
+
file_path = File.join(Dir.pwd, config.elements_path, config.elements_ruby_file)
|
17
|
+
|
18
|
+
return map unless File.exist?(file_path)
|
19
|
+
|
20
|
+
# ... (o resto do método continua igual)
|
21
|
+
begin
|
22
|
+
require file_path
|
23
|
+
instance = OnboardingElementLists.new
|
24
|
+
unless instance.respond_to?(:elements)
|
25
|
+
Utils.logger.warn("AVISO: A classe OnboardingElementLists não expõe um `attr_reader :elements`.")
|
26
|
+
return map
|
27
|
+
end
|
28
|
+
instance.elements.each do |key, value|
|
29
|
+
map[key.to_s] = { 'tipoBusca' => value[0], 'valor' => value[1] }
|
30
|
+
end
|
31
|
+
rescue => e
|
32
|
+
Utils.logger.warn("AVISO: Erro ao processar o arquivo #{file_path}: #{e.message}")
|
33
|
+
end
|
34
|
+
|
35
|
+
map
|
36
|
+
end
|
37
|
+
|
3
38
|
def self.load_all_from_yaml
|
4
39
|
elements_map = {}
|
5
|
-
#
|
6
|
-
|
40
|
+
# ALTERADO: Lê o caminho base da configuração central.
|
41
|
+
config = AppiumFailureHelper.configuration
|
42
|
+
glob_path = File.join(Dir.pwd, config.elements_path, '**', '*.yaml')
|
7
43
|
|
8
44
|
Dir.glob(glob_path).each do |file|
|
9
|
-
#
|
45
|
+
# ... (o resto do método continua igual) ...
|
10
46
|
next if file.include?('reports_failure')
|
11
|
-
|
12
47
|
begin
|
13
48
|
data = YAML.load_file(file)
|
14
|
-
if data.is_a?(Hash)
|
15
|
-
data.each do |key, value|
|
16
|
-
if value.is_a?(Hash) && value['tipoBusca'] && value['value']
|
17
|
-
elements_map[key] = value
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
49
|
+
elements_map.merge!(data) if data.is_a?(Hash)
|
21
50
|
rescue => e
|
22
51
|
Utils.logger.warn("Aviso: Erro ao carregar o arquivo YAML #{file}: #{e.message}")
|
23
52
|
end
|
24
53
|
end
|
25
54
|
elements_map
|
26
55
|
end
|
27
|
-
|
28
|
-
# NOVO: Método para verificar a existência de um elemento em um arquivo .rb
|
29
|
-
def self.find_in_ruby_file(element_name, path = 'elements/elements.rb')
|
30
|
-
return { found: false, path: path, reason: "Arquivo não encontrado" } unless File.exist?(path)
|
31
|
-
|
32
|
-
begin
|
33
|
-
content = File.read(path)
|
34
|
-
# Regex flexível para encontrar definições como:
|
35
|
-
# def nome_do_elemento
|
36
|
-
# element :nome_do_elemento
|
37
|
-
# element('nome_do_elemento')
|
38
|
-
if content.match?(/def #{element_name}|element[ |\(]['|:]#{element_name}/)
|
39
|
-
return { found: true, path: path }
|
40
|
-
else
|
41
|
-
return { found: false, path: path, reason: "Definição não encontrada" }
|
42
|
-
end
|
43
|
-
rescue => e
|
44
|
-
Utils.logger.warn("Aviso: Erro ao ler o arquivo Ruby #{path}: #{e.message}")
|
45
|
-
return { found: false, path: path, reason: "Erro de leitura" }
|
46
|
-
end
|
47
|
-
end
|
48
56
|
end
|
49
57
|
end
|
@@ -29,20 +29,27 @@ module AppiumFailureHelper
|
|
29
29
|
File.open("#{@output_folder}/all_elements_dump_#{@data[:timestamp]}.yaml", 'w') { |f| f.write(YAML.dump(@data[:all_page_elements])) }
|
30
30
|
end
|
31
31
|
|
32
|
+
# --- MÉTODO CORRIGIDO COM A LÓGICA COMPLETA ---
|
32
33
|
def generate_html_report
|
33
|
-
#
|
34
|
-
locators_html = ->(locators) { locators.map { |loc| "..." }.join }
|
35
|
-
all_elements_html = ->(elements) { elements.map { |el| "..." }.join }
|
36
|
-
|
37
|
-
# Prepara o conteúdo dinâmico
|
34
|
+
# Prepara as variáveis a partir do hash de dados
|
38
35
|
failed_info = @data[:failed_element]
|
39
36
|
similar_elements = @data[:similar_elements]
|
37
|
+
all_suggestions = @data[:all_page_elements]
|
40
38
|
de_para_yaml = @data[:de_para_yaml_analysis]
|
41
39
|
de_para_rb = @data[:de_para_rb_analysis]
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
timestamp = @data[:timestamp]
|
41
|
+
platform = @data[:platform]
|
42
|
+
screenshot_base64 = @data[:screenshot_base64]
|
45
43
|
|
44
|
+
# CORREÇÃO: Funções (lambdas) para gerar HTML restauradas na íntegra
|
45
|
+
locators_html = lambda do |locators|
|
46
|
+
locators.map { |loc| "<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(loc[:strategy].upcase.gsub('_', ' '))}:</span><span class='text-gray-700 ml-2 overflow-auto max-w-[70%]'>#{CGI.escapeHTML(loc[:locator])}</span></li>" }.join
|
47
|
+
end
|
48
|
+
|
49
|
+
all_elements_html = lambda do |elements|
|
50
|
+
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
|
51
|
+
end
|
52
|
+
|
46
53
|
# Bloco de análise YAML
|
47
54
|
de_para_yaml_html = ""
|
48
55
|
if de_para_yaml
|
@@ -58,7 +65,7 @@ module AppiumFailureHelper
|
|
58
65
|
</div>
|
59
66
|
HTML
|
60
67
|
end
|
61
|
-
|
68
|
+
|
62
69
|
# Bloco de análise Ruby
|
63
70
|
de_para_rb_html = ""
|
64
71
|
if de_para_rb
|
@@ -80,6 +87,7 @@ module AppiumFailureHelper
|
|
80
87
|
end
|
81
88
|
end
|
82
89
|
|
90
|
+
# Geração do conteúdo das seções que estavam faltando
|
83
91
|
similar_elements_content = similar_elements.empty? ? "<p class='text-gray-500'>Nenhuma alternativa semelhante foi encontrada na tela atual.</p>" : similar_elements.map { |el|
|
84
92
|
score_percent = (el[:score] * 100).round(1)
|
85
93
|
"<div class='border border-indigo-100 p-3 rounded-lg bg-indigo-50'><p class='font-bold text-indigo-800 mb-2'>#{CGI.escapeHTML(el[:name])} <span class='text-xs font-normal text-green-600 bg-green-100 rounded-full px-2 py-1'>Similaridade: #{score_percent}%</span></p><ul>#{locators_html.call(el[:locators])}</ul></div>"
|
@@ -91,13 +99,14 @@ module AppiumFailureHelper
|
|
91
99
|
"<p class='text-sm text-gray-500'>O localizador exato não pôde ser extraído da mensagem de erro.</p>"
|
92
100
|
end
|
93
101
|
|
102
|
+
# Template HTML completo
|
94
103
|
html_content = <<~HTML_REPORT
|
95
104
|
<!DOCTYPE html>
|
96
105
|
<html lang="pt-BR">
|
97
106
|
<head>
|
98
107
|
<meta charset="UTF-8">
|
99
108
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
100
|
-
<title>Relatório de Falha Appium - #{
|
109
|
+
<title>Relatório de Falha Appium - #{timestamp}</title>
|
101
110
|
<script src="https://cdn.tailwindcss.com"></script>
|
102
111
|
<style> body { font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto; } .tab-content { display: none; } .tab-content.active { display: block; } .tab-button.active { background-color: #4f46e5; color: white; } </style>
|
103
112
|
</head>
|
@@ -105,18 +114,19 @@ module AppiumFailureHelper
|
|
105
114
|
<div class="max-w-7xl mx-auto">
|
106
115
|
<header class="mb-8 pb-4 border-b border-gray-300">
|
107
116
|
<h1 class="text-3xl font-bold text-gray-800">Diagnóstico de Falha Automatizada</h1>
|
108
|
-
<p class="text-sm text-gray-500">Relatório gerado em: #{
|
117
|
+
<p class="text-sm text-gray-500">Relatório gerado em: #{timestamp} | Plataforma: #{platform.upcase}</p>
|
109
118
|
</header>
|
110
119
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
111
120
|
<div class="lg:col-span-1">
|
112
121
|
#{de_para_yaml_html}
|
122
|
+
#{de_para_rb_html}
|
113
123
|
<div class="bg-white p-4 rounded-lg shadow-xl mb-6 border border-red-200">
|
114
124
|
<h2 class="text-xl font-bold text-red-600 mb-4">Elemento com Falha</h2>
|
115
125
|
#{failed_info_content}
|
116
126
|
</div>
|
117
127
|
<div class="bg-white p-4 rounded-lg shadow-xl">
|
118
128
|
<h2 class="text-xl font-bold text-gray-800 mb-4">Screenshot da Falha</h2>
|
119
|
-
<img src="data:image/png;base64,#{
|
129
|
+
<img src="data:image/png;base64,#{screenshot_base64}" alt="Screenshot da Falha" class="w-full rounded-md shadow-lg border border-gray-200">
|
120
130
|
</div>
|
121
131
|
</div>
|
122
132
|
<div class="lg:col-span-2">
|
@@ -154,7 +164,7 @@ module AppiumFailureHelper
|
|
154
164
|
</html>
|
155
165
|
HTML_REPORT
|
156
166
|
|
157
|
-
File.write("#{@output_folder}/report_#{
|
167
|
+
File.write("#{@output_folder}/report_#{timestamp}.html", html_content)
|
158
168
|
end
|
159
169
|
end
|
160
170
|
end
|
@@ -15,8 +15,22 @@ require_relative 'appium_failure_helper/element_repository'
|
|
15
15
|
require_relative 'appium_failure_helper/page_analyzer'
|
16
16
|
require_relative 'appium_failure_helper/report_generator'
|
17
17
|
require_relative 'appium_failure_helper/handler'
|
18
|
+
require_relative 'appium_failure_helper/configuration'
|
19
|
+
|
18
20
|
module AppiumFailureHelper
|
19
21
|
class Error < StandardError; end
|
22
|
+
class << self
|
23
|
+
attr_writer :configuration
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.configuration
|
27
|
+
@configuration ||= Configuration.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.configure
|
31
|
+
yield(configuration)
|
32
|
+
end
|
33
|
+
|
20
34
|
def self.handler_failure(driver, exception)
|
21
35
|
Handler.call(driver, exception)
|
22
36
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appium_failure_helper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Nascimento
|
@@ -95,6 +95,7 @@ files:
|
|
95
95
|
- Rakefile
|
96
96
|
- lib/appium_failure_helper.rb
|
97
97
|
- lib/appium_failure_helper/analyzer.rb
|
98
|
+
- lib/appium_failure_helper/configuration.rb
|
98
99
|
- lib/appium_failure_helper/element_repository.rb
|
99
100
|
- lib/appium_failure_helper/handler.rb
|
100
101
|
- lib/appium_failure_helper/page_analyzer.rb
|