bddgenx 0.1.4 β†’ 0.1.6

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: b9c3f8b999f17eb3672fee8875aa4fed6a40f43058e726b4165f79fbf50a4dbb
4
- data.tar.gz: 11f0b8b6383c148fccd3db0477f2d338fbd42872e5f1352f2cd19b6abf3ff14b
3
+ metadata.gz: a47dee0bafd5ea5c1b24fc6786a5023ca79a39701400887aacb4b4478c3399fe
4
+ data.tar.gz: 29ccdabe23627889fb61be3923521bbff9b28c72cf69ff45ff56fe624e63d48a
5
5
  SHA512:
6
- metadata.gz: 721eb213987e3dfe30a1b65c67f02b72fc2653d449705805bc9c9b00f4bf1281536cf157e90249cb32d5da379ee0b3ddf736ed84a26d8b1029198afe9d1f3bac
7
- data.tar.gz: 391db864b436e7fc6f4eab73738663e24cbfbd9a92cc5ac6fba4103b1b6ab7d7012649cdbb9e228c59f43ea9ba73350cb7c79e52095aac3a83ca2acc65153769
6
+ metadata.gz: 15a2ac6231db9033750dec759047f74c13f84b105799d68b40235c386e7f9d790da9cb0a04993b6e3683708990a1890b340b888cf7e0e18d18ba80c58cdb6ea9
7
+ data.tar.gz: 7fa68adb98ca0bd12226d5f1aaa4e83e9ca187a655368d3828e1d33b77aa09f1860185b3c4137ecc14aa3d1901d7fe22c483d63e931c6e689ff01b5d1e66e7a3
data/README.md CHANGED
@@ -7,24 +7,28 @@ Este projeto gera arquivos `.feature` (Gherkin) e `steps.rb` automaticamente a p
7
7
  ## πŸ“‚ Estrutura do Projeto
8
8
  ```txt
9
9
  bdd_generator/
10
- β”œβ”€β”€ doc
10
+ β”œβ”€β”€ doc/
11
11
  β”‚ β”œβ”€β”€ configuracao-padra.md
12
12
  β”‚ └── configuracao-rake.md
13
- β”œβ”€β”€ input/ # Arquivos .txt com histΓ³rias de usuΓ‘rio
14
- β”œβ”€β”€ features/ # Arquivos .feature gerados
15
- β”œβ”€β”€ steps/ # Arquivos com step definitions
13
+ β”œβ”€β”€ input/ # Arquivos .txt com histΓ³rias de usuΓ‘rio
14
+ β”œβ”€β”€ features/ # Arquivos .feature gerados
15
+ β”œβ”€β”€ steps/ # Arquivos com step definitions
16
16
  β”œβ”€β”€ output/
17
- β”‚ └── rastreabilidade.csv
18
- β”œβ”€β”€ backup/ # VersΓ΅es antigas de features sobrescritas
17
+ β”‚ └── rastreabilidade.csv
18
+ β”œβ”€β”€ pdf/ # RelatΓ³rios PDF gerados
19
+ β”œβ”€β”€ backup/ # VersΓ΅es antigas de arquivos sobrescritos
19
20
  β”œβ”€β”€ lib/
20
- β”‚ β”œβ”€β”€ bddgenx
21
- β”‚ β”‚ β”œβ”€β”€ parser.rb
22
- β”‚ β”‚ β”œβ”€β”€ generator.rb
23
- β”‚ β”‚ β”œβ”€β”€ validator.rb
24
- β”‚ β”‚ β”œβ”€β”€ steps_generator.rb
25
- β”‚ β”‚ β”œβ”€β”€ tracer.rb
26
- β”‚ β”‚ └── backup.rb
27
- β”‚ └── bddgenx.rb
21
+ β”‚ β”œβ”€β”€ bddgenx/
22
+ β”‚ β”‚ β”œβ”€β”€ parser.rb
23
+ β”‚ β”‚ β”œβ”€β”€ generator.rb
24
+ β”‚ β”‚ β”œβ”€β”€ steps_generator.rb
25
+ β”‚ β”‚ β”œβ”€β”€ validator.rb
26
+ β”‚ β”‚ β”œβ”€β”€ tracer.rb
27
+ β”‚ β”‚ β”œβ”€β”€ backup.rb
28
+ β”‚ β”‚ β”œβ”€β”€ pdf_exporter.rb
29
+ β”‚ β”‚ └── utils/
30
+ β”‚ β”‚ └── verificador.rb
31
+ β”‚ └── bddgenx.rb # Arquivo principal de execuΓ§Γ£o
28
32
  β”œβ”€β”€ Rakefile
29
33
  └── README.md
30
34
  ```
