bddgenx 1.0.0 → 2.1.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.
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: 1.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Nascimento
@@ -52,6 +52,62 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: 0.2.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: ruby-openai
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '8.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '8.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: faraday
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 2.13.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 2.13.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: dotenv
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '3.1'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '3.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: unicode
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0.4'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0.4'
55
111
  description: Transforma arquivos .txt com histórias em arquivos .feature, com steps,
56
112
  rastreabilidade e integração com CI/CD.
57
113
  email:
@@ -70,20 +126,22 @@ files:
70
126
  - lib/bddgenx/assets/fonts/DejaVuSansMono-BoldOblique.ttf
71
127
  - lib/bddgenx/assets/fonts/DejaVuSansMono-Oblique.ttf
72
128
  - lib/bddgenx/assets/fonts/DejaVuSansMono.ttf
73
- - lib/bddgenx/generator.rb
129
+ - lib/bddgenx/configuration.rb
130
+ - lib/bddgenx/generators/generator.rb
131
+ - lib/bddgenx/generators/runner.rb
132
+ - lib/bddgenx/generators/steps_generator.rb
133
+ - lib/bddgenx/ia/chatgtp_cliente.rb
74
134
  - lib/bddgenx/ia/gemini_cliente.rb
75
- - lib/bddgenx/ia/gemini_generator.rb
76
- - lib/bddgenx/runner.rb
77
- - lib/bddgenx/steps_generator.rb
78
- - lib/bddgenx/utils/backup.rb
79
- - lib/bddgenx/utils/font_loader.rb
80
- - lib/bddgenx/utils/gherkin_cleaner.rb
81
- - lib/bddgenx/utils/parser.rb
82
- - lib/bddgenx/utils/pdf_exporter.rb
83
- - lib/bddgenx/utils/remover_steps_duplicados.rb
84
- - lib/bddgenx/utils/tracer.rb
85
- - lib/bddgenx/utils/validator.rb
135
+ - lib/bddgenx/reports/backup.rb
136
+ - lib/bddgenx/reports/pdf_exporter.rb
137
+ - lib/bddgenx/reports/tracer.rb
138
+ - lib/bddgenx/support/font_loader.rb
139
+ - lib/bddgenx/support/gherkin_cleaner.rb
140
+ - lib/bddgenx/support/remover_steps_duplicados.rb
141
+ - lib/bddgenx/support/validator.rb
86
142
  - lib/bddgenx/version.rb
143
+ - lib/env.rb
144
+ - lib/parser.rb
87
145
  homepage: https://github.com/David-Nascimento/bdd-generation
88
146
  licenses:
89
147
  - MIT
