bddgenx 2.4.6 → 2.4.9

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: b9a93bafcbda4d21a1cc0b340565b6dbf269fe635fddbb69ead9c9154ebfef95
4
- data.tar.gz: 48421b76147e864c97b4a6e024173ca1f587c23778166ae29f69ac3290aac194
3
+ metadata.gz: adf58e7c99b89027c56542f9b2dbff2cc71c336ca164eb6a2bc65308546beef3
4
+ data.tar.gz: 2093d4ac9a62eea83e87eb7faff89a43196f0a4585f885e515c967e52e0688b8
5
5
  SHA512:
6
- metadata.gz: 98853eae51856f72cc90a007f5949c94e3b9151aaf7e18c0b6163f2f6397685159ef8fbf8c6fe6c37ded30b275cbfc4c5499ef18e0cb06fc5a61d9d9aac32a9c
7
- data.tar.gz: 1979c869cbb720f13d498f658986a9eecf3b79e85ac82bd45d0ee52415f70b533925e9a0a601af139ce5855b10f90322d7b9aaadef1f19649593ca906a897a2e
6
+ metadata.gz: 10e4ffafcb2230a6a88cd3556ab995dfe936c36925c86027fee92d0e8f74f7e7b75c4bf6ea0415ec5a083dd5c63f9043750b350dcfa04176b3740c1123e67fa8
7
+ data.tar.gz: 3791efeb6c7a684e666bd73cfa03d21d2f8cc0825e6791655aa639f51901395c0595170661ff06b331e801ee2a158ec5193182ce4e6030712133c0c10c0b1176
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.4.6
1
+ 2.4.9
@@ -1,34 +1,92 @@
1
- # lib/bddgenx/configuration.rb
1
+ # encoding: utf-8
2
+ #
3
+ # Este arquivo define a classe de configuração global da gem BDDGenX.
4
+ #
5
+ # A classe `Bddgenx::Configuration` permite configurar o modo de operação da ferramenta
6
+ # (ex: estático ou com IA) e define as variáveis de ambiente que armazenam as chaves de API
7
+ # para serviços externos como OpenAI (ChatGPT), Google Gemini e Microsoft Copilot.
8
+
2
9
  module Bddgenx
10
+ # Classe de configuração principal da gem BDDGenX.
11
+ # Permite definir o modo de geração BDD e os nomes das variáveis de ambiente
12
+ # que armazenam as chaves de API para integração com serviços de IA.
3
13
  class Configuration
4
- # :static, :chatgpt ou :gemini
14
+ # Modo de execução da gem.
15
+ # Pode ser:
16
+ # - :static → geração local
17
+ # - :chatgpt → uso da IA do ChatGPT (OpenAI)
18
+ # - :gemini → uso da IA Gemini (Google)
19
+ # - :copilot → uso do Microsoft Copilot
20
+ #
21
+ # @return [Symbol]
5
22
  attr_accessor :mode
6
23
 
7
- # Nomes das ENV vars para as chaves
8
- attr_accessor :openai_api_key_env, :gemini_api_key_env
24
+ # Nome da variável de ambiente que contém a chave da API da OpenAI
25
+ # @return [String]
26
+ attr_accessor :openai_api_key_env
27
+
28
+ # Nome da variável de ambiente que contém a chave da API do Google Gemini
29
+ # @return [String]
30
+ attr_accessor :gemini_api_key_env
31
+
32
+ # Nome da variável de ambiente que contém a chave da API do Microsoft Copilot
33
+ # @return [String]
34
+ attr_accessor :microsoft_copilot_api_env
9
35
 
36
+ ##
37
+ # Inicializa a configuração com valores padrão:
38
+ # - modo: :static
39
+ # - ENV keys: 'OPENAI_API_KEY', 'GEMINI_API_KEY', 'MICROSOFT_COPILOT_API_KEY'
10
40
  def initialize
11
41
  @mode = :static
12
42
  @openai_api_key_env = 'OPENAI_API_KEY'
13
43
  @gemini_api_key_env = 'GEMINI_API_KEY'
44
+ @microsoft_copilot_api_env = 'MICROSOFT_COPILOT_API_KEY'
14
45
  end
15
46
 