@@ -63,7 +63,14 @@ module Generator
63
63
  # CenΓ‘rios
64
64
  TIPOS_CENARIO.each do |tipo|
65
65
  passos = historia[:blocos][tipo]
66
- next unless passos&.any?
66
+ passos = passos&.reject { |l| l.strip.empty? } || []
67
+ next if passos.empty?
68
+
69
+ # Ignora geraΓ§Γ£o duplicada se for um SUCCESS parametrizado com EXAMPLES
70
+ if tipo == "SUCCESS" && historia[:blocos]["EXAMPLES"]&.any?
71
+ possui_parametros = passos.any? { |p| p.include?('<') }
72
+ next if possui_parametros
73
+ end
67
74
 
68
75
  nome_teste = TIPOS_ISTQB[tipo] || palavras[:cenario]
69
76
  contexto = passos.first&.gsub(/^(Dado que|Given|Quando|When|EntΓ£o|Then|E|And)/, '')&.strip || "CondiΓ§Γ£o"
@@ -78,32 +85,34 @@ module Generator
78
85
 
79
86
  # Esquema do CenΓ‘rio com Exemplos
80
87
  if historia[:blocos]["EXAMPLES"]&.any?
81
- exemplos_bruto = historia[:blocos]["EXAMPLES"]
82
- cabecalho = exemplos_bruto.first.gsub('|', '').split.map(&:strip)
83
- linhas = exemplos_bruto[1..]
88
+ exemplo_bruto = historia[:blocos]["EXAMPLES"]
89
+ grupos = dividir_examples(exemplo_bruto)
84
90
 
85
- # Procura qualquer cenΓ‘rio que use parΓ’metros
86
- tipo_cenario = historia[:blocos].keys.find { |k| historia[:blocos][k].any? { |l| l.include?('<') } }
87
- passos_exemplo = tipo_cenario ? historia[:blocos][tipo_cenario].select { |l| l.include?('<') } : []
91
+ grupos.each_with_index do |tabela, i|
92
+ cabecalho = tabela.first.gsub('|', '').split.map(&:strip)
93
+ linhas = tabela[1..].map { |linha| linha.split('|').reject(&:empty?).map(&:strip) }
88
94
 
89
- if idioma == 'en'
90
- conteudo += " Scenario Outline: Generated scenario with data\n"
91
- else
92
- conteudo += " Esquema do CenΓ‘rio: Gerado a partir de dados de exemplo\n"
93
- end
95
+ exemplos = { cabecalho: cabecalho, linhas: linhas }
94
96
 
95
- passos_exemplo.each do |passo|
96
- conteudo += " #{passo}\n"
97
- end
97
+ passos_outline = historia[:blocos]["SUCCESS"].select do |linha|
98
+ cabecalho.any? { |coluna| linha.include?("<#{coluna}>") }
99
+ end
98
100
 
99
- conteudo += "\n"
100
- conteudo += idioma == 'en' ? " Examples:\n" : " Exemplos:\n"
101
- conteudo += " #{exemplos_bruto.first}\n"
102
- linhas.each { |linha| conteudo += " #{linha}\n" }
103
- end
101
+ next if passos_outline.empty?
104
102
 
103
+ conteudo += "\n"
104
+ conteudo += idioma == 'en' ? " Scenario Outline: Example #{i + 1}\n" : " Esquema do CenΓ‘rio: Exemplo #{i + 1}\n"
105
105
 
