appium_failure_helper 1.10.1 → 1.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51a04db2050508826da2df00704d3c09dd27396b16cbdb6daa465a01069ddb1c
4
- data.tar.gz: b6c523d78d7f0694b17b8e7aec538a972ff7b9885de05a4ccee057a76613c806
3
+ metadata.gz: e17ebf8bf639db0cd95be1fa0eb2dec93e2275f5ac8c6f9129a0ad078cd1e35e
4
+ data.tar.gz: ffc7672e11de338194c168b715c6aee9c86543c56ac4f3161c1b3aa2bc84fcc3
5
5
  SHA512:
6
- metadata.gz: a42471ed673f57d2c60a3c6b74d333eaf8e319db233505c4c75972c5d8d199b74f9d8fd5cdd657db016ebc5c9f5e6c1e6ddf0cf305b13f79758637b5b5bc8662
7
- data.tar.gz: ca095c221019f692059ee9c5e014a0639e7a286e421ad049b6f5763a5830a7a89893bb746927cd44763b19f1a856289237efff1777780cc08228a82770c5adce
6
+ metadata.gz: 7eefc5f3ecb97a34bcc4270263da7870bdfd728174212bf9378652556ecc91b3474a9516174795eccb68cfda6ff03bfeb91d085532d439b9276f126880deb84e
7
+ data.tar.gz: 5fae4884e8ac05ecbbb3f43b7f30882bfda77350f963cb30901a7d538d71d8e3188d658601d249944a8045aff44ce3b532cd6eb3682a98088cac97ea3f11b0e8
data/README.md CHANGED
@@ -1,24 +1,25 @@
1
-
2
1
  # Appium Failure Helper: Diagnóstico Inteligente de Falhas
3
2
 
