appium_failure_helper 1.3.0 → 1.5.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/.rspec +1 -0
- data/README.md +155 -144
- data/appium_failure_helper-1.4.0.gem +0 -0
- data/lib/appium_failure_helper/analyzer.rb +3 -0
- data/lib/appium_failure_helper/element_repository.rb +13 -34
- data/lib/appium_failure_helper/handler.rb +130 -36
- data/lib/appium_failure_helper/version.rb +1 -1
- data/release_gem.rb +23 -12
- metadata +3 -3
- data/appium_failure_helper-1.2.3.gem +0 -0
- data/appium_failure_helper-1.2.4.gem +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 43096445c0c64d280ed41cc96d50ddd088c660bfac0441b8ff4e9ddc34e9107d
|
|
4
|
+
data.tar.gz: a2b4d0c6ebe03d445ae1e39d5dd35da188b01b4835b1a9a16055709b201f0864
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f27c3e50dbef777ef84b756738e32ca50779a970e01e8cbd086a13c5a65a15d6c22c3f27e51e357b78555d3ae8cf5619b9e9fa36131c5f6761d54707b3fe8d93
|
|
7
|
+
data.tar.gz: a1dc3d6684a2d5afc79c6a7c956075fdeb5a90afd6d5ac4b02ae8a1129e16c18fcdae7cfd2f66794cab9e86ec65880ff7c5cb0edfddbef0b8ab6326ff163e445
|
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/README.md
CHANGED
|
@@ -1,144 +1,155 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
|
|
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.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## ✨ Principais Funcionalidades
|
|
13
|
+
|
|
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.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🚀 Instalação
|
|
26
|
+
|
|
27
|
+
Adicione ao `Gemfile` do seu projeto de automação:
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
gem 'appium_failure_helper', git: 'URL_DO_SEU_REPOSITORIO_GIT'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Depois execute:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
bundle install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 🛠️ Uso e Configuração
|
|
42
|
+
|
|
43
|
+
### 1️⃣ Configuração Inicial (Opcional)
|
|
44
|
+
|
|
45
|
+
No arquivo de inicialização (`features/support/env.rb`), configure os caminhos de elementos se necessário:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
require 'appium_failure_helper'
|
|
49
|
+
|
|
50
|
+
AppiumFailureHelper.configure do |config|
|
|
51
|
+
config.elements_path = 'features/elements' # Pasta de elementos
|
|
52
|
+
config.elements_ruby_file = 'elementLists.rb' # Arquivo Ruby de elementos
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
### 2️⃣ Enriquecer Exceções (Altamente Recomendado)
|
|
59
|
+
|
|
60
|
+
Para extrair o máximo de informações de falhas, ajuste seus métodos de busca de elementos:
|
|
61
|
+
|
|
62
|
+
```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
|
|
70
|
+
|
|
71
|
+
def waitForElementExist(el, timeout = 10)
|
|
72
|
+
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
|
73
|
+
begin
|
|
74
|
+
wait.until { $driver.find_elements(el['tipoBusca'], el['value']).size > 0 }
|
|
75
|
+
rescue Selenium::WebDriver::Error::TimeoutError => e
|
|
76
|
+
raise e.class, "Timeout de #{timeout}s esperando pelo elemento: using \"\#{el['tipoBusca']}\" with value \"\#{el['value']}\""
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
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
|
|
88
|
+
end
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### 3️⃣ Integração com Cucumber
|
|
94
|
+
|
|
95
|
+
No `hooks.rb`, acione a GEM após cada cenário com falha:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
After do |scenario|
|
|
99
|
+
if scenario.failed? && $driver&.session_id
|
|
100
|
+
AppiumFailureHelper.handler_failure($driver, scenario.exception)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 📄 Relatório Gerado
|
|
108
|
+
|
|
109
|
+
A cada falha, a GEM cria uma pasta em `reports_failure/` com:
|
|
110
|
+
|
|
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.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 🏛️ Arquitetura
|
|
121
|
+
|
|
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.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 🔄 Fluxo Interno da GEM
|
|
131
|
+
|
|
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
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 🤝 Contribuindo
|
|
148
|
+
|
|
149
|
+
Pull requests e issues são bem-vindos! Abra uma *Issue* para bugs ou sugestões.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 📜 Licença
|
|
154
|
+
|
|
155
|
+
MIT License
|
|
Binary file
|
|
@@ -6,6 +6,9 @@ module AppiumFailureHelper
|
|
|
6
6
|
Selenium::WebDriver::Error::TimeoutError,
|
|
7
7
|
Selenium::WebDriver::Error::UnknownCommandError
|
|
8
8
|
:locator_issue
|
|
9
|
+
when Selenium::WebDriver::Error::TimeoutError,
|
|
10
|
+
Appium::Core::Wait::TimeoutError
|
|
11
|
+
:locator_issue
|
|
9
12
|
when Selenium::WebDriver::Error::ElementNotInteractableError
|
|
10
13
|
:visibility_issue
|
|
11
14
|
when Selenium::WebDriver::Error::StaleElementReferenceError
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# lib/appium_failure_helper/element_repository.rb
|
|
2
2
|
module AppiumFailureHelper
|
|
3
3
|
module ElementRepository
|
|
4
|
+
ELEMENTS = {}
|
|
4
5
|
def self.load_all
|
|
5
6
|
config = AppiumFailureHelper.configuration
|
|
6
7
|
base_path = config.elements_path
|
|
@@ -38,48 +39,26 @@ module AppiumFailureHelper
|
|
|
38
39
|
map
|
|
39
40
|
end
|
|
40
41
|
|
|
41
|
-
def self.load_all_from_yaml(
|
|
42
|
-
|
|
43
|
-
glob_path = File.join(base_path, '**', '*.yaml')
|
|
44
|
-
files_found = Dir.glob(glob_path)
|
|
45
|
-
files_found.each do |file|
|
|
46
|
-
next if file.include?('reports_failure')
|
|
42
|
+
def self.load_all_from_yaml(dir_path = 'features/elements')
|
|
43
|
+
Dir.glob("#{dir_path}/*.yaml").each do |file|
|
|
47
44
|
begin
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
data[k] = normalize_element(v)
|
|
52
|
-
end
|
|
53
|
-
elements_map.merge!(data)
|
|
45
|
+
yaml_data = YAML.load_file(file)
|
|
46
|
+
yaml_data.each do |key, value|
|
|
47
|
+
ELEMENTS[key.to_sym] = normalize_element(value)
|
|
54
48
|
end
|
|
55
49
|
rescue => e
|
|
56
50
|
Utils.logger.warn("Aviso: Erro ao carregar o arquivo YAML #{file}: #{e.message}")
|
|
57
51
|
end
|
|
58
52
|
end
|
|
59
|
-
|
|
53
|
+
Utils.logger.info("Número de elementos carregados: #{ELEMENTS.size}")
|
|
54
|
+
ELEMENTS
|
|
60
55
|
end
|
|
61
56
|
|
|
62
|
-
def self.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
k_s = k.to_s
|
|
68
|
-
v_n = normalize_yaml_hash_keys(v)
|
|
69
|
-
# Se v_n é um Hash com chaves :value ou 'valor' -> faça unificação para 'value'
|
|
70
|
-
if v_n.is_a?(Hash)
|
|
71
|
-
if v_n.key?('valor') && !v_n.key?('value')
|
|
72
|
-
v_n['value'] = v_n.delete('valor')
|
|
73
|
-
end
|
|
74
|
-
# também converte :tipoBusca para 'tipoBusca' (string)
|
|
75
|
-
end
|
|
76
|
-
result[k_s] = v_n
|
|
77
|
-
end
|
|
78
|
-
result
|
|
79
|
-
when Array
|
|
80
|
-
obj.map { |el| normalize_yaml_hash_keys(el) }
|
|
81
|
-
else
|
|
82
|
-
obj
|
|
57
|
+
def self.normalize_element(element_hash)
|
|
58
|
+
# Ajuste para garantir keys simbólicas e paths padrão
|
|
59
|
+
element_hash.transform_keys(&:to_sym).tap do |h|
|
|
60
|
+
h[:selector_type] ||= h[:type] || 'unknown'
|
|
61
|
+
h[:selector_value] ||= h[:value] || 'unknown'
|
|
83
62
|
end
|
|
84
63
|
end
|
|
85
64
|
end
|
|
@@ -12,72 +12,166 @@ module AppiumFailureHelper
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def call
|
|
15
|
+
report_data = {}
|
|
15
16
|
begin
|
|
16
17
|
unless @driver && @driver.session_id
|
|
17
18
|
Utils.logger.error("Helper não executado: driver nulo ou sessão encerrada.")
|
|
18
|
-
return
|
|
19
|
+
return {}
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
FileUtils.mkdir_p(@output_folder)
|
|
22
|
-
|
|
23
23
|
|
|
24
24
|
triage_result = Analyzer.triage_error(@exception)
|
|
25
|
-
|
|
25
|
+
screenshot_b64 = begin
|
|
26
|
+
@driver.screenshot_as(:base64)
|
|
27
|
+
rescue
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
26
30
|
report_data = {
|
|
27
31
|
exception: @exception,
|
|
28
32
|
triage_result: triage_result,
|
|
29
33
|
timestamp: @timestamp,
|
|
30
|
-
platform: @driver.capabilities['platformName']
|
|
31
|
-
screenshot_base64:
|
|
34
|
+
platform: (@driver.capabilities['platformName'] rescue @driver.capabilities[:platform_name]) || 'unknown',
|
|
35
|
+
screenshot_base64: screenshot_b64
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
if triage_result == :locator_issue
|
|
35
|
-
page_source = @driver.page_source
|
|
36
|
-
doc = Nokogiri::XML(page_source)
|
|
39
|
+
page_source = @driver.page_source rescue nil
|
|
40
|
+
doc = Nokogiri::XML(page_source) rescue nil
|
|
37
41
|
|
|
38
|
-
failed_info =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
report_data[:
|
|
45
|
-
|
|
46
|
-
page_analyzer = PageAnalyzer.new(page_source, report_data[:platform].to_s)
|
|
47
|
-
all_page_elements = page_analyzer.analyze || []
|
|
42
|
+
failed_info = fetch_failed_element
|
|
43
|
+
|
|
44
|
+
report_data[:page_source] = page_source
|
|
45
|
+
report_data[:failed_element] = failed_info
|
|
46
|
+
|
|
47
|
+
unless failed_info.nil? || failed_info.empty?
|
|
48
|
+
page_analyzer = PageAnalyzer.new(page_source, report_data[:platform].to_s) rescue nil
|
|
49
|
+
all_page_elements = page_analyzer ? (page_analyzer.analyze || []) : []
|
|
48
50
|
similar_elements = Analyzer.find_similar_elements(failed_info, all_page_elements) || []
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if !similar_elements.empty?
|
|
52
|
-
target_suggestion = similar_elements.first
|
|
53
|
-
if target_suggestion[:attributes] && (target_path = target_suggestion[:attributes][:path])
|
|
54
|
-
target_node = doc.at_xpath(target_path)
|
|
55
|
-
alternative_xpaths = XPathFactory.generate_for_node(target_node) if target_node
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
unified_element_map = ElementRepository.load_all
|
|
51
|
+
alternative_xpaths = generate_alternative_xpaths(similar_elements, doc)
|
|
52
|
+
unified_element_map = ElementRepository.load_all rescue {}
|
|
60
53
|
de_para_result = Analyzer.find_de_para_match(failed_info, unified_element_map)
|
|
61
54
|
code_search_results = CodeSearcher.find_similar_locators(failed_info) || []
|
|
62
55
|
|
|
63
|
-
report_data.merge!(
|
|
64
|
-
page_source: page_source,
|
|
65
|
-
failed_element: failed_info,
|
|
56
|
+
report_data.merge!(
|
|
66
57
|
similar_elements: similar_elements,
|
|
67
58
|
alternative_xpaths: alternative_xpaths,
|
|
68
59
|
de_para_analysis: de_para_result,
|
|
69
60
|
code_search_results: code_search_results,
|
|
70
61
|
all_page_elements: all_page_elements
|
|
71
|
-
|
|
62
|
+
)
|
|
72
63
|
end
|
|
64
|
+
|
|
65
|
+
ReportGenerator.new(@output_folder, report_data).generate_all
|
|
66
|
+
Utils.logger.info("Relatórios gerados com sucesso em: #{@output_folder}")
|
|
73
67
|
end
|
|
74
68
|
|
|
75
|
-
ReportGenerator.new(@output_folder, report_data).generate_all
|
|
76
|
-
Utils.logger.info("Relatórios gerados com sucesso em: #{@output_folder}")
|
|
77
|
-
|
|
78
69
|
rescue => e
|
|
79
70
|
Utils.logger.error("Erro fatal na GEM de diagnóstico: #{e.message}\n#{e.backtrace.join("\n")}")
|
|
71
|
+
report_data = { exception: @exception, triage_result: :error } if report_data.nil? || report_data.empty?
|
|
72
|
+
ensure
|
|
73
|
+
return report_data
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def fetch_failed_element
|
|
80
|
+
msg = @exception&.message.to_s
|
|
81
|
+
|
|
82
|
+
# 1) pattern: using "type" with value "value"
|
|
83
|
+
if (m = msg.match(/using\s+["']?([^"']+)["']?\s+with\s+value\s+["']([^"']+)["']/i))
|
|
84
|
+
return { selector_type: m[1], selector_value: m[2] }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# 2) JSON-like: {"method":"id","selector":"btn"}
|
|
88
|
+
if (m = msg.match(/"method"\s*:\s*"([^"]+)"[\s,}].*"selector"\s*:\s*"([^"]+)"/i))
|
|
89
|
+
return { selector_type: m[1], selector_value: m[2] }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# 3) generic quoted token "value" or 'value'
|
|
93
|
+
if (m = msg.match(/["']([^"']+)["']/))
|
|
94
|
+
maybe_value = m[1]
|
|
95
|
+
# try lookup in repo by that value
|
|
96
|
+
unified_map = ElementRepository.load_all rescue {}
|
|
97
|
+
found = find_in_element_repository_by_value(maybe_value, unified_map)
|
|
98
|
+
if found
|
|
99
|
+
return found
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# guess type from message heuristics
|
|
103
|
+
guessed_type = msg[/\b(xpath|id|accessibility id|css)\b/i] ? $&.downcase : nil
|
|
104
|
+
return { selector_type: guessed_type || 'unknown', selector_value: maybe_value }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# 4) try SourceCodeAnalyzer
|
|
108
|
+
begin
|
|
109
|
+
code_info = SourceCodeAnalyzer.extract_from_exception(@exception) rescue {}
|
|
110
|
+
unless code_info.nil? || code_info.empty?
|
|
111
|
+
return code_info
|
|
112
|
+
end
|
|
113
|
+
rescue => _; end
|
|
114
|
+
|
|
115
|
+
# 5) fallback: try to inspect unified map for likely candidates (keys or inner values)
|
|
116
|
+
unified_map = ElementRepository.load_all rescue {}
|
|
117
|
+
# try to match any key that looks like an identifier present in the message
|
|
118
|
+
unified_map.each do |k, v|
|
|
119
|
+
k_str = k.to_s.downcase
|
|
120
|
+
if msg.downcase.include?(k_str)
|
|
121
|
+
return normalize_repo_element(v)
|
|
122
|
+
end
|
|
123
|
+
# inspect value fields
|
|
124
|
+
vals = []
|
|
125
|
+
if v.is_a?(Hash)
|
|
126
|
+
vals << v['valor'] if v.key?('valor')
|
|
127
|
+
vals << v['value'] if v.key?('value')
|
|
128
|
+
vals << v[:valor] if v.key?(:valor)
|
|
129
|
+
vals << v[:value] if v.key?(:value)
|
|
130
|
+
end
|
|
131
|
+
vals.compact!
|
|
132
|
+
vals.each do |vv|
|
|
133
|
+
if vv.to_s.downcase == vv.to_s.downcase && msg.downcase.include?(vv.to_s.downcase)
|
|
134
|
+
return normalize_repo_element(v)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# final fallback
|
|
140
|
+
debug_log("fetch_failed_element: fallback unknown")
|
|
141
|
+
{ selector_type: 'unknown', selector_value: 'unknown' }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def find_in_element_repository_by_value(value, map = {})
|
|
145
|
+
return nil if value.nil? || value.to_s.strip.empty?
|
|
146
|
+
normalized_value = value.to_s.downcase.strip
|
|
147
|
+
map.each do |k, v|
|
|
148
|
+
entry = v.is_a?(Hash) ? v : (v.respond_to?(:to_h) ? v.to_h : nil)
|
|
149
|
+
next unless entry
|
|
150
|
+
entry_val = entry['valor'] || entry['value'] || entry[:valor] || entry[:value] || entry['locator'] || entry[:locator]
|
|
151
|
+
next unless entry_val
|
|
152
|
+
return normalize_repo_element(entry) if entry_val.to_s.downcase.strip == normalized_value
|
|
153
|
+
end
|
|
154
|
+
nil
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def normalize_repo_element(entry)
|
|
158
|
+
return nil unless entry.is_a?(Hash)
|
|
159
|
+
tipo = entry['tipoBusca'] || entry[:tipoBusca] || entry['type'] || entry[:type] || entry['search_type'] || entry[:search]
|
|
160
|
+
valor = entry['valor'] || entry[:value] || entry[:locator] || entry[:valor_final] || entry[:value_final]
|
|
161
|
+
return nil unless valor
|
|
162
|
+
{ selector_type: (tipo || 'unknown'), selector_value: valor.to_s }
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def generate_alternative_xpaths(similar_elements, doc)
|
|
166
|
+
alternative_xpaths = []
|
|
167
|
+
if !similar_elements.empty?
|
|
168
|
+
target_suggestion = similar_elements.first
|
|
169
|
+
if target_suggestion[:attributes] && (target_path = target_suggestion[:attributes][:path])
|
|
170
|
+
target_node = doc.at_xpath(target_path) rescue nil
|
|
171
|
+
alternative_xpaths = XPathFactory.generate_for_node(target_node) if target_node
|
|
172
|
+
end
|
|
80
173
|
end
|
|
174
|
+
alternative_xpaths
|
|
81
175
|
end
|
|
82
176
|
end
|
|
83
|
-
end
|
|
177
|
+
end
|
data/release_gem.rb
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
1
|
require 'fileutils'
|
|
3
|
-
|
|
2
|
+
require 'selenium-webdriver'
|
|
3
|
+
require_relative 'lib/appium_failure_helper'
|
|
4
4
|
# Caminho do arquivo de versão
|
|
5
5
|
VERSION_FILE = 'lib/appium_failure_helper/version.rb'
|
|
6
6
|
|
|
7
|
+
# Executa os testes e retorna true se todos passarem
|
|
8
|
+
def tests_pass?
|
|
9
|
+
puts "Rodando testes..."
|
|
10
|
+
system('bundle exec rspec')
|
|
11
|
+
$?.success?
|
|
12
|
+
end
|
|
13
|
+
|
|
7
14
|
# Lê a versão atual da gem
|
|
8
15
|
def current_version
|
|
9
16
|
content = File.read(VERSION_FILE)
|
|
@@ -53,20 +60,24 @@ def git_commit_and_tag(new_version)
|
|
|
53
60
|
`git add .`
|
|
54
61
|
`git commit -m "Bump version to #{new_version.join('.')}"`
|
|
55
62
|
`git tag v#{new_version.join('.')}`
|
|
63
|
+
`git push && git push --tags`
|
|
56
64
|
end
|
|
57
65
|
|
|
58
66
|
# Publicar a GEM
|
|
59
|
-
def push_gem
|
|
67
|
+
def push_gem(new_version)
|
|
60
68
|
`gem build appium_failure_helper.gemspec`
|
|
61
|
-
`gem push appium_failure_helper-#{
|
|
69
|
+
`gem push appium_failure_helper-#{new_version.join('.')}.gem`
|
|
62
70
|
end
|
|
63
71
|
|
|
64
72
|
# Fluxo principal
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
puts "GEM publicada com sucesso! Nova versão: #{new_version.join('.')}"
|
|
73
|
+
if tests_pass?
|
|
74
|
+
version = current_version
|
|
75
|
+
type = change_type
|
|
76
|
+
new_version = increment_version(version, type)
|
|
77
|
+
update_version_file(new_version)
|
|
78
|
+
git_commit_and_tag(new_version)
|
|
79
|
+
push_gem(new_version)
|
|
80
|
+
puts "GEM publicada com sucesso! Nova versão: #{new_version.join('.')}"
|
|
81
|
+
else
|
|
82
|
+
puts "Testes falharam! Commit e push cancelados."
|
|
83
|
+
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: 1.
|
|
4
|
+
version: 1.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Nascimento
|
|
@@ -90,11 +90,11 @@ executables: []
|
|
|
90
90
|
extensions: []
|
|
91
91
|
extra_rdoc_files: []
|
|
92
92
|
files:
|
|
93
|
+
- ".rspec"
|
|
93
94
|
- LICENSE.txt
|
|
94
95
|
- README.md
|
|
95
96
|
- Rakefile
|
|
96
|
-
- appium_failure_helper-1.
|
|
97
|
-
- appium_failure_helper-1.2.4.gem
|
|
97
|
+
- appium_failure_helper-1.4.0.gem
|
|
98
98
|
- elements/cadastro.yaml
|
|
99
99
|
- elements/login.yaml
|
|
100
100
|
- img/fluxo_appium_failure_helper.png
|
|
Binary file
|
|
Binary file
|