106
+ passos_outline.each do |passo|
107
+ conteudo += " #{passo}\n"
108
+ end
106
109
 
110
+ conteudo += "\n"
111
+ conteudo += idioma == 'en' ? " Examples:\n" : " Exemplos:\n"
112
+ conteudo += " #{tabela.first}\n"
113
+ tabela[1..].each { |linha| conteudo += " #{linha}\n" }
114
+ end
115
+ end
107
116
 
108
117
  [caminho, conteudo]
109
118
  end
@@ -120,4 +129,22 @@ module Generator
120
129
  puts "βœ… Arquivo .feature gerado: #{caminho}"
121
130
  true
122
131
  end
132
+
133
+ def self.dividir_examples(tabela_bruta)
134
+ grupos = []
135
+ grupo_atual = []
136
+
137
+ tabela_bruta.each do |linha|
138
+ if linha.strip =~ /^\|\s*[\w\s]+\|/ && grupo_atual.any? && linha.strip == linha.strip.squeeze(" ")
139
+ grupos << grupo_atual
140
+ grupo_atual = [linha]
141
+ else
142
+ grupo_atual << linha
143
+ end
144
+ end
145
+
146
+ grupos << grupo_atual unless grupo_atual.empty?
147
+ grupos
148
+ end
149
+
123
150
  end
@@ -1,7 +1,7 @@
1
1
  require 'prawn'
2
2
  require 'fileutils'
3
3
 
4
- module Bddgen
4
+ module Bddgenx
5
5
  class PDFExporter
6
6
  def self.exportar_todos
7
7
  FileUtils.mkdir_p('pdf')
@@ -9,8 +9,11 @@ module Bddgen
9
9
  Dir.glob('features/*.feature').each do |feature_file|
10
10
  nome = File.basename(feature_file, '.feature')
11
11
  destino = "pdf/#{nome}.pdf"
12
-
13
12
  exportar_arquivo(feature_file, destino)
13
+ if File.exist?(destino)
14
+ puts "⚠️ PDF jΓ‘ existente: #{destino} β€” pulando geraΓ§Γ£o."
15
+ return
16
+ end
14
17
  puts "πŸ“„ PDF gerado: #{destino}"
15
18
  end
16
19
  end
@@ -19,11 +22,76 @@ module Bddgen
19
22
  conteudo = File.read(origem, encoding: 'utf-8')
20
23
 
21
24
  Prawn::Document.generate(destino) do |pdf|
25
+ # Fontes externas com suporte UTF-8
26
+ pdf.font_families.update(
27
+ "DejaVu" => {
28
+ normal: "fonts/DejaVuSansMono.ttf",
29
+ bold: "fonts/DejaVuSansMono-Bold.ttf",
30
+ italic: "fonts/DejaVuSansMono-Oblique.ttf",
31
+ bold_italic: "fonts/DejaVuSansMono-BoldOblique.ttf"
32
+ }
33
+ )
34
+ pdf.font "DejaVu"
22
35
  pdf.font_size 10
23
- pdf.text "Arquivo: #{File.basename(origem)}", style: :bold, size: 14
36
+
37
+ pdf.text "πŸ“„ #{File.basename(origem)}", style: :normal, size: 14
24
38
  pdf.move_down 10
