bddgenx 2.4.6 → 2.4.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/VERSION +1 -1
- data/lib/bddgenx/generators/generator.rb +18 -47
- data/lib/bddgenx/generators/runner.rb +5 -3
- data/lib/bddgenx/generators/steps_generator.rb +6 -18
- data/lib/bddgenx/ia/chatgtp_cliente.rb +3 -21
- data/lib/bddgenx/ia/gemini_cliente.rb +3 -21
- data/lib/bddgenx/{support/gherkin_cleaner.rb → utils/gherkin_cleaner_helper.rb} +1 -1
- data/lib/bddgenx/utils/language_helper.rb +45 -0
- data/lib/bddgenx/utils/remover_steps_duplicados_helper.rb +79 -0
- data/lib/env.rb +4 -2
- metadata +4 -3
- data/lib/bddgenx/support/remover_steps_duplicados.rb +0 -81
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94b275818990d76f36659c7be2a611232c1dfa90d812ac0e70dbfa6ae9c6e3e9
|
4
|
+
data.tar.gz: 5219e00c5048c07aee0ad93617a8a140e5725610ea532fe06fea798c097fe5b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59b7b3e17525393b9cf63e6ef4b9a71ad3e2a49c69c35967180883d920ab0160400ba3ce42fe8dd5a1571c9246264dccc388faa5bcbae7808c6385dc79133ad5
|
7
|
+
data.tar.gz: 8dc211ad3e323a958fdb2e0b273fb5054d5f432d7be98677fbe1bf41eea2923284d763e0fe73181cc1740dfe2c5c9cfc88fa76d4fe299569f5af9e4decf0d5cc
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.4.
|
1
|
+
2.4.7
|
@@ -8,36 +8,6 @@
|
|
8
8
|
|
9
9
|
module Bddgenx
|
10
10
|
class Generator
|
11
|
-
# Palavras-chave do Gherkin em Português
|
12
|
-
GHERKIN_KEYS_PT = %w[Dado Quando Então E Mas].freeze
|
13
|
-
|
14
|
-
# Palavras-chave do Gherkin em Inglês
|
15
|
-
GHERKIN_KEYS_EN = %w[Given When Then And But].freeze
|
16
|
-
|
17
|
-
# Mapeamento PT → EN
|
18
|
-
GHERKIN_MAP_PT_EN = GHERKIN_KEYS_PT.zip(GHERKIN_KEYS_EN).to_h
|
19
|
-
|
20
|
-
# Mapeamento EN → PT
|
21
|
-
GHERKIN_MAP_EN_PT = GHERKIN_KEYS_EN.zip(GHERKIN_KEYS_PT).to_h
|
22
|
-
|
23
|
-
# Todas as palavras-chave reconhecidas pelos parsers
|
24
|
-
ALL_KEYS = GHERKIN_KEYS_PT + GHERKIN_KEYS_EN
|
25
|
-
|
26
|
-
##
|
27
|
-
# Função para extrair o idioma do arquivo .txt
|
28
|
-
# @param txt_file [String] Caminho do arquivo .txt
|
29
|
-
# @return [String] O idioma extraído ou 'pt' como padrão
|
30
|
-
def self.obter_idioma_do_arquivo(txt_file)
|
31
|
-
idioma = nil
|
32
|
-
File.foreach(txt_file) do |line|
|
33
|
-
if line.strip.start_with?('# language:')
|
34
|
-
idioma = line.strip.split(':').last.strip.downcase
|
35
|
-
break
|
36
|
-
end
|
37
|
-
end
|
38
|
-
idioma || 'pt' # Retorna 'pt' como padrão caso o idioma não seja encontrado
|
39
|
-
end
|
40
|
-
|
41
11
|
##
|
42
12
|
# Gera o conteúdo de um arquivo `.feature` baseado na história fornecida.
|
43
13
|
# Pode operar em três modos:
|
@@ -48,9 +18,11 @@ module Bddgenx
|
|
48
18
|
# @param input [String, Hash] Caminho para um `.txt` ou estrutura de história já processada
|
49
19
|
# @param override_path [String, nil] Caminho alternativo de saída
|
50
20
|
# @return [Array<String, String>] Caminho e conteúdo do `.feature`
|
21
|
+
|
51
22
|
def self.gerar_feature(input, override_path = nil)
|
52
23
|
modo = ENV['BDD_MODE']&.to_sym || :static
|
53
24
|
|
25
|
+
# Verifique o idioma antes de continuar
|
54
26
|
if input.is_a?(String) && input.end_with?('.txt') && [:gemini, :chatgpt].include?(modo)
|
55
27
|
# Geração com IA
|
56
28
|
raw_txt = File.read(input)
|
@@ -62,38 +34,38 @@ module Bddgenx
|
|
62
34
|
grupos: []
|
63
35
|
}
|
64
36
|
|
37
|
+
# Detecta idioma do arquivo
|
38
|
+
idioma = Utils::detecta_idioma_de_texto(raw_txt)
|
39
|
+
historia[:idioma] = idioma
|
40
|
+
|
65
41
|
texto_gerado = if modo == :gemini
|
66
42
|
GeminiCliente.gerar_cenarios(raw_txt)
|
67
43
|
else
|
68
|
-
|
44
|
+
ChatGptCliente.gerar_cenarios(raw_txt)
|
69
45
|
end
|
70
46
|
|
71
47
|
historia[:grupos] << {
|
72
48
|
tipo: 'gerado',
|
73
49
|
tag: 'ia',
|
74
|
-
passos:
|
50
|
+
passos: Utils.limpar(texto_gerado).lines.map(&:strip).reject(&:empty?)
|
75
51
|
}
|
76
52
|
else
|
77
53
|
# Geração estática
|
78
54
|
historia = input.is_a?(String) ? Parser.ler_historia(input) : input
|
79
55
|
end
|
80
56
|
|
81
|
-
#
|
82
|
-
idioma = historia[:idioma] || obter_idioma_do_arquivo(input)
|
57
|
+
# Verifique o idioma que está sendo usado
|
58
|
+
idioma = historia[:idioma] || Utils::obter_idioma_do_arquivo(input)
|
59
|
+
|
60
|
+
# Verifique se o idioma está correto
|
61
|
+
puts "Idioma detectado: #{idioma}"
|
62
|
+
|
83
63
|
cont = 1
|
84
64
|
|
85
65
|
# Cria nome-base do arquivo .feature
|
86
|
-
nome_base = historia[:quero]
|
87
|
-
.gsub(/[^a-z0-9]/i, '_')
|
88
|
-
.downcase
|
89
|
-
.split('_')
|
90
|
-
.reject(&:empty?)
|
91
|
-
.first(5)
|
92
|
-
.join('_')
|
93
|
-
|
66
|
+
nome_base = historia[:quero].gsub(/[^a-z0-9]/i, '_').downcase.split('_').reject(&:empty?).first(5).join('_')
|
94
67
|
caminho = override_path || "features/#{nome_base}.feature"
|
95
68
|
|
96
|
-
# Palavras-chave localizadas
|
97
69
|
palavras = {
|
98
70
|
feature: idioma == 'en' ? 'Feature' : 'Funcionalidade',
|
99
71
|
contexto: idioma == 'en' ? 'Background' : 'Contexto',
|
@@ -103,7 +75,6 @@ module Bddgenx
|
|
103
75
|
regra: idioma == 'en' ? 'Rule' : 'Regra'
|
104
76
|
}
|
105
77
|
|
106
|
-
# Cabeçalho do arquivo .feature
|
107
78
|
conteudo = <<~GHK
|
108
79
|
# language: #{idioma}
|
109
80
|
#{palavras[:feature]}: #{historia[:quero].sub(/^Quero\s*/i,'')}
|
@@ -114,9 +85,9 @@ module Bddgenx
|
|
114
85
|
|
115
86
|
# Controle para não repetir passos
|
116
87
|
passos_unicos = Set.new
|
117
|
-
pt_map = GHERKIN_MAP_PT_EN
|
118
|
-
en_map = GHERKIN_MAP_EN_PT
|
119
|
-
detect = ALL_KEYS
|
88
|
+
pt_map = Utils::GHERKIN_MAP_PT_EN
|
89
|
+
en_map = Utils::GHERKIN_MAP_EN_PT
|
90
|
+
detect = Utils::ALL_KEYS
|
120
91
|
|
121
92
|
historia[:grupos].each do |grupo|
|
122
93
|
passos = grupo[:passos] || []
|
@@ -101,16 +101,18 @@ module Bddgenx
|
|
101
101
|
puts "\n🔍 #{I18n.t('messages.processing')}: #{arquivo}"
|
102
102
|
|
103
103
|
historia = Parser.ler_historia(arquivo)
|
104
|
+
idioma = Utils.obter_idioma_do_arquivo(arquivo) || historia[:idioma]
|
105
|
+
historia[:idioma] = idioma
|
104
106
|
unless Validator.validar(historia)
|
105
107
|
ignored += 1
|
106
108
|
puts "❌ #{I18n.t('messages.invalid_story')}: #{arquivo}"
|
107
109
|
next
|
108
110
|
end
|
109
111
|
|
110
|
-
# Geração via IA (ChatGPT, Gemini
|
112
|
+
# Geração via IA (ChatGPT, Gemini)
|
111
113
|
if %w[gemini chatgpt].include?(modo)
|
112
114
|
puts I18n.t('messages.start_ia', modo: modo.capitalize)
|
113
|
-
idioma =
|
115
|
+
idioma = Utils.obter_idioma_do_arquivo(arquivo) || historia[:idioma]
|
114
116
|
|
115
117
|
feature_text = Support::Loader.run(I18n.t('messages.ia_waiting'), :default) do
|
116
118
|
case modo
|
@@ -123,7 +125,7 @@ module Bddgenx
|
|
123
125
|
|
124
126
|
if feature_text
|
125
127
|
feature_path = Generator.path_para_feature(arquivo)
|
126
|
-
feature_content =
|
128
|
+
feature_content = Utils.limpar(feature_text)
|
127
129
|
else
|
128
130
|
ignored += 1
|
129
131
|
puts I18n.t('messages.feature_fail', arquivo: arquivo)
|
@@ -8,15 +8,6 @@
|
|
8
8
|
|
9
9
|
module Bddgenx
|
10
10
|
class StepsGenerator
|
11
|
-
# Palavras-chave Gherkin em Português
|
12
|
-
GHERKIN_KEYS_PT = %w[Dado Quando Então E Mas].freeze
|
13
|
-
|
14
|
-
# Palavras-chave Gherkin em Inglês
|
15
|
-
GHERKIN_KEYS_EN = %w[Given When Then And But].freeze
|
16
|
-
|
17
|
-
# Conjunto de todas as palavras-chave reconhecidas
|
18
|
-
ALL_KEYS = GHERKIN_KEYS_PT + GHERKIN_KEYS_EN
|
19
|
-
|
20
11
|
##
|
21
12
|
# Transforma uma string em camelCase (sem alterar acentuação).
|
22
13
|
#
|
@@ -38,23 +29,20 @@ module Bddgenx
|
|
38
29
|
raise ArgumentError, I18n.t('errors.invalid_path', path: feature_path.class) unless feature_path.is_a?(String)
|
39
30
|
|
40
31
|
linhas = File.readlines(feature_path)
|
32
|
+
lang = Utils::detecta_idioma_de_texto(linhas.join)
|
33
|
+
|
34
|
+
I18n.locale = lang.to_sym rescue :pt
|
41
35
|
|
42
|
-
# Detecta o idioma a partir da linha `# language:`
|
43
|
-
lang = if (m = linhas.find { |l| l =~ /^#\s*language:\s*(\w+)/i })
|
44
|
-
m[/^#\s*language:\s*(\w+)/i, 1].downcase
|
45
|
-
else
|
46
|
-
'pt'
|
47
|
-
end
|
48
36
|
|
49
37
|
# Define o locale do I18n conforme idioma detectado
|
50
38
|
I18n.locale = lang.to_sym rescue :pt
|
51
39
|
|
52
|
-
pt_para_en = GHERKIN_KEYS_PT.zip(GHERKIN_KEYS_EN).to_h
|
53
|
-
en_para_pt = GHERKIN_KEYS_EN.zip(GHERKIN_KEYS_PT).to_h
|
40
|
+
pt_para_en = Utils::GHERKIN_KEYS_PT.zip(Utils::GHERKIN_KEYS_EN).to_h
|
41
|
+
en_para_pt = Utils::GHERKIN_KEYS_EN.zip(Utils::GHERKIN_KEYS_PT).to_h
|
54
42
|
|
55
43
|
# Seleciona apenas as linhas que representam passos
|
56
44
|
linhas_passos = linhas.map(&:strip).select do |linha|
|
57
|
-
ALL_KEYS.any? { |chave| linha.start_with?(chave + ' ') }
|
45
|
+
Utils::ALL_KEYS.any? { |chave| linha.start_with?(chave + ' ') }
|
58
46
|
end
|
59
47
|
|
60
48
|
return false if linhas_passos.empty?
|
@@ -28,7 +28,7 @@ module Bddgenx
|
|
28
28
|
return fallback_com_gemini(historia, idioma)
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
|
32
32
|
keywords_pt = {
|
33
33
|
feature: "Funcionalidade",
|
34
34
|
scenario: "Cenário",
|
@@ -104,8 +104,8 @@ module Bddgenx
|
|
104
104
|
texto_ia = json.dig("choices", 0, "message", "content")
|
105
105
|
|
106
106
|
if texto_ia
|
107
|
-
texto_limpo =
|
108
|
-
Utils::
|
107
|
+
texto_limpo = Utils.limpar(texto_ia)
|
108
|
+
Utils::remover_steps_duplicados(texto_ia, idioma)
|
109
109
|
|
110
110
|
# Ajusta a linha de idioma no arquivo gerado
|
111
111
|
texto_limpo.sub!(/^# language: .*/, "# language: #{idioma}")
|
@@ -137,24 +137,6 @@ module Bddgenx
|
|
137
137
|
warn I18n.t('messages.fallback_gemini')
|
138
138
|
GeminiCliente.gerar_cenarios(historia, idioma)
|
139
139
|
end
|
140
|
-
|
141
|
-
##
|
142
|
-
# Detecta o idioma de um arquivo de feature pela linha "# language:".
|
143
|
-
#
|
144
|
-
# @param caminho_arquivo [String] Caminho para o arquivo de feature.
|
145
|
-
# @return [String] Código do idioma detectado ('pt' por padrão).
|
146
|
-
#
|
147
|
-
def self.detecta_idioma_arquivo(caminho_arquivo)
|
148
|
-
return 'pt' unless File.exist?(caminho_arquivo)
|
149
|
-
|
150
|
-
File.foreach(caminho_arquivo) do |linha|
|
151
|
-
if linha =~ /^#\s*language:\s*(\w{2})/i
|
152
|
-
return $1.downcase
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
'pt'
|
157
|
-
end
|
158
140
|
end
|
159
141
|
end
|
160
142
|
end
|
@@ -106,15 +106,15 @@ module Bddgenx
|
|
106
106
|
texto_ia = json["candidates"].first.dig("content", "parts", 0, "text")
|
107
107
|
if texto_ia
|
108
108
|
# Limpeza e sanitização do texto para manter padrão Gherkin
|
109
|
-
texto_limpo =
|
110
|
-
Utils
|
109
|
+
texto_limpo = Utils.limpar(texto_ia)
|
110
|
+
Utils.remover_steps_duplicados(texto_ia, idioma)
|
111
111
|
|
112
112
|
# Ajuste da diretiva de idioma na saída gerada
|
113
113
|
texto_limpo.sub!(/^# language: .*/, "# language: #{idioma}")
|
114
114
|
texto_limpo.prepend("# language: #{idioma}\n") unless texto_limpo.start_with?("# language:")
|
115
115
|
|
116
116
|
# Garante diretiva de idioma
|
117
|
-
feature_text =
|
117
|
+
feature_text = Utils.limpar(texto_ia)
|
118
118
|
feature_text.sub!(/^# language: .*/, "") # remove qualquer # language: existente
|
119
119
|
feature_text.prepend("# language: #{idioma}\n") # insere a correta
|
120
120
|
|
@@ -129,24 +129,6 @@ module Bddgenx
|
|
129
129
|
return nil
|
130
130
|
end
|
131
131
|
end
|
132
|
-
|
133
|
-
##
|
134
|
-
# Detecta o idioma do arquivo de feature pela linha "# language:".
|
135
|
-
#
|
136
|
-
# @param caminho_arquivo [String] Caminho do arquivo para detecção do idioma.
|
137
|
-
# @return [String] Código do idioma detectado (ex: 'pt'), padrão 'pt'.
|
138
|
-
#
|
139
|
-
def self.detecta_idioma_arquivo(caminho_arquivo)
|
140
|
-
return 'pt' unless File.exist?(caminho_arquivo)
|
141
|
-
|
142
|
-
File.foreach(caminho_arquivo) do |linha|
|
143
|
-
if linha =~ /^#\s*language:\s*(\w{2})/i
|
144
|
-
return $1.downcase
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
'pt' # idioma padrão caso não encontre
|
149
|
-
end
|
150
132
|
end
|
151
133
|
end
|
152
134
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Bddgenx
|
2
|
+
module Utils
|
3
|
+
# Palavras-chave do Gherkin em Português
|
4
|
+
GHERKIN_KEYS_PT = %w[Dado Quando Então E Mas].freeze
|
5
|
+
|
6
|
+
# Palavras-chave do Gherkin em Inglês
|
7
|
+
GHERKIN_KEYS_EN = %w[Given When Then And But].freeze
|
8
|
+
|
9
|
+
# Mapeamento PT → EN
|
10
|
+
GHERKIN_MAP_PT_EN = GHERKIN_KEYS_PT.zip(GHERKIN_KEYS_EN).to_h
|
11
|
+
|
12
|
+
# Mapeamento EN → PT
|
13
|
+
GHERKIN_MAP_EN_PT = GHERKIN_KEYS_EN.zip(GHERKIN_KEYS_PT).to_h
|
14
|
+
|
15
|
+
# Todas as palavras-chave reconhecidas
|
16
|
+
ALL_KEYS = GHERKIN_KEYS_PT + GHERKIN_KEYS_EN
|
17
|
+
|
18
|
+
##
|
19
|
+
# Extrai o idioma do arquivo .txt, a partir da linha "# language:".
|
20
|
+
# @param txt_file [String] Caminho do arquivo .txt
|
21
|
+
# @return [String] O idioma extraído ou 'pt' como padrão
|
22
|
+
def self.obter_idioma_do_arquivo(caminho_arquivo)
|
23
|
+
return 'pt' unless File.exist?(caminho_arquivo)
|
24
|
+
|
25
|
+
File.foreach(caminho_arquivo) do |linha|
|
26
|
+
if linha =~ /^#\s*language:\s*(\w{2})/i
|
27
|
+
return $1.downcase
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
'pt' # idioma padrão caso não encontre
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Detecta o idioma a partir de um texto (como conteúdo de arquivo ou string).
|
36
|
+
# @param texto [String] O texto onde o idioma será detectado
|
37
|
+
# @return [String] O idioma detectado ('pt' por padrão)
|
38
|
+
def self.detecta_idioma_de_texto(texto)
|
39
|
+
if texto =~ /^#\s*language:\s*(\w{2})/i
|
40
|
+
return $1.downcase
|
41
|
+
end
|
42
|
+
'pt' # Idioma padrão se o idioma não for detectado
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Bddgenx
|
2
|
+
module Utils
|
3
|
+
# Remove passos duplicados em um texto de cenários BDD,
|
4
|
+
# levando em conta o idioma para identificar as keywords (Given, When, Then, And / Dado, Quando, Então, E)
|
5
|
+
#
|
6
|
+
# Parâmetros:
|
7
|
+
# - texto: string contendo o texto do cenário BDD
|
8
|
+
# - idioma: 'en' para inglês ou qualquer outro para português
|
9
|
+
#
|
10
|
+
# Retorna o texto com passos duplicados removidos, preservando a ordem original
|
11
|
+
def self.remover_steps_duplicados(texto, idioma)
|
12
|
+
# Define as keywords principais para o idioma
|
13
|
+
keywords = idioma == 'en' ? %w[Given When Then And] : %w[Dado Quando Então E]
|
14
|
+
|
15
|
+
# Conjunto para rastrear passos já vistos (versão canônica)
|
16
|
+
seen = Set.new
|
17
|
+
resultado = []
|
18
|
+
|
19
|
+
# Percorre linha a linha
|
20
|
+
texto.each_line do |linha|
|
21
|
+
# Verifica se a linha começa com uma das keywords
|
22
|
+
if keywords.any? { |kw| linha.strip.start_with?(kw) }
|
23
|
+
# Canonicaliza o passo para comparação sem variações irrelevantes
|
24
|
+
canonical = Utils::canonicalize_step(linha, keywords)
|
25
|
+
|
26
|
+
# Só adiciona se ainda não viu o passo canônico
|
27
|
+
unless seen.include?(canonical)
|
28
|
+
seen.add(canonical)
|
29
|
+
resultado << linha
|
30
|
+
end
|
31
|
+
else
|
32
|
+
# Linhas que não são passos são adicionadas normalmente
|
33
|
+
resultado << linha
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Retorna o texto reconstruído sem duplicatas
|
38
|
+
resultado.join
|
39
|
+
end
|
40
|
+
|
41
|
+
# Gera uma versão canônica (normalizada) do passo para facilitar
|
42
|
+
# a identificação de duplicatas mesmo com variações menores de texto.
|
43
|
+
#
|
44
|
+
# Exemplo: Dado "usuario" fez login e Dado <usuario> fez login
|
45
|
+
# gerarão o mesmo canonical para evitar repetição.
|
46
|
+
#
|
47
|
+
# Passos:
|
48
|
+
# - Remove a keyword (Given, When, etc) do começo
|
49
|
+
# - Substitui textos entre aspas, placeholders <> e números por <param>
|
50
|
+
# - Remove acentuação e pontuação para normalizar
|
51
|
+
# - Converte para minúsculas e remove espaços extras
|
52
|
+
#
|
53
|
+
# Parâmetros:
|
54
|
+
# - linha: string com o passo completo
|
55
|
+
# - keywords: array com as keywords para remoção
|
56
|
+
#
|
57
|
+
# Retorna uma string normalizada representando o passo
|
58
|
+
def self.canonicalize_step(linha, keywords)
|
59
|
+
texto = linha.dup.strip
|
60
|
+
|
61
|
+
# Remove a keyword do início, se existir
|
62
|
+
keywords.each do |kw|
|
63
|
+
texto.sub!(/^#{kw}\s+/i, '')
|
64
|
+
end
|
65
|
+
|
66
|
+
# Substitui textos entre aspas, placeholders e números por <param>
|
67
|
+
texto.gsub!(/"[^"]*"|<[^>]*>|\b\d+\b/, '<param>')
|
68
|
+
|
69
|
+
# Remove acentos usando Unicode Normalization Form KD (decompõe caracteres)
|
70
|
+
texto = Unicode.normalize_KD(texto).gsub(/\p{Mn}/, '')
|
71
|
+
|
72
|
+
# Remove pontuação, deixando apenas letras, números, espaços e <>
|
73
|
+
texto.gsub!(/[^a-zA-Z0-9\s<>]/, '')
|
74
|
+
|
75
|
+
# Converte para minúsculas, remove espaços extras e retorna
|
76
|
+
texto.downcase.strip.squeeze(" ")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/env.rb
CHANGED
@@ -59,11 +59,13 @@ require 'bundler/setup' if File.exist?(File.expand_path('../../Gemfile', __FILE_
|
|
59
59
|
# 🧩 Módulos utilitários da gem
|
60
60
|
# --------------------------------------
|
61
61
|
|
62
|
-
require_relative 'bddgenx/support/gherkin_cleaner' # Sanitização de Gherkin gerado
|
63
|
-
require_relative 'bddgenx/support/remover_steps_duplicados' # Remove passos duplicados
|
64
62
|
require_relative 'bddgenx/support/validator' # Valida estrutura de entrada
|
65
63
|
require_relative 'bddgenx/support/font_loader' # Carrega fontes do PDF
|
66
64
|
|
65
|
+
require_relative 'bddgenx/utils/gherkin_cleaner_helper' # Sanitização de Gherkin gerado
|
66
|
+
require_relative 'bddgenx/utils/remover_steps_duplicados_helper' # Remove passos duplicados
|
67
|
+
require_relative 'bddgenx/utils/language_helper'
|
68
|
+
|
67
69
|
# --------------------------------------
|
68
70
|
# 🤖 Clientes de IA (ChatGPT, Gemini)
|
69
71
|
# --------------------------------------
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bddgenx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.4.
|
4
|
+
version: 2.4.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Nascimento
|
@@ -139,10 +139,11 @@ files:
|
|
139
139
|
- lib/bddgenx/reports/tracer.rb
|
140
140
|
- lib/bddgenx/setup.rb
|
141
141
|
- lib/bddgenx/support/font_loader.rb
|
142
|
-
- lib/bddgenx/support/gherkin_cleaner.rb
|
143
142
|
- lib/bddgenx/support/loader.rb
|
144
|
-
- lib/bddgenx/support/remover_steps_duplicados.rb
|
145
143
|
- lib/bddgenx/support/validator.rb
|
144
|
+
- lib/bddgenx/utils/gherkin_cleaner_helper.rb
|
145
|
+
- lib/bddgenx/utils/language_helper.rb
|
146
|
+
- lib/bddgenx/utils/remover_steps_duplicados_helper.rb
|
146
147
|
- lib/bddgenx/version.rb
|
147
148
|
- lib/env.rb
|
148
149
|
- lib/parser.rb
|
@@ -1,81 +0,0 @@
|
|
1
|
-
module Bddgenx
|
2
|
-
module Utils
|
3
|
-
class StepCleaner
|
4
|
-
# Remove passos duplicados em um texto de cenários BDD,
|
5
|
-
# levando em conta o idioma para identificar as keywords (Given, When, Then, And / Dado, Quando, Então, E)
|
6
|
-
#
|
7
|
-
# Parâmetros:
|
8
|
-
# - texto: string contendo o texto do cenário BDD
|
9
|
-
# - idioma: 'en' para inglês ou qualquer outro para português
|
10
|
-
#
|
11
|
-
# Retorna o texto com passos duplicados removidos, preservando a ordem original
|
12
|
-
def self.remover_steps_duplicados(texto, idioma)
|
13
|
-
# Define as keywords principais para o idioma
|
14
|
-
keywords = idioma == 'en' ? %w[Given When Then And] : %w[Dado Quando Então E]
|
15
|
-
|
16
|
-
# Conjunto para rastrear passos já vistos (versão canônica)
|
17
|
-
seen = Set.new
|
18
|
-
resultado = []
|
19
|
-
|
20
|
-
# Percorre linha a linha
|
21
|
-
texto.each_line do |linha|
|
22
|
-
# Verifica se a linha começa com uma das keywords
|
23
|
-
if keywords.any? { |kw| linha.strip.start_with?(kw) }
|
24
|
-
# Canonicaliza o passo para comparação sem variações irrelevantes
|
25
|
-
canonical = canonicalize_step(linha, keywords)
|
26
|
-
|
27
|
-
# Só adiciona se ainda não viu o passo canônico
|
28
|
-
unless seen.include?(canonical)
|
29
|
-
seen.add(canonical)
|
30
|
-
resultado << linha
|
31
|
-
end
|
32
|
-
else
|
33
|
-
# Linhas que não são passos são adicionadas normalmente
|
34
|
-
resultado << linha
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Retorna o texto reconstruído sem duplicatas
|
39
|
-
resultado.join
|
40
|
-
end
|
41
|
-
|
42
|
-
# Gera uma versão canônica (normalizada) do passo para facilitar
|
43
|
-
# a identificação de duplicatas mesmo com variações menores de texto.
|
44
|
-
#
|
45
|
-
# Exemplo: Dado "usuario" fez login e Dado <usuario> fez login
|
46
|
-
# gerarão o mesmo canonical para evitar repetição.
|
47
|
-
#
|
48
|
-
# Passos:
|
49
|
-
# - Remove a keyword (Given, When, etc) do começo
|
50
|
-
# - Substitui textos entre aspas, placeholders <> e números por <param>
|
51
|
-
# - Remove acentuação e pontuação para normalizar
|
52
|
-
# - Converte para minúsculas e remove espaços extras
|
53
|
-
#
|
54
|
-
# Parâmetros:
|
55
|
-
# - linha: string com o passo completo
|
56
|
-
# - keywords: array com as keywords para remoção
|
57
|
-
#
|
58
|
-
# Retorna uma string normalizada representando o passo
|
59
|
-
def self.canonicalize_step(linha, keywords)
|
60
|
-
texto = linha.dup.strip
|
61
|
-
|
62
|
-
# Remove a keyword do início, se existir
|
63
|
-
keywords.each do |kw|
|
64
|
-
texto.sub!(/^#{kw}\s+/i, '')
|
65
|
-
end
|
66
|
-
|
67
|
-
# Substitui textos entre aspas, placeholders e números por <param>
|
68
|
-
texto.gsub!(/"[^"]*"|<[^>]*>|\b\d+\b/, '<param>')
|
69
|
-
|
70
|
-
# Remove acentos usando Unicode Normalization Form KD (decompõe caracteres)
|
71
|
-
texto = Unicode.normalize_KD(texto).gsub(/\p{Mn}/, '')
|
72
|
-
|
73
|
-
# Remove pontuação, deixando apenas letras, números, espaços e <>
|
74
|
-
texto.gsub!(/[^a-zA-Z0-9\s<>]/, '')
|
75
|
-
|
76
|
-
# Converte para minúsculas, remove espaços extras e retorna
|
77
|
-
texto.downcase.strip.squeeze(" ")
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|