bddgenx 0.1.3 β 0.1.5
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/README.md +18 -14
- data/lib/bddgenx/generator.rb +47 -20
- data/lib/bddgenx/pdf_exporter.rb +72 -4
- data/lib/bddgenx/steps_generator.rb +79 -23
- data/lib/bddgenx/utils/verificador.rb +20 -0
- data/lib/bddgenx/version.rb +1 -1
- data/lib/bddgenx.rb +5 -5
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0928e8e8fd94c33980e4d963e2a82cfcb3cb01e4b64b0cf6ba7dd6f57d96de1
|
4
|
+
data.tar.gz: f3b570d8cd2a0fdb941929a14108b507900a4b8e00ae31610712a1b856600528
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df527606b3354db9cccf5261e29b8123a53d95d491741049d0024614799bf96c34f9ca2c012ed7c9ca7bb898ae416c5199f081ff131226d8486cde2dee840edc
|
7
|
+
data.tar.gz: 8da5e40dcdfa48aea97d9e76b1b057964fff881354a12f7c8fb58e8db244e2cb174bcefd5cef0db705e0fc8d9bc1155c78e5b316f75e723a57aaf30e7799caab
|
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/
|
14
|
-
βββ features/
|
15
|
-
βββ steps/
|
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
|
-
β
|
18
|
-
βββ
|
17
|
+
β βββ rastreabilidade.csv
|
18
|
+
βββ pdf/ # RelatΓ³rios PDF gerados
|
19
|
+
βββ backup/ # VersΓ΅es antigas de arquivos sobrescritos
|
19
20
|
βββ lib/
|
20
|
-
β
|
21
|
-
β
|
22
|
-
β
|
23
|
-
β
|
24
|
-
β
|
25
|
-
β
|
26
|
-
β
|
27
|
-
β
|
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
|
```
|
data/lib/bddgenx/generator.rb
CHANGED
@@ -63,7 +63,14 @@ module Generator
|
|
63
63
|
# CenΓ‘rios
|
64
64
|
TIPOS_CENARIO.each do |tipo|
|
65
65
|
passos = historia[:blocos][tipo]
|
66
|
-
|
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
|
-
|
82
|
-
|
83
|
-
linhas = exemplos_bruto[1..]
|
88
|
+
exemplo_bruto = historia[:blocos]["EXAMPLES"]
|
89
|
+
grupos = dividir_examples(exemplo_bruto)
|
84
90
|
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
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
|
-
|
96
|
-
|
97
|
-
|
97
|
+
passos_outline = historia[:blocos]["SUCCESS"].select do |linha|
|
98
|
+
cabecalho.any? { |coluna| linha.include?("<#{coluna}>") }
|
99
|
+
end
|
98
100
|
|
99
|
-
|
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
|
data/lib/bddgenx/pdf_exporter.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'prawn'
|
2
2
|
require 'fileutils'
|
3
3
|
|
4
|
-
module
|
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
|
-
|
36
|
+
|
37
|
+
pdf.text "π #{File.basename(origem)}", style: :normal, size: 14
|
24
38
|
pdf.move_down 10
|
25
|
-
|
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
|
-
|
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.
|
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
|
-
|
37
|
-
|
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
|
-
|
56
|
-
|
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
|
-
|
100
|
+
STEP
|
65
101
|
end
|
66
102
|
|
67
103
|
FileUtils.mkdir_p("steps")
|
68
|
-
|
69
|
-
|
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 { |
|
125
|
+
valores = exemplos[:linhas].map { |linha| linha[idx].to_s.strip }
|
86
126
|
|
87
|
-
if valores.all? { |v|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
data/lib/bddgenx/version.rb
CHANGED
data/lib/bddgenx.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
require_relative "bddgenx/version"
|
2
|
-
require_relative "bddgenx/cli"
|
3
2
|
require_relative "bddgenx/parser"
|
4
|
-
require_relative "bddgenx/validator"
|
5
3
|
require_relative "bddgenx/generator"
|
4
|
+
require_relative "bddgenx/cli"
|
5
|
+
require_relative "bddgenx/validator"
|
6
6
|
require_relative "bddgenx/steps_generator"
|
7
7
|
require_relative "bddgenx/tracer"
|
8
8
|
require_relative "bddgenx/backup"
|
9
9
|
require_relative 'bddgenx/pdf_exporter'
|
10
10
|
|
11
|
+
|
11
12
|
cont_total = 0
|
12
13
|
cont_features = 0
|
13
14
|
cont_steps = 0
|
@@ -33,10 +34,9 @@ arquivos.each do |arquivo_path|
|
|
33
34
|
cont_features += 1 if Generator.salvar_feature(nome_feature, conteudo_feature)
|
34
35
|
cont_steps += 1 if StepsGenerator.gerar_passos(historia, nome_feature)
|
35
36
|
|
36
|
-
|
37
|
-
|
37
|
+
Tracer.adicionar_entrada(historia, nome_feature)
|
38
|
+
Bddgenx::PDFExporter.exportar_todos
|
38
39
|
end
|
39
|
-
|
40
40
|
puts "\nβ
Processamento finalizado. Arquivos gerados em: features/, steps/, output/"
|
41
41
|
puts "π VersΓ΅es antigas salvas em: backup/"
|
42
42
|
|
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
|
+
version: 0.1.5
|
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
|