25
- pdf.text conteudo, size: 10
39
+
40
+ conteudo.each_line do |linha|
41
+ linha = linha.strip
42
+
43
+ case linha
44
+ when /^#/
45
+ pdf.fill_color "888888"
46
+ pdf.text linha, style: :italic, size: 8
47
+ pdf.fill_color "000000"
48
+
49
+ when /^Funcionalidade:|^Feature:/
50
+ pdf.move_down 6
51
+ pdf.text linha, style: :bold, size: 12
52
+ pdf.move_down 4
53
+
54
+ when /^CenΓ‘rio:|^Scenario:|^Esquema do CenΓ‘rio:|^Scenario Outline:/
55
+ pdf.move_down 4
56
+ pdf.text linha, style: :bold
57
+
58
+ when /^@/
59
+ pdf.text linha, style: :italic, color: "555555"
60
+
61
+ when /^(Dado|Quando|EntΓ£o|E|Mas|Given|When|Then|And|But)\b/
62
+ pdf.indent(20) do
63
+ pdf.text linha
64
+ end
65
+
66
+ when /^Exemplos:|^Examples:/
67
+ pdf.move_down 4
68
+ pdf.text linha, style: :bold
69
+
70
+ when /^\|.*\|$/
71
+ pdf.indent(20) do
72
+ pdf.font_size 9
73
+ pdf.text linha, font: "DejaVu"
74
+ pdf.font_size 10
75
+ end
76
+
77
+ when /^\s*$/
78
+ pdf.move_down 4
79
+
80
+ else
81
+ pdf.text linha
82
+ end
83
+ end
84
+
85
+ pdf.move_down 20
86
+ pdf.number_pages "PΓ‘gina <page> de <total>", align: :right, size: 8
26
87
  end
88
+ rescue => e
89
+ puts "❌ Erro ao gerar PDF de #{origem}: #{e.message}"
27
90
  end
28
91
  end
29
92
  end
93
+
94
+ # Execute automaticamente se chamado como script direto
95
+ if __FILE__ == $PROGRAM_NAME
96
+ Bddgen::PDFExporter.exportar_todos
97
+ end
@@ -1,4 +1,5 @@
1
1
  require 'fileutils'
2
+ require_relative 'utils/verificador'
2
3
 
3
4
  module StepsGenerator