4
- ![Build Status](https://img.shields.io/badge/build-passing-brightgreen)
5
- ![Gem Version](https://badge.fury.io/rb/appium_failure_helper.svg)
6
- ![License](https://img.shields.io/badge/license-MIT-lightgrey)
3
+ ![Build Status](https://img.shields.io/badge/build-passing-brightgreen)
4
+ ![Gem Version](https://badge.fury.io/rb/appium_failure_helper.svg)
5
+ ![License](https://img.shields.io/badge/license-MIT-lightgrey)
7
6
 
8
- 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, a ferramenta gera um relatório HTML detalhado, identificando a causa provável e acelerando drasticamente o tempo de depuração.
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 e interativo, identificando a causa provável e acelerando drasticamente o tempo de depuração.
9
8
 
10
9
  ---
11
10
 
12
11
  ## Principais Funcionalidades
13
12
 
14
- - **Diagnóstico Inteligente de Falhas:** Identifica automaticamente o tipo de erro (`NoSuchElementError`, `TimeoutError`, falha de asserção ou erro de código Ruby) e gera relatórios personalizados para cada caso.
15
- - **Análise de Código-Fonte:** Para erros "silenciosos", inspeciona o `stack trace` e extrai o seletor diretamente do código, apontando arquivo e linha exatos.
16
- - **Comparação Avançada de Atributos:** Compara atributo por atributo (`resource-id`, `text`, etc.) para encontrar o candidato mais provável na tela, evitando análises superficiais.
17
- - **Relatórios Interativos:** HTML completo com:
18
- - Screenshot da falha
19
- - Diagnóstico claro e sugestões acionáveis
20
- - Abas com "Análise Avançada" e "Dump Completo" de todos os elementos da tela
21
- - **Configuração Flexível:** Personalize caminhos e arquivos de elementos para se adaptar a diferentes estruturas de projeto.
13
+ - **Triagem Inteligente de Erros:** Identifica automaticamente o *tipo* de falha (`NoSuchElementError`, `TimeoutError`, `NoMethodError`, etc.) e decide se deve gerar um relatório de análise profunda ou um diagnóstico simples.
14
+ - **Análise de Código-Fonte:** Para erros "silenciosos" (onde a mensagem não contém o seletor), inspeciona o `stack trace` para encontrar o arquivo e a linha exatos do erro, extraindo o seletor diretamente do código.
15
+ - **Análise Avançada (Atributos Ponderados):** O "coração" da GEM. Em vez de uma simples comparação de strings, ela "desmonta" o seletor que falhou e o compara, atributo por atributo, com todos os elementos na tela. Ela dá pesos diferentes para `resource-id`, `text`, etc., para encontrar o "candidato mais provável" na tela.
16
+ - **Fábrica de Estratégias de Reparo:** Após identificar o "candidato mais provável", a `XPathFactory` gera uma lista rica (até 20) de seletores alternativos e robustos para *aquele* elemento, exibidos em um carrossel paginado.
17
+ - **Busca Reversa no Código:** A ferramenta varre seus arquivos `.rb` para encontrar definições de seletores que são parecidas com o que falhou, exibindo o trecho de código e o arquivo.
18
+ - **Relatórios Ricos e Interativos:** Gera um relatório HTML completo com:
19
+ - Screenshot da falha.
20
+ - Diagnóstico claro e sugestões acionáveis.
21
+ - Abas com "Análise Avançada", "Estratégias de Reparo" e "Dump Completo" de todos os elementos da tela.
22
+ - **Configuração Flexível:** Permite a customização de caminhos de arquivos de elementos.
22
23
 
23
24
  ---
24
25
 
@@ -40,61 +41,80 @@ bundle install
40
41
 
41
42
  ## Uso e Configuração
42
43
 
43
- ### Configuração Inicial (Opcional)
44
+ A integração é feita em 3 etapas para garantir máxima eficiência.
45
+
46
+ ### 1. Configuração Inicial (Opcional)
44
47
 
45
- No arquivo de inicialização (`features/support/env.rb`), configure os caminhos de elementos se necessário:
48
+ No arquivo de inicialização (`features/support/env.rb`), carregue a GEM e, opcionalmente, configure os caminhos de elementos se eles forem diferentes do padrão.
46
49
 
47
50
  ```ruby
48
51
  require 'appium_failure_helper'
49
52
 
50
53
  AppiumFailureHelper.configure do |config|
51
- config.elements_path = 'features/elements' # Pasta de elementos
52
- config.elements_ruby_file = 'elementLists.rb' # Arquivo Ruby de elementos
54
+ # Caminho para a pasta que contém os arquivos de elementos.
55
+ # Padrão: 'features/elements'
56
+ config.elements_path = 'features/elements'
57
+
58
+ # Nome do arquivo Ruby principal que define os elementos.
59
+ # Padrão: 'elementLists.rb'
60
+ config.elements_ruby_file = 'elementLists.rb'
53
61
  end
54
62
  ```
55
63
 
56
- ---
64
+ ### 2. Enriquecer Exceções (Etapa Crucial)
57
65
 
58
- ### Enriquecer Exceções (Altamente Recomendado)
66
+ Para que a GEM consiga analisar erros "silenciosos" (como `TimeoutError` ou falhas dentro de helpers), é **essencial** que seu framework de automação "enriqueça" a exceção antes de ela ser lançada.
59
67
 
60
- Para extrair o máximo de informações de falhas, ajuste seus métodos de busca de elementos:
68
+ Ajuste seus métodos de busca de elementos (ex: em `features/support/appiumCustom.rb`) para que eles capturem a falha e a relancem com uma mensagem detalhada no formato `using "tipo" with value "valor"`.
61
69
 
62
70
  ```ruby
63
- def find(el)
64
- find_element_with_enriched_error(el)
65
- end
66
-
67
- def clickElement(el)
68
- find_element_with_enriched_error(el).click
69
- end
71
+ # features/support/appiumCustom.rb
70
72
 
71
- def waitForElementExist(el, timeout = 10)
73
+ # --- MÉTODO DE ESPERA ENRIQUECIDO ---
74
+ def waitForElementExist(el, timeout = 30)
72
75
  wait = Selenium::WebDriver::Wait.new(timeout: timeout)
73
76
  begin
74
77
  wait.until { $driver.find_elements(el['tipoBusca'], el['value']).size > 0 }
75
78
  rescue Selenium::WebDriver::Error::TimeoutError => e
76
- raise e.class, "Timeout de #{timeout}s esperando pelo elemento: using \"\#{el['tipoBusca']}\" with value \"\#{el['value']}\""
79
+ # CRUCIAL: Relança o erro com uma mensagem explícita que a GEM entende.
80
+ new_message = "Timeout de #{timeout}s esperando pelo elemento: using \"#{el['tipoBusca']}\" with value \"#{el['value']}\""
81
+ new_exception = e.class.new(new_message)
82
+ new_exception.set_backtrace(e.backtrace) # Preserva o stack trace
83
+ raise new_exception
77
84
  end
78
85
  end
79
86
 
87
+ # --- MÉTODO DE BUSCA ENRIQUECIDO ---
88
+ def find(el)
89
+ find_element_with_enriched_error(el)
90
+ end
91
+
92
+ def clickElement(el)
93
+ find_element_with_enriched_error(el).click
94
+ end
95
+
80
96
  private
81
97
 
98
+ # Helper central que enriquece erros de 'find_element'
82
99
  def find_element_with_enriched_error(el)
83
- $driver.find_element(el['tipoBusca'], el['value'])
84
- rescue Selenium::WebDriver::Error::NoSuchElementError => e
85
- new_exception = e.class.new("using \"\#{el['tipoBusca']}\" with value \"\#{el['value']}\"")
86
- new_exception.set_backtrace(e.backtrace)
87
- raise new_exception
100
+ begin
101
+ return $driver.find_element(el['tipoBusca'], el['value'])
102
+ rescue Selenium::WebDriver::Error::NoSuchElementError => e
103
+ new_message = "using \"#{el['tipoBusca']}\" with value \"#{el['value']}\""
104
+ new_exception = e.class.new(new_message)
105
+ new_exception.set_backtrace(e.backtrace)
106
+ raise new_exception
107
+ end
88
108
  end
89
109
  ```
90
110
 
91
- ---
92
-
93
- ### Integração com Cucumber
111
+ ### 3. Integração com Cucumber
94
112
 
95
113
  No `hooks.rb`, acione a GEM após cada cenário com falha:
96
114
 
97
115
  ```ruby
116
+ # features/support/hooks.rb
117
+
98
118
  After do |scenario|
99
119
  if scenario.failed? && $driver&.session_id
100
120
  AppiumFailureHelper.handler_failure($driver, scenario.exception)
@@ -104,43 +124,41 @@ end
104
124
 
105
125
  ---
106
126
 
107
- ## Relatório Gerado
127
+ ## 4. Integração com CI/CD (Jenkins)
108
128
 
109
- A cada falha, a GEM cria uma pasta em `reports_failure/` com:
129
+ Você pode configurar sua GEM para publicar os relatórios HTML diretamente no painel do Jenkins. Isso dá visibilidade imediata para toda a equipe sobre a causa de um build quebrado, sem a necessidade de acessar logs ou baixar arquivos.
110
130
 
111
- 1. **Relatório Simples:** Para falhas genéricas, mostrando erro, stack trace e diagnóstico direto.
112
- 2. **Relatório Detalhado:** Para problemas de seletor:
113
- - **Coluna Esquerda:** Elemento com falha, seletores sugeridos e screenshot.
114
- - **Coluna Direita:** Abas interativas:
115
- - **Análise Avançada:** Mostra o candidato mais provável, atributos comparados e sugestões acionáveis.
116
- - **Dump Completo:** Lista todos os elementos e possíveis seletores da tela.
131
+ ➡️ **[Guia Completo de Integração com Jenkins](ci/CI_INTEGRATION.md)**
117
132
 
118
- ---
133
+ ## O Relatório Gerado
119
134
 
120
- ## Arquitetura
135
+ A cada falha, a GEM cria uma pasta em `reports_failure/` com:
121
136
 
122
- - **Handler:** Captura falhas e aciona o fluxo de análise.
123
- - **SourceCodeAnalyzer:** Extrai seletores diretamente do código-fonte.
124
- - **PageAnalyzer:** Analisa o `page_source` e sugere nomes e locators alternativos.
125
- - **XPathFactory:** Gera estratégias de localização (diretas, combinatórias, parent-based, relativas, parciais, booleanas e posicionais).
126
- - **ReportGenerator:** Cria relatórios HTML, XML e YAML ricos e interativos.
137
+ 1. **Relatório Simples:** Para falhas não relacionadas a seletores (ex: erro de código Ruby, falha de conexão). Mostra um diagnóstico direto, o erro original, o stack trace e o screenshot.
138
+ 2. **Relatório Detalhado:** Gerado quando um problema de seletor é identificado.
139
+ * **Coluna Esquerda:**
140
+ * `Elemento com Falha`: O seletor exato que falhou (extraído da mensagem ou do código).
141
+ * `Sugestões Encontradas no Código`: (Opcional) Sugestões de seletores parecidos encontrados no seu código-fonte.
142
+ * `Screenshot da Falha`: A imagem da tela no momento do erro.
143
+ * **Coluna Direita (Abas):**
144
+ * `Análise Avançada`: O "candidato mais provável" encontrado na tela, com uma análise comparativa de seus atributos (`resource-id`, `text`, etc.) e uma sugestão acionável.
145
+ * `Estratégias de Reparo`: Um carrossel paginado com até 20 estratégias de localização (XPaths, IDs) geradas pela `XPathFactory` para o candidato encontrado.
146
+ * `Dump Completo`: A lista de todos os elementos visíveis na tela.
127
147
 
128
148
  ---
129
149
 
130
- ## Fluxo Interno da GEM
150
+ ## Arquitetura
131
151
 
132
- ```
133
- Falha Appium
134
-
135
- ├─► SourceCodeAnalyzer {selector_type, selector_value}
136
-
137
- └─► PageAnalyzer [{name, locators, attributes}, ...]
138
-
139
- └─► XPathFactory [estratégias alternativas]
140
-
141
-
142
- ReportGenerator → HTML / XML / YAML
143
- ```
152
+ * **Handler:** O maestro que orquestra todo o fluxo de análise.
153
+ * **Analyzer:** O analista. Faz a triagem do erro e executa a "Análise Avançada" por atributos ponderados.
154
+ * **SourceCodeAnalyzer:** Especialista em ler o `stack trace` para extrair seletores de dentro do código-fonte.
155
+ * **CodeSearcher:** O detetive. Faz a busca reversa por strings de seletores similares em todo o projeto.
156
+ * **ElementRepository:** O repositório que carrega os mapas de elementos de arquivos `.rb` e `.yaml` (De/Para).
157
+ * **PageAnalyzer:** O leitor de tela. Processa o XML da página para extrair todos os elementos e seus atributos.
158
+ * **XPathFactory:** A fábrica que gera dezenas de estratégias de XPath (diretas, combinatórias, relacionais, etc.).
159
+ * **ReportGenerator:** O construtor. Renderiza os relatórios HTML (detalhado ou simples) com base nos dados da análise.
160
+ * **Configuration:** Gerencia as configurações da GEM.
161
+ * **Utils:** Funções auxiliares (Logger, etc.).
144
162
 
145
163
  ---
146
164
 
@@ -152,4 +170,4 @@ Pull requests e issues são bem-vindos! Abra uma *Issue* para bugs ou sugestões
152
170
 
153
171
  ## 📜 Licença
154
172
 
155
- MIT License
173
+ MIT License
@@ -0,0 +1,108 @@
1
+ # Guia de Integração com Jenkins (CI/CD)
2
+
3
+ Este guia mostra como configurar o job no Jenkins para publicar automaticamente os relatórios HTML gerados pela `AppiumFailureHelper` a cada build com falha.
4
+
5
+ ## Visão Geral
6
+
7
+ A integração é simples e não requer nenhuma configuração complexa na GEM. A própria GEM já está pré-configurada para gerar um relatório estável para o Jenkins.
8
+
9
+ Quando um teste falha, a GEM automaticamente:
10
+ 1. Gera o relatório detalhado em uma pasta com timestamp (ex: `reports_failure/failure_...`).
11
+ 2. Copia o arquivo `report.html` principal para uma pasta fixa na raiz do projeto: `ci_failure_report/index.html`.
12
+
13
+ O seu trabalho no Jenkins é apenas instalar um plugin e apontá-lo para essa pasta.
14
+
15
+ ---
16
+
17
+ ### Passo 1: Instalar o Plugin no Jenkins
18
+
19
+ 1. No seu painel do Jenkins, vá para `Gerenciar Jenkins` > `Plugins`.
20
+ 2. Na aba `Disponíveis`, procure por `HTML Publisher`.
21
+ 3. Instale o plugin e reinicie o Jenkins, se solicitado.
22
+
23
+ ---
24
+
25
+ ### Passo 2: Adicionar a Pasta ao `.gitignore`
26
+
27
+ Para evitar que os relatórios de CI sejam comitados no seu repositório Git, adicione a pasta de relatórios ao seu arquivo `.gitignore` na raiz do projeto:
28
+
29
+ ```
30
+ # .gitignore
31
+
32
+ # Relatórios de falha locais
33
+ reports_failure/
34
+
35
+ # Relatório estável para o Jenkins
36
+ ci_failure_report/
37
+ ```
38
+
39
+ ---
40
+
41
+ ### Passo 3: Configurar seu Job no Jenkins
42
+
43
+ Abaixo estão as duas formas mais comuns de configurar o job.
44
+
45
+ #### Opção A: Projeto Freestyle (Configuração pela UI)
46
+
47
+ 1. Abra a **Configuração** do seu Job.
48
+ 2. Vá até a seção **"Ações de pós-build"** (Post-build Actions).
49
+ 3. Clique em **"Adicionar ação de pós-build"** e selecione **"Publish HTML reports"**.
50
+ 4. Preencha os campos da seguinte forma:
51
+ * **HTML directory to archive:** `ci_failure_report`
52
+ * **Index page[s]:** `index.html`
53
+ * **Report title:** `Diagnóstico de Falha`
54
+ 5. Salve.
55
+
56
+ #### Opção B: Projeto Pipeline (Configuração via `Jenkinsfile`)
57
+
58
+ Se você usa um `Jenkinsfile`, adicione o passo `publishHTML` no seu bloco `post { failure { ... } }`. Isso garante que o relatório só seja publicado se o build falhar.
59
+
60
+ ```groovy
61
+ // Jenkinsfile
62
+
63
+ pipeline {
64
+ agent any
65
+ stages {
66
+ stage('Test') {
67
+ steps {
68
+ // Roda seus testes. O 'catchError' garante que o pipeline continue
69
+ // para que as ações de pós-build possam ser executadas.
70
+ catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
71
+ sh 'bundle exec cucumber'
72
+ }
73
+ }
74
+ }
75
+ }
76
+ post {
77
+ // Executa APENAS SE O BUILD FALHAR
78
+ failure {
79
+ echo "Build falhou. Publicando relatório de diagnóstico da GEM..."
80
+
81
+ publishHTML(
82
+ target: [
83
+ allowMissing: true, // Não falha o build se a pasta não existir
84
+ directory: 'ci_failure_report', // A pasta que a GEM cria
85
+ files: 'index.html', // O arquivo HTML padrão
86
+ keepAll: true, // Mantém o histórico de relatórios
87
+ reportDir: 'DiagnosticoFalha', // O nome na URL do Jenkins
88
+ reportName: 'Diagnóstico de Falha' // O nome do link no menu
89
+ ]
90
+ )
91
+ }
92
+
93
+ always {
94
+ // (Opcional, mas recomendado) Arquiva os relatórios do Allure
95
+ // allure(results: [[path: 'logs/allure-results']])
96
+
97
+ // Limpa o workspace para a próxima execução
98
+ cleanWs()
99
+ }
100
+ }
101
+ }
102
+ ```
103
+
104
+ ---
105
+
106
+ ### Resultado
107
+
108
+ Após a próxima execução falha no Jenkins, um novo link chamado **"Diagnóstico de Falha"** aparecerá no menu esquerdo do build. Ao clicar, o relatório HTML interativo da sua GEM será exibido diretamente na interface do Jenkins.
data/ci/Jenkinsfile ADDED
@@ -0,0 +1,57 @@
1
+ pipeline {
2
+ agent any
3
+
4
+ environment {
5
+ // PATH = "/usr/local/bin:$PATH"
6
+ }
7
+
8
+ stages {
9
+ stage('Checkout') {
10
+ steps {
11
+ git url: 'https://github.com/seu-usuario/seu-projeto-de-automacao.git', branch: 'main'
12
+ }
13
+ }
14
+
15
+ stage('Setup') {
16
+ steps {
17
+ sh 'bundle install'
18
+ }
19
+ }
20
+
21
+ stage('Test') {
22
+ steps {
23
+ catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
24
+ ansiColor('xterm') {
25
+ sh 'bundle exec cucumber --tags "@regressao" -f AllureCucumber::Formatter -o logs'
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
31
+
32
+ post {
33
+ always {
34
+ allure(
35
+ includeProperties: false,
36
+ results: [[path: 'logs']]
37
+ )
38
+
39
+ archiveArtifacts artifacts: 'reports_failure/**/*', allowEmptyArchive: true
40
+ }
41
+
42
+ failure {
43
+ echo "Build falhou. Publicando relatório de diagnóstico da GEM..."
44
+
45
+ publishHTML(
46
+ target: [
47
+ allowMissing: true,
48
+ directory: 'ci_failure_report',
49
+ files: 'index.html',
50
+ keepAll: true,
51
+ reportDir: 'DiagnosticoDeFalha',
52
+ reportName: 'Diagnóstico de Falha'
53
+ ]
54
+ )
55
+ }
56
+ }
57
+ }
@@ -34,31 +34,13 @@ module AppiumFailureHelper
34
34
 
35
35
  if triage_result == :locator_issue
36
36
  page_source = safe_page_source
37
- # tenta extrair detalhes do Analyzer (se existir), senão usa fetch_failed_element
38
- failed_info = {}
39
- begin
40
- failed_info = Analyzer.extract_failure_details(@exception) if Analyzer.respond_to?(:extract_failure_details)
41
- rescue
42
- failed_info = {}
43
- end
44
-
45
- if failed_info.nil? || failed_info.empty?
46
- # tenta extrair do próprio handler (regex mais robusta)
47
- failed_info = fetch_failed_element || {}
48
- end
37
+ failed_info = fetch_failed_element || {}
49
38
 
50
- # fallback para extrair do código-fonte (se existir)
51
- if (failed_info.nil? || failed_info.empty?) && SourceCodeAnalyzer.respond_to?(:extract_from_exception)
52
- begin
53
- failed_info = SourceCodeAnalyzer.extract_from_exception(@exception) || {}
54
- rescue
55
- failed_info = {}
56
- end
39
+ if failed_info.empty? && SourceCodeAnalyzer.respond_to?(:extract_from_exception)
40
+ failed_info = SourceCodeAnalyzer.extract_from_exception(@exception) || {}
57
41
  end
58
42
 
59
- # garante que exista ao menos um objeto failed_element
60
- if failed_info.nil? || failed_info.empty?
61
- failed_info = { selector_type: 'unknown', selector_value: @exception&.message.to_s }
43
+ if failed_info.empty?
62
44
  report_data[:triage_result] = :unidentified_locator_issue
63
45
  end
64
46
 
@@ -66,51 +48,31 @@ module AppiumFailureHelper
66
48
  best_candidate_analysis = nil
67
49
  alternative_xpaths = []
68
50
 
69
- if page_source
70
- begin
71
- doc = Nokogiri::XML(page_source)
72
- page_analyzer = PageAnalyzer.new(page_source, platform)
73
- all_page_elements = page_analyzer.analyze || []
74
- best_candidate_analysis = Analyzer.perform_advanced_analysis(failed_info, all_page_elements, platform) rescue nil
75
- rescue => e
76
- Utils.logger.warn("Erro analisando page_source: #{e.message}")
77
- end
78
- end
79
-
80
- # se não encontrou candidato, gera alternativas a partir do locator bruto
81
- if best_candidate_analysis.nil?
82
- # tenta parse por Analyzer (se exposto), senão regex fallback
83
- failed_attrs = {}
84
- begin
85
- if Analyzer.respond_to?(:parse_locator) || Analyzer.private_methods.include?(:parse_locator)
86
- failed_attrs = Analyzer.send(:parse_locator, failed_info[:selector_type], failed_info[:selector_value], platform) rescue {}
87
- end
88
- rescue
89
- failed_attrs = {}
90
- end
91
-
92
- if failed_attrs.nil? || failed_attrs.empty?
51
+ if page_source && !failed_info.empty?
52
+ doc = Nokogiri::XML(page_source)
53
+ page_analyzer = PageAnalyzer.new(page_source, platform)
54
+ all_page_elements = page_analyzer.analyze || []
55
+ best_candidate_analysis = Analyzer.perform_advanced_analysis(failed_info, all_page_elements, platform) rescue nil
56
+
57
+ # --- SUA REGRA DE NEGÓCIO INTEGRADA AQUI ---
58
+ target_node = nil
59
+ if best_candidate_analysis && best_candidate_analysis[:attributes] && (path = best_candidate_analysis[:attributes][:path])
60
+ # Se encontrou um candidato, ele é o alvo para a XPathFactory
61
+ target_node = doc.at_xpath(path)
62
+ else
63
+ # Se NÃO encontrou, o alvo é o próprio elemento que falhou
93
64
  failed_attrs = parse_attrs_from_locator_string(failed_info[:selector_value] || '')
94
- end
95
-
96
- if failed_attrs && !failed_attrs.empty?
97
- temp_doc = Nokogiri::XML::Document.new
98
- tag = (failed_attrs.delete('tag') || failed_attrs.delete(:tag) || 'element').to_s
99
- target_node = Nokogiri::XML::Node.new(tag, temp_doc)
100
- failed_attrs.each { |k, v| target_node[k.to_s] = v.to_s unless k.to_s == 'tag' }
101
- alternative_xpaths = XPathFactory.generate_for_node(target_node) || []
102
- end
103
- else
104
- # se encontrou candidato, tenta gerar alternativas a partir do node encontrado
105
- if best_candidate_analysis[:attributes] && (path = best_candidate_analysis[:attributes][:path])
106
- begin
107
- doc = Nokogiri::XML(page_source) unless defined?(doc) && doc
108
- target_node = doc.at_xpath(path) rescue nil
109
- alternative_xpaths = XPathFactory.generate_for_node(target_node) if target_node
110
- rescue
111
- # ignore, já temos best_candidate_analysis
65
+ if !failed_attrs.empty?
66
+ temp_doc = Nokogiri::XML::Document.new
67
+ tag = (failed_attrs.delete('tag') || 'element').to_s
68
+ target_node = Nokogiri::XML::Node.new(tag, temp_doc)
69
+ failed_attrs.each { |k, v| target_node[k.to_s] = v.to_s }
112
70
  end
113
71
  end
72
+
73
+ # Gera as estratégias para o nó alvo, seja ele real ou "fantasma"
74
+ alternative_xpaths = XPathFactory.generate_for_node(target_node) if target_node
75
+ # -----------------------------------------------
114
76
  end
115
77
 
116
78
  report_data.merge!({
@@ -122,7 +84,9 @@ module AppiumFailureHelper
122
84
  })
123
85
  end
124
86
 
125
- ReportGenerator.new(@output_folder, report_data).generate_all
87
+ report_generator = ReportGenerator.new(@output_folder, report_data)
88
+ generated_html_path = report_generator.generate_all
89
+ copy_report_for_ci(generated_html_path)
126
90
  Utils.logger.info("Relatórios gerados com sucesso em: #{@output_folder}")
127
91
 
128
92
  rescue => e
@@ -200,5 +164,19 @@ module AppiumFailureHelper
200
164
 
201
165
  attrs
202
166
  end
167
+
168
+ def copy_report_for_ci(source_html_path)
169
+ return unless source_html_path && File.exist?(source_html_path)
170
+
171
+ ci_report_dir = File.join(Dir.pwd, 'ci_failure_report')
172
+ FileUtils.mkdir_p(ci_report_dir)
173
+
174
+ destination_path = File.join(ci_report_dir, 'index.html')
175
+
176
+ FileUtils.cp(source_html_path, destination_path)
177
+ Utils.logger.info("Relatório copiado para CI em: #{destination_path}")
178
+ rescue => e
179
+ Utils.logger.warn("AVISO: Falha ao copiar relatório para CI. Erro: #{e.message}")
180
+ end
203
181
  end
204
182
  end
@@ -43,7 +43,10 @@ module AppiumFailureHelper
43
43
  message: "A análise profunda do seletor não foi executada ou falhou. Verifique a mensagem de erro original e o stack trace."
44
44
  )
45
45
  end
46
- File.write("#{@output_folder}/report_#{@data[:timestamp]}.html", html_content)
46
+ html_file_path = File.join(@output_folder, "report_#{@data[:timestamp]}.html")
47
+ File.write(html_file_path, html_content)
48
+
49
+ return html_file_path
47
50
  end
48
51
 
49
52
  def build_full_report
@@ -1,3 +1,3 @@
1
1
  module AppiumFailureHelper
2
- VERSION = "1.10.1"
2
+ VERSION = "1.11.0"
3
3
  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: 1.10.1
4
+ version: 1.11.0
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-10-07 00:00:00.000000000 Z
11
+ date: 2025-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -99,6 +99,8 @@ files:
99
99
  - LICENSE.txt
100
100
  - README.md
101
101
  - Rakefile
102
+ - ci/CI_INTEGRATION.md
103
+ - ci/Jenkinsfile
102
104
  - img/fluxo_appium_failure_helper.png
103
105
  - lib/appium_failure_helper.rb
104
106
  - lib/appium_failure_helper/analyzer.rb