@@ -1,127 +0,0 @@
1
- # lib/bddgenx/cli.rb
2
- # encoding: utf-8
3
- #
4
- # Este arquivo define a classe Runner (CLI) da gem bddgenx,
5
- # responsável por orquestrar o fluxo de leitura de histórias,
6
- # validação, geração de features, steps, backups e exportação de PDFs.
7
- require 'dotenv/load'
8
- require 'fileutils'
9
- require_relative 'utils/parser'
10
- require_relative 'generator'
11
- require_relative 'utils/pdf_exporter'
12
- require_relative 'steps_generator'
13
- require_relative 'utils/validator'
14
- require_relative 'utils/backup'
15
- require_relative 'ia/gemini_cliente'
16
- require_relative 'utils/gherkin_cleaner'
17
- require_relative 'gemini_generator'
18
-
19
- module Bddgenx
20
- # Ponto de entrada da gem: coordena todo o processo de geração BDD.
21
- class Runner
22
- def self.choose_files(input_dir)
23
- ARGV.any? ? selecionar_arquivos_txt(input_dir) : choose_input(input_dir)
24
- end
25
-
26
- def self.selecionar_arquivos_txt(input_dir)
27
- ARGV.map do |arg|
28
- nome = arg.end_with?('.txt') ? arg : "#{arg}.txt"
29
- path = File.join(input_dir, nome)
30
- unless File.exist?(path)
31
- warn "⚠️ Arquivo não encontrado: #{path}"
32
- next
33
- end
34
- path
35
- end.compact
36
- end
37
-
38
- def self.choose_input(input_dir)
39
- files = Dir.glob(File.join(input_dir, '*.txt'))
40
- if files.empty?
41
- warn "❌ Não há arquivos .txt no diretório #{input_dir}"; exit 1
42
- end
43
-
44
- puts "Selecione o arquivo de história para processar:"
45
- files.each_with_index { |f, i| puts "#{i+1}. #{File.basename(f)}" }
46
- print "Digite o número correspondente (ou ENTER para todos): "
47
- choice = STDIN.gets.chomp
48
-
49
- return files if choice.empty?
50
- idx = choice.to_i - 1
51
- unless idx.between?(0, files.size - 1)
52
- warn "❌ Escolha inválida."; exit 1
53
- end
54
- [files[idx]]
55
- end
56
-
57
- def self.execute
58
- modo = ENV['BDDGENX_MODE'] || 'static'
59
-
60
- input_dir = 'input'
61
- Dir.mkdir(input_dir) unless Dir.exist?(input_dir)
62
-
63
- arquivos = choose_files(input_dir)
64
- if arquivos.empty?
65
- warn "❌ Nenhum arquivo de história para processar."; exit 1
66
- end
67
-
68
- total = features = steps = ignored = 0
69
- skipped_steps = []
70
- generated_pdfs = []
71
- skipped_pdfs = []
72
-
73
- arquivos.each do |arquivo|
74
- total += 1
75
- puts "\n🔍 Processando: #{arquivo}"
76
-
77
- historia =
78
- if modo == 'gemini'
79
- puts "🤖 Gerando cenários com IA (Gemini)..."
80
- begin
81
- idioma = GeminiCliente.detecta_idioma_arquivo(arquivo)
82
- historia = File.read(arquivo)
83
- GeminiCliente.gerar_cenarios(historia, idioma)
84
- rescue => e
85
- ignored += 1
86
- puts "❌ Falha ao gerar com Gemini: #{e.message}"
87
- next
88
- end
89
- else
90
- historia = Parser.ler_historia(arquivo)
91
- unless Validator.validar(historia)
92
- ignored += 1
93
- puts "❌ História inválida: #{arquivo}"
94
- next
95
- end
96
- historia
97
- end
98
-
99
- historia_limpa = GherkinCleaner.limpar(historia_ia_gerada)
100
- feature_path, feature_content = Generator.gerar_feature(historia_limpa)
101
-
102
- Backup.salvar_versao_antiga(feature_path)
103
- features += 1 if Generator.salvar_feature(feature_path, feature_content)
104
-
105
- if StepsGenerator.gerar_passos(feature_path)
106
- steps += 1
107
- else
108
- skipped_steps << feature_path
109
- end
110
-
111
- FileUtils.mkdir_p('reports')
112
- result = PDFExporter.exportar_todos(only_new: true)
113
- generated_pdfs.concat(result[:generated])
114
- skipped_pdfs.concat(result[:skipped])
115
- end
116
-
117
- puts "\n✅ Processamento concluído"
118
- puts "- Total de histórias: #{total}"
119
- puts "- Features geradas: #{features}"
120
- puts "- Steps gerados: #{steps}"
121
- puts "- Steps ignorados: #{skipped_steps.size}"
122
- puts "- PDFs gerados: #{generated_pdfs.size}"
123
- puts "- PDFs já existentes: #{skipped_pdfs.size}"
124
- puts "- Histórias ignoradas: #{ignored}"
125
- end
126
- end
127
- end
@@ -1,57 +0,0 @@
1
- require 'set'
2
- require 'unicode'
3
-
4
- module Bddgenx
5
- class GherkinCleaner
6
- def self.limpar(texto)
7
- texto = remover_blocos_markdown(texto)
8
- texto = corrigir_language(texto)
9
- texto = corrigir_indentacao(texto)
10
- texto.strip
11
- end
12
-
13
- def self.remover_blocos_markdown(texto)
14
- texto.gsub(/```[a-z]*\n?/i, '').gsub(/```/, '')
15
- end
16
-
17
- def self.corrigir_language(texto)
18
- linhas = texto.lines
19
- primeira_language = linhas.find { |linha| linha.strip.start_with?('# language:') }
20
-
21
- # Remove duplicações de # language
22
- linhas.reject! { |linha| linha.strip.start_with?('# language:') }
23
-
24
- if primeira_language
25
- linhas.unshift(primeira_language.strip + "\n")
26
- else
27
- idioma = detectar_idioma(linhas.join)
28
- linhas.unshift("# language: #{idioma}\n")
29
- end
30
-
31
- linhas.join
32
- end
33
-
34
- def self.detectar_idioma(texto)
35
- return 'pt' if texto =~ /Dado|Quando|Então|E /i
36
- return 'en' if texto =~ /Given|When|Then|And /i
37
- 'pt' # padrão
38
- end
39
-
40
- def self.corrigir_indentacao(texto)
41
- linhas = texto.lines.map do |linha|
42
- if linha.strip.start_with?('Feature', 'Funcionalidade')
43
- linha.strip + "\n"
44
- elsif linha.strip.start_with?('Scenario', 'Cenário', 'Scenario Outline', 'Esquema do Cenário')
45
- " #{linha.strip}\n"
46
- elsif linha.strip.start_with?('Given', 'When', 'Then', 'And', 'Dado', 'Quando', 'Então', 'E')
47
- " #{linha.strip}\n"
48
- elsif linha.strip.start_with?('|')
49
- " #{linha.strip}\n"
50
- else
51
- " #{linha.strip}\n"
52
- end
53
- end
54
- linhas.join
55
- end
56
- end
57
- end
@@ -1,44 +0,0 @@
1
- require 'set'
2
- require 'unicode'
3
-
4
- module Bddgenx
5
- module Utils
6
- class StepCleaner
7
- def self.remover_steps_duplicados(texto, idioma)
8
- keywords = idioma == 'en' ? %w[Given When Then And] : %w[Dado Quando Então E]
9
- seen = Set.new
10
- resultado = []
11
-
12
- texto.each_line do |linha|
13
- if keywords.any? { |kw| linha.strip.start_with?(kw) }
14
- canonical = canonicalize_step(linha, keywords)
15
- unless seen.include?(canonical)
16
- seen.add(canonical)
17
- resultado << linha
18
- end
19
- else
20
- resultado << linha
21
- end
22
- end
23
-
24
- resultado.join
25
- end
26
-
27
- def self.canonicalize_step(linha, keywords)
28
- # Remove keyword
29
- texto = linha.dup.strip
30
- keywords.each do |kw|
31
- texto.sub!(/^#{kw}\s+/i, '')
32
- end
33
-
34
- # Generaliza: substitui textos entre aspas, colchetes e números por <param>
35
- texto.gsub!(/"[^"]*"|<[^>]*>|\b\d+\b/, '<param>')
36
-
37
- # Remove acentos e pontuação, normaliza espaços
38
- texto = Unicode.normalize_KD(texto).gsub(/\p{Mn}/, '')
39
- texto.gsub!(/[^a-zA-Z0-9\s<>]/, '')
40
- texto.downcase.strip.squeeze(" ")
41
- end
42
- end
43
- end
44
- end