16
- # Retorna a chave real, carregada do ENV
47
+ ##
48
+ # Retorna a chave da API do OpenAI, lida diretamente da ENV.
49
+ #
50
+ # @return [String, nil] Chave de API ou nil se não definida
17
51
  def openai_api_key
18
52
  ENV[@openai_api_key_env]
19
53
  end
20
54
 
55
+ ##
56
+ # Retorna a chave da API do Gemini, lida diretamente da ENV.
57
+ #
58
+ # @return [String, nil] Chave de API ou nil se não definida
21
59
  def gemini_api_key
22
60
  ENV[@gemini_api_key_env]
23
61
  end
62
+
63
+ ##
64
+ # Retorna a chave da API do Microsoft Copilot, lida diretamente da ENV.
65
+ #
66
+ # @return [String, nil] Chave de API ou nil se não definida
67
+ def microsoft_copilot_api_key
68
+ ENV[@microsoft_copilot_api_env]
69
+ end
24
70
  end
25
71
 
26
- # Singleton de configuração
72
+ ##
73
+ # Retorna uma instância singleton da configuração da gem.
74
+ #
75
+ # @return [Configuration]
27
76
  def self.configuration
28
77
  @configuration ||= Configuration.new
29
78
  end
30
79
 
31
- # DSL para configurar
80
+ ##
81
+ # Permite configurar a gem usando um bloco DSL.
82
+ #
83
+ # Exemplo:
84
+ # Bddgenx.configure do |config|
85
+ # config.mode = :gemini
86
+ # config.gemini_api_key_env = 'MY_GEMINI_KEY'
87
+ # end
88
+ #
89
+ # @yieldparam config [Configuration] instância de configuração atual
32
90
  def self.configure
33
91
  yield(configuration)
34
92
  end
@@ -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
- ChatGPTCliente.gerar_cenarios(raw_txt)
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: GherkinCleaner.limpar(texto_gerado).lines.map(&:strip).reject(&:empty?)
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
- # Atribui o idioma corretamente antes da geração do conteúdo
82
- idioma = historia[:idioma] || obter_idioma_do_arquivo(input) # Uso da função para pegar o idioma do arquivo
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] || []
@@ -1,36 +1,49 @@
1
1
  # lib/bddgenx/cli.rb
2
2
  # encoding: utf-8
3
3
  #
4
- # Este arquivo define a classe Runner (CLI) da gem bddgenx,
5
- # responsável por orquestrar todo o fluxo de geração BDD:
6
- # leitura e validação de histórias, geração de features, steps,
7
- # exportação de PDFs e controle de modo (static / IA).
4
+ # Este arquivo define a classe Runner (CLI) da gem BDDGenX.
5
+ #
6
+ # A Runner é responsável por orquestrar todo o fluxo de geração BDD:
7
+ # - Leitura e validação de histórias (arquivos `.txt`)
8
+ # - Geração de arquivos `.feature` e steps
9
+ # - Integração com IA (ChatGPT, Gemini, Copilot)
10
+ # - Exportação para PDF
11
+ # - Rastreabilidade via CSV
12
+ #
13
+ # Esta classe é o ponto de entrada da gem em execução via terminal (CLI).
14
+ # O comportamento é configurado com variáveis de ambiente como:
15
+ # - BDDGENX_MODE (static, chatgpt, gemini, copilot)
16
+ # - BDDGENX_LANG (pt, en)
8
17
 
9
18
  require_relative '../../bddgenx'
10
19
 
11
20
  module Bddgenx
12
- # Classe principal de execução da gem.
13
- # Atua como ponto de entrada (CLI) para processar arquivos de entrada
14
- # e gerar todos os artefatos BDD relacionados.
21
+ # Classe principal de execução da gem BDDGenX.
22
+ # Controla o fluxo de leitura de histórias, geração de artefatos BDD
23
+ # e exportação de relatórios. Suporta execução via terminal.
15
24
  class Runner
16
25
 
17
26
  ##