4
5
  PADROES = {
@@ -17,25 +18,64 @@ module StepsGenerator
17
18
  conectores = PADROES[idioma]
18
19
  passos_gerados = []
19
20
 
20
- # Detectar parΓ’metros e tipos a partir do bloco EXAMPLES
21
- exemplos = extrair_exemplos(historia[:blocos]["EXAMPLES"])
21
+ grupos_examples = dividir_examples(historia[:blocos]["EXAMPLES"]) if historia[:blocos]["EXAMPLES"]&.any?
22
22
 
23
23
  TIPOS_BLOCOS.each do |tipo|
24
24
  blocos = tipo == "REGRA" || tipo == "RULE" ? historia[:regras] : historia[:blocos][tipo]
25
25
  next unless blocos.is_a?(Array)
26
26
 
27
- blocos.each do |linha|
28
- next if tipo == "EXAMPLES" && linha.strip.start_with?("|") # ignora linhas de tabela
27
+ passos = blocos.dup
29
28
 
29
+ passos.each do |linha|
30
30
  conector = conectores.find { |c| linha.strip.start_with?(c) }
31
31
  next unless conector
32
32
 
33
33
  corpo = linha.strip.sub(/^#{conector}/, '').strip
34
- corpo_parametrizado = substituir_parametros(corpo, exemplos)
35
34
 
36
- chave = { conector: conector, raw: corpo, param: corpo_parametrizado }
37
- passos_gerados << chave unless passos_gerados.any? { |p| p[:param] == corpo_parametrizado }
35
+ # Sanitiza aspas duplas envolvendo parΓ’metros, ex: "<nome>" -> <nome>
36
+ corpo_sanitizado = corpo.gsub(/"(<[^>]+>)"/, '\1')
37
+
38
+ # Verifica se este passo pertence a algum grupo de exemplos
39
+ grupo_exemplo_compat = nil
40
+
41
+ if tipo == "SUCCESS" && grupos_examples
42
+ grupos_examples.each do |grupo|
43
+ cabecalho = grupo.first.gsub('|', '').split.map(&:strip)
44
+ if cabecalho.any? { |col| corpo.include?("<#{col}>") }
45
+ grupo_exemplo_compat = {
46
+ cabecalho: cabecalho,
47
+ linhas: grupo[1..].map { |linha| linha.split('|').reject(&:empty?).map(&:strip) }
48
+ }
49
+ break
50
+ end
51
+ end
52
+ end
53
+
54
+ if grupo_exemplo_compat
55
+ # Substitui cada <param> por {tipo} dinamicamente
56
+ corpo_parametrizado = corpo_sanitizado.gsub(/<([^>]+)>/) do
57
+ nome = $1.strip
58
+ tipo_param = detectar_tipo_param(nome, grupo_exemplo_compat)
59
+ "{#{tipo_param}}"
60
+ end
61
+
62
+ parametros = corpo.scan(/<([^>]+)>/).flatten.map { |p| p.strip.gsub(' ', '_') }
63
+ param_list = parametros.join(', ')
64
+ else
65
+ corpo_parametrizado = corpo
66
+ parametros = []
67
+ param_list = ""
68
+ end
69
+
70
+ passos_gerados << {
71
+ conector: conector,
72
+ raw: corpo,
73
+ param: corpo_parametrizado,
74
+ args: param_list,
75
+ tipo: tipo
76
+ } unless passos_gerados.any? { |p| p[:param] == corpo_parametrizado }
38
77
  end
78
+
39
79
  end
40
80
 
41
81
  if passos_gerados.empty?
@@ -52,24 +92,24 @@ module StepsGenerator
52
92
  conteudo = "#{comentario}\n\n"
53
93
 
54
94
  passos_gerados.each do |passo|
55
- # Extrai nomes dos parΓ’metros entre < >
56
- parametros = passo[:raw].scan(/<([^>]+)>/).flatten.map(&:strip)
57
- param_list = parametros.map { |p| p.gsub(' ', '_') }.join(', ')
58
-
59
- conteudo += <<~STEP
60
- #{passo[:conector]}('#{passo[:param]}') do#{param_list.empty? ? '' : " |#{param_list}|" }
95
+ conteudo += <<~STEP
96
+ #{passo[:conector]}('#{passo[:param]}') do#{passo[:args].empty? ? '' : " |#{passo[:args]}|"}
61
97
  pending '#{idioma == 'en' ? 'Implement step' : 'Implementar passo'}: #{passo[:raw]}'
62
98
  end
63
99
 
64
- STEP
100
+ STEP
65
101
  end
66
102
 
67
103
  FileUtils.mkdir_p("steps")
68
- File.write(caminho, conteudo)
69
- puts "βœ… Step definitions gerados: #{caminho}"
104
+ if Bddgenx::Verificador.gerar_arquivo_se_novo(caminho, conteudo)
105
+ puts "βœ… Step definitions gerados: #{caminho}"
106
+ else
107
+ puts "⏭️ Steps mantidos: #{caminho}"
108
+ end
70
109
  true
71
110
  end
72
111
 
112
+
73
113
  def self.substituir_parametros(texto, exemplos)
74
114
  texto.gsub(/<([^>]+)>/) do |_match|
75
115
  nome = $1.strip
@@ -82,17 +122,33 @@ module StepsGenerator
82
122
  return 'string' unless exemplos && exemplos[:cabecalho].include?(nome_coluna)
83
123
 
84
124
  idx = exemplos[:cabecalho].index(nome_coluna)
85
- valores = exemplos[:linhas].map { |l| l[idx] }
125
+ valores = exemplos[:linhas].map { |linha| linha[idx].to_s.strip }
86
126
 
87
- if valores.all? { |v| v.match?(/^\d+$/) }
88
- 'int'
89
- elsif valores.all? { |v| v.match?(/^\d+\.\d+$/) }
90
- 'float'
91
- else
92
- 'string'
127
+ return 'boolean' if valores.all? { |v| %w[true false].include?(v.downcase) }
128
+ return 'int' if valores.all? { |v| v.match?(/^\d+$/) }
129
+ return 'float' if valores.all? { |v| v.match?(/^\d+\.\d+$/) }
130
+
131
+ 'string'
132
+ end
133
+
134
+ def self.dividir_examples(tabela_bruta)
135
+ grupos = []
136
+ grupo_atual = []
137
+
138
+ tabela_bruta.each do |linha|
139
+ if linha.strip =~ /^\|\s*[\w\s]+\|/ && grupo_atual.any? && linha.strip == linha.strip.squeeze(" ")
140
+ grupos << grupo_atual
141
+ grupo_atual = [linha]
142
+ else
143
+ grupo_atual << linha
144
+ end
93
145
  end
146
+
147
+ grupos << grupo_atual unless grupo_atual.empty?
148
+ grupos
94
149
  end
95
150
 
151
+
96
152
  def self.extrair_exemplos(bloco)
97
153
  return nil unless bloco&.any?
98
154
 
@@ -0,0 +1,20 @@
1
+ require 'fileutils'
2
+
3
+ module Bddgenx
4
+ module Verificador
5
+ # Impede sobrescrita de arquivos existentes
6
+ def self.gerar_arquivo_se_novo(caminho, novo_conteudo)
7
+ if File.exist?(caminho)
8
+ conteudo_atual = File.read(caminho, encoding: 'utf-8').strip
9
+ return false if conteudo_atual == novo_conteudo.strip
10
+
11
+ puts "⚠️ Arquivo jΓ‘ existe: #{caminho} β€” nΓ£o serΓ‘ sobrescrito."
12
+ return false
13
+ end
14
+
15
+ FileUtils.mkdir_p(File.dirname(caminho))
16
+ File.write(caminho, novo_conteudo)
17
+ true
18
+ end
19
+ end
20
+ end
@@ -1,3 +1,3 @@
1
1
  module Bddgenx
2
- VERSION = "0.1.4"
2
+ VERSION = "0.1.6"
3
3
  end
data/lib/bddgenx.rb CHANGED
@@ -1,11 +1,11 @@
1
- require_relative "bddgenx/version"
2
- require_relative "bddgenx/parser"
3
- require_relative "bddgenx/generator"
4
- require_relative "bddgenx/cli"
5
- require_relative "bddgenx/validator"
6
- require_relative "bddgenx/steps_generator"
7
- require_relative "bddgenx/tracer"
8
- require_relative "bddgenx/backup"
1
+ require_relative 'bddgenx/parser'
2
+ require_relative 'bddgenx/validator'
3
+ require_relative 'bddgenx/generator'
4
+ require_relative 'bddgenx/steps_generator'
5
+ require_relative 'bddgenx/tracer'
6
+ require_relative 'bddgenx/backup'
7
+ require_relative 'bddgenx/pdf_exporter' if File.exist?('lib/bddgenx/pdf_exporter.rb')
8
+ require_relative 'bddgenx/utils/verificador' if File.exist?('lib/bddgenx/utils/verificador.rb')
9
9
 
10
10
 
11
11
  cont_total = 0
@@ -33,10 +33,9 @@ arquivos.each do |arquivo_path|
33
33
  cont_features += 1 if Generator.salvar_feature(nome_feature, conteudo_feature)
34
34
  cont_steps += 1 if StepsGenerator.gerar_passos(historia, nome_feature)
35
35
 
36
-
37
- Tracer.adicionar_entrada(historia, nome_feature)
36
+ Tracer.adicionar_entrada(historia, nome_feature)
37
+ Bddgenx::PDFExporter.exportar_todos
38
38
  end
39
-
40
39
  puts "\nβœ… Processamento finalizado. Arquivos gerados em: features/, steps/, output/"
41
40
  puts "πŸ”„ VersΓ΅es antigas salvas em: backup/"
42
41
 
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: 0.1.4
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Nascimento
@@ -31,6 +31,7 @@ files:
31
31
  - lib/bddgenx/pdf_exporter.rb
32
32
  - lib/bddgenx/steps_generator.rb
33
33
  - lib/bddgenx/tracer.rb
34
+ - lib/bddgenx/utils/verificador.rb
34
35
  - lib/bddgenx/validator.rb
35
36
  - lib/bddgenx/version.rb
36
37
  homepage: https://github.com/David-Nascimento/bdd-generation