18
- # Retorna a lista de arquivos de entrada.
19
- # Se houver argumentos em ARGV, utiliza-os como nomes de arquivos `.txt`.
20
- # Caso contrário, chama prompt interativo.
27
+ # Retorna a lista de arquivos `.txt` a processar.
28
+ #
29
+ # - Se ARGV contiver argumentos, usa esses nomes como arquivos `.txt`
30
+ # - Caso contrário, entra em modo interativo para seleção manual
21
31
  #
22
- # @param input_dir [String] Caminho do diretório de entrada
23
- # @return [Array<String>] Lista de arquivos `.txt` a processar
32
+ # @param input_dir [String] Caminho do diretório com arquivos `.txt`
33
+ # @return [Array<String>] Lista de caminhos de arquivos `.txt`
24
34
  def self.choose_files(input_dir)
25
35
  ARGV.any? ? selecionar_arquivos_txt(input_dir) : choose_input(input_dir)
26
36
  end
27
37
 
28
38
  ##
29
- # Processa argumentos ARGV e converte em caminhos válidos de arquivos `.txt`.
30
- # Adiciona extensão `.txt` se ausente e remove arquivos inexistentes.
39
+ # Processa os argumentos da linha de comando (ARGV) e gera caminhos
40
+ # completos para arquivos `.txt` no diretório informado.
31
41
  #
32
- # @param input_dir [String] Diretório onde estão os arquivos
33
- # @return [Array<String>] Caminhos válidos para arquivos de entrada
42
+ # - Se a extensão `.txt` estiver ausente, ela é adicionada.
43
+ # - Arquivos inexistentes são ignorados com aviso.
44
+ #
45
+ # @param input_dir [String] Diretório base onde estão os arquivos `.txt`
46
+ # @return [Array<String>] Lista de arquivos válidos encontrados
34
47
  def self.selecionar_arquivos_txt(input_dir)
35
48
  ARGV.map do |arg|
36
49
  nome = arg.end_with?('.txt') ? arg : "#{arg}.txt"
@@ -44,15 +57,18 @@ module Bddgenx
44
57
  end
45
58
 
46
59
  ##
47
- # Interface interativa para o usuário selecionar arquivos `.txt` a processar.
48
- # Exibe uma lista dos arquivos disponíveis e solicita um número ao usuário.
60
+ # Modo interativo para o usuário escolher o arquivo `.txt` a ser processado.
61
+ #
62
+ # Exibe uma lista numerada com os arquivos disponíveis no diretório.
63
+ # O usuário pode selecionar um específico ou pressionar ENTER para todos.
49
64
  #
50
- # @param input_dir [String] Diretório de entrada
51
- # @return [Array<String>] Lista com o arquivo escolhido ou todos
65
+ # @param input_dir [String] Caminho do diretório com arquivos `.txt`
66
+ # @return [Array<String>] Arquivo selecionado ou todos disponíveis
52
67
  def self.choose_input(input_dir)
53
68
  files = Dir.glob(File.join(input_dir, '*.txt'))
54
69
  if files.empty?
55
- warn "❌ Não há arquivos .txt no diretório #{input_dir}"; exit 1
70
+ warn "❌ Não há arquivos .txt no diretório #{input_dir}"
71
+ exit 1
56
72
  end
57
73
 
58
74
  puts "Selecione o arquivo de história para processar:"
@@ -63,20 +79,24 @@ module Bddgenx
63
79
  return files if choice.empty?
64
80
  idx = choice.to_i - 1
65
81
  unless idx.between?(0, files.size - 1)
66
- warn "❌ Escolha inválida."; exit 1
82
+ warn "❌ Escolha inválida."
83
+ exit 1
67
84
  end
68
85
  [files[idx]]
69
86
  end
70
87
 
71
88
  ##
72
- # Executa o fluxo completo de geração BDD:
73
- # - Define o modo (static / IA)
74
- # - Coleta arquivos de entrada
75
- # - Valida as histórias
76
- # - Gera arquivos `.feature` e `steps`
77
- # - Exporta PDFs e faz backup de versões antigas
89
+ # Executa o fluxo principal de geração dos artefatos BDD.
90
+ #
91
+ # Etapas executadas:
92
+ # - Detecta o modo de execução via ENV['BDDGENX_MODE'] (static/chatgpt/gemini/copilot)
93
+ # - Carrega e valida histórias de usuário
94
+ # - Gera arquivos `.feature` e seus respectivos steps
95
+ # - Executa geração via IA (quando aplicável)
96
+ # - Exporta arquivos em PDF
97
+ # - Gera rastreabilidade via CSV
78
98
  #
79
- # O modo de execução é lido da variável de ambiente `BDDGENX_MODE`.
99
+ # Exibe no final um resumo das operações executadas.
80
100
  #
81
101
  # @return [void]
82
102
  def self.execute
@@ -90,7 +110,7 @@ module Bddgenx
90
110
  exit 1
91
111
  end
92
112
 
93
- # Contadores de geração
113
+ # Contadores
94
114
  total = features = steps = ignored = 0
95
115
  skipped_steps = []
96
116
  generated_pdfs = []
@@ -101,29 +121,30 @@ module Bddgenx
101
121
  puts "\n🔍 #{I18n.t('messages.processing')}: #{arquivo}"
102
122
 
103
123
  historia = Parser.ler_historia(arquivo)
124
+ idioma = Utils.obter_idioma_do_arquivo(arquivo) || historia[:idioma]
125
+ historia[:idioma] = idioma
126
+
104
127
  unless Validator.validar(historia)
105
128
  ignored += 1
106
129
  puts "❌ #{I18n.t('messages.invalid_story')}: #{arquivo}"
107
130
  next
108
131
  end
109
132
 
110
- # Geração via IA (ChatGPT, Gemini, Deepseek)
111
- if %w[gemini chatgpt].include?(modo)
133
+ # IA: geração de cenários com Gemini, ChatGPT ou Copilot
134
+ if %w[gemini chatgpt copilot].include?(modo)
112
135
  puts I18n.t('messages.start_ia', modo: modo.capitalize)
113
- idioma = IA::GeminiCliente.detecta_idioma_arquivo(arquivo)
114
136
 
115
137
  feature_text = Support::Loader.run(I18n.t('messages.ia_waiting'), :default) do
116
138
  case modo
117
- when 'gemini'
118
- IA::GeminiCliente.gerar_cenarios(historia, idioma)
119
- when 'chatgpt'
120
- IA::ChatGptCliente.gerar_cenarios(historia, idioma)
139
+ when 'gemini' then IA::GeminiCliente.gerar_cenarios(historia, idioma)
140
+ when 'chatgpt' then IA::ChatGptCliente.gerar_cenarios(historia, idioma)
141
+ when 'copilot' then IA::MicrosoftCopilotCliente.gerar_cenarios(historia, idioma)
121
142
  end
122
143
  end
123
144
 
124
145
  if feature_text
125
146
  feature_path = Generator.path_para_feature(arquivo)
126
- feature_content = Bddgenx::GherkinCleaner.limpar(feature_text)
147
+ feature_content = Utils.limpar(feature_text)
127
148
  else
128
149
  ignored += 1
129
150
  puts I18n.t('messages.feature_fail', arquivo: arquivo)
@@ -137,7 +158,9 @@ module Bddgenx
137
158
  end
138
159
  end
139
160
 
161
+ # Salva versão antiga se existir
140
162
  Backup.salvar_versao_antiga(feature_path)
163
+
141
164
  features += 1 if Generator.salvar_feature(feature_path, feature_content)
142
165
 
143
166
  if StepsGenerator.gerar_passos(feature_path)
@@ -148,6 +171,8 @@ module Bddgenx
148
171
 
149
172
  FileUtils.mkdir_p('reports')
150
173
  result = PDFExporter.exportar_todos(only_new: true)
174
+ Tracer.adicionar_entrada(historia, feature_path)
175
+
151
176
  generated_pdfs.concat(result[:generated])
152
177
  skipped_pdfs.concat(result[:skipped])
153
178
  end
@@ -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
- # Palavras-chave Gherkin para português e inglês
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 = Bddgenx::GherkinCleaner.limpar(texto_ia)
108
- Utils::StepCleaner.remover_steps_duplicados(texto_ia, idioma)
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 = Bddgenx::GherkinCleaner.limpar(texto_ia)
110
- Utils::StepCleaner.remover_steps_duplicados(texto_ia, idioma)
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 = Bddgenx::GherkinCleaner.limpar(texto_ia)
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