bddgenx 0.1.43 → 0.1.44
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 +61 -129
- data/Rakefile +41 -40
- data/VERSION +1 -1
- data/lib/bddgenx/generator.rb +86 -41
- data/lib/bddgenx/runner.rb +52 -21
- data/lib/bddgenx/steps_generator.rb +87 -58
- data/lib/bddgenx/utils/backup.rb +19 -4
- data/lib/bddgenx/utils/fontLoader.rb +24 -6
- data/lib/bddgenx/utils/parser.rb +49 -10
- data/lib/bddgenx/utils/pdf_exporter.rb +51 -18
- data/lib/bddgenx/utils/tracer.rb +31 -3
- data/lib/bddgenx/utils/validator.rb +29 -3
- metadata +29 -2
- data/lib/bddgenx/utils/tipo_param.rb +0 -16
@@ -1,18 +1,34 @@
|
|
1
1
|
# lib/bddgenx/pdf_exporter.rb
|
2
|
+
# encoding: utf-8
|
3
|
+
#
|
4
|
+
# Este arquivo define a classe PDFExporter, responsável por gerar documentos
|
5
|
+
# PDF a partir de arquivos .feature, formatados no estilo pretty Cucumber em
|
6
|
+
# preto e branco.
|
7
|
+
# Utiliza a gem Prawn para renderização de texto e tabelas.
|
8
|
+
|
2
9
|
require 'prawn'
|
3
10
|
require 'prawn/table'
|
4
11
|
require 'fileutils'
|
5
12
|
require_relative 'fontLoader'
|
6
13
|
|
7
|
-
# Suprime aviso de internacionalização para fontes AFM
|
14
|
+
# Suprime aviso de internacionalização para fontes AFM internas
|
8
15
|
Prawn::Fonts::AFM.hide_m17n_warning = true
|
9
16
|
|
10
17
|
module Bddgenx
|
18
|
+
# Gera documentos PDF baseados em arquivos .feature.
|
11
19
|
class PDFExporter
|
12
|
-
# Gera PDFs
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
20
|
+
# Gera PDFs de features, criando um para cada arquivo .feature ou apenas para
|
21
|
+
# o especificado em caminho_feature.
|
22
|
+
#
|
23
|
+
# @param caminho_feature [String, nil]
|
24
|
+
# Caminho para um arquivo .feature específico. Se nil ou vazio, gera para todos
|
25
|
+
# os arquivos em features/*.feature.
|
26
|
+
# @param only_new [Boolean]
|
27
|
+
# Se true, não sobrescreve arquivos PDF já existentes.
|
28
|
+
# @return [Hash<Symbol, Array<String>>]
|
29
|
+
# Retorna um hash com duas chaves:
|
30
|
+
# - :generated => array de caminhos dos PDFs gerados
|
31
|
+
# - :skipped => array de caminhos dos PDFs que foram pulados
|
16
32
|
def self.exportar_todos(caminho_feature: nil, only_new: false)
|
17
33
|
FileUtils.mkdir_p('reports/pdf')
|
18
34
|
features_list = if caminho_feature && !caminho_feature.empty?
|
@@ -21,14 +37,17 @@ module Bddgenx
|
|
21
37
|
Dir.glob('features/*.feature')
|
22
38
|
end
|
23
39
|
|
24
|
-
generated
|
40
|
+
generated = []
|
41
|
+
skipped = []
|
42
|
+
|
25
43
|
features_list.each do |feature|
|
26
44
|
unless File.file?(feature)
|
27
45
|
warn "⚠️ Feature não encontrada: #{feature}"
|
28
46
|
next
|
29
47
|
end
|
30
|
-
|
31
|
-
|
48
|
+
|
49
|
+
nome = File.basename(feature, '.feature')
|
50
|
+
destino = "reports/pdf/#{camel_case(nome)}.pdf"
|
32
51
|
|
33
52
|
if only_new && File.exist?(destino)
|
34
53
|
skipped << destino
|
@@ -42,14 +61,22 @@ module Bddgenx
|
|
42
61
|
{ generated: generated, skipped: skipped }
|
43
62
|
end
|
44
63
|
|
45
|
-
# Converte string para camelCase, removendo caracteres especiais
|
64
|
+
# Converte uma string para formato camelCase, removendo caracteres especiais.
|
65
|
+
#
|
66
|
+
# @param str [String] A string de entrada a ser transformada.
|
67
|
+
# @return [String] String no formato camelCase.
|
46
68
|
def self.camel_case(str)
|
47
69
|
clean = str.gsub(/[^0-9A-Za-z ]/, '')
|
48
70
|
parts = clean.split(/ |_/)
|
49
71
|
([parts.first&.downcase] + (parts[1..] || []).map(&:capitalize)).join
|
50
72
|
end
|
51
73
|
|
52
|
-
# Gera
|
74
|
+
# Gera um documento PDF a partir de um arquivo .feature, aplicando estilos
|
75
|
+
# de cabeçalhos, cenários, passos e tabelas conforme padrões Cucumber.
|
76
|
+
#
|
77
|
+
# @param origem [String] Caminho para o arquivo .feature de origem.
|
78
|
+
# @param destino [String] Caminho onde o PDF será salvo.
|
79
|
+
# @return [void]
|
53
80
|
def self.exportar_arquivo(origem, destino)
|
54
81
|
FileUtils.mkdir_p(File.dirname(destino))
|
55
82
|
conteudo = File.read(origem, encoding: 'utf-8')
|
@@ -59,17 +86,21 @@ module Bddgenx
|
|
59
86
|
pdf.font_size 9
|
60
87
|
|
61
88
|
table_buffer = []
|
89
|
+
|
62
90
|
conteudo.each_line do |linha|
|
63
91
|
text = linha.chomp
|
64
92
|
|
65
|
-
#
|
93
|
+
# Agrega linhas de tabela até o bloco terminar
|
66
94
|
if text =~ /^\s*\|.*\|/i
|
67
|
-
|
95
|
+
# Remove bordas laterais e separa colunas
|
96
|
+
row = text.gsub(/^\s*\||\|\s*$/, '').split('|').map(&:strip)
|
97
|
+
table_buffer << row
|
68
98
|
next
|
69
99
|
elsif table_buffer.any?
|
100
|
+
# Renderiza tabela acumulada
|
70
101
|
pdf.table(table_buffer, header: true, width: pdf.bounds.width) do
|
71
|
-
self.header
|
72
|
-
self.row_colors
|
102
|
+
self.header = true
|
103
|
+
self.row_colors = ['EEEEEE', 'FFFFFF']
|
73
104
|
self.cell_style = { size: 8, font: 'Courier' }
|
74
105
|
end
|
75
106
|
pdf.move_down 4
|
@@ -95,6 +126,7 @@ module Bddgenx
|
|
95
126
|
pdf.text text, size: 8, style: :italic
|
96
127
|
pdf.move_down 4
|
97
128
|
when /^(?:\s*)(Given|When|Then|And|But|Dado|Quando|Então|E|Mas)\b/i
|
129
|
+
# Passo Gherkin: destaca palavra-chave e texto
|
98
130
|
keyword, rest = text.strip.split(' ', 2)
|
99
131
|
pdf.indent(20) do
|
100
132
|
pdf.formatted_text [
|
@@ -110,20 +142,21 @@ module Bddgenx
|
|
110
142
|
end
|
111
143
|
end
|
112
144
|
|
113
|
-
# Renderiza
|
145
|
+
# Renderiza tabela remanescente, se houver
|
114
146
|
if table_buffer.any?
|
115
147
|
pdf.table(table_buffer, header: true, width: pdf.bounds.width) do
|
116
|
-
self.header
|
117
|
-
self.row_colors
|
148
|
+
self.header = true
|
149
|
+
self.row_colors = ['EEEEEE', 'FFFFFF']
|
118
150
|
self.cell_style = { size: 8, font: 'Courier' }
|
119
151
|
end
|
120
152
|
pdf.move_down 4
|
121
153
|
end
|
122
154
|
|
155
|
+
# Numeração de páginas
|
123
156
|
pdf.number_pages 'Página <page> de <total>', align: :right, size: 8
|
124
157
|
end
|
125
158
|
rescue => e
|
126
159
|
warn "❌ Erro ao gerar PDF de #{origem}: #{e.message}"
|
127
160
|
end
|
128
161
|
end
|
129
|
-
end
|
162
|
+
end
|
data/lib/bddgenx/utils/tracer.rb
CHANGED
@@ -1,20 +1,40 @@
|
|
1
|
+
# lib/bddgenx/tracer.rb
|
2
|
+
# encoding: utf-8
|
3
|
+
#
|
4
|
+
# Este arquivo define a classe Tracer, responsável por gerar e manter
|
5
|
+
# informações de rastreabilidade de cenários e passos em um arquivo CSV.
|
6
|
+
# Útil para auditoria e análise de cobertura de cenários gerados.
|
7
|
+
|
1
8
|
require 'csv'
|
2
9
|
require 'fileutils'
|
3
10
|
|
4
11
|
module Bddgenx
|
12
|
+
# Classe para adicionar registros de rastreabilidade a um relatório CSV.
|
5
13
|
class Tracer
|
14
|
+
# Adiciona entradas de rastreabilidade para cada passo de cada grupo
|
15
|
+
# da história em um arquivo CSV localizado em 'reports/output/rastreabilidade.csv'.
|
16
|
+
#
|
17
|
+
# @param historia [Hash]
|
18
|
+
# Objeto de história contendo :quero (título da funcionalidade) e :grupos,
|
19
|
+
# onde cada grupo possui :tipo, :tag, e :passos (Array<String>)
|
20
|
+
# @param nome_arquivo_feature [String]
|
21
|
+
# Nome do arquivo .feature de onde os passos foram gerados
|
22
|
+
# @return [void]
|
6
23
|
def self.adicionar_entrada(historia, nome_arquivo_feature)
|
24
|
+
# Garante existência do diretório de saída
|
7
25
|
FileUtils.mkdir_p('reports/output')
|
8
26
|
arquivo_csv = 'reports/output/rastreabilidade.csv'
|
9
27
|
|
28
|
+
# Cabeçalho padrão do CSV: identifica colunas
|
10
29
|
cabecalho = ['Funcionalidade', 'Tipo', 'Tag', 'Cenário', 'Passo', 'Origem']
|
11
30
|
|
12
31
|
linhas = []
|
13
32
|
|
33
|
+
# Itera sobre grupos de passos para compor linhas de rastreabilidade
|
14
34
|
historia[:grupos].each_with_index do |grupo, idx|
|
15
|
-
tipo
|
16
|
-
tag
|
17
|
-
passos = grupo[:passos]
|
35
|
+
tipo = grupo[:tipo]
|
36
|
+
tag = grupo[:tag]
|
37
|
+
passos = grupo[:passos] || []
|
18
38
|
|
19
39
|
nome_funcionalidade = historia[:quero].gsub(/^Quero\s*/, '').strip
|
20
40
|
nome_cenario = "Cenário #{idx + 1}"
|
@@ -31,10 +51,18 @@ module Bddgenx
|
|
31
51
|
end
|
32
52
|
end
|
33
53
|
|
54
|
+
# Escreve ou anexa as linhas geradas ao CSV
|
34
55
|
escrever_csv(arquivo_csv, cabecalho, linhas)
|
35
56
|
end
|
36
57
|
|
58
|
+
# Escreve ou anexa registros em um arquivo CSV, criando cabeçalho se necessário.
|
59
|
+
#
|
60
|
+
# @param caminho [String] Caminho completo para o arquivo CSV de rastreabilidade
|
61
|
+
# @param cabecalho [Array<String>] Array de títulos das colunas a serem escritos
|
62
|
+
# @param linhas [Array<Array<String>>] Dados a serem gravados no CSV (cada sub-array é uma linha)
|
63
|
+
# @return [void]
|
37
64
|
def self.escrever_csv(caminho, cabecalho, linhas)
|
65
|
+
# Verifica se é um novo arquivo para incluir o cabeçalho
|
38
66
|
novo_arquivo = !File.exist?(caminho)
|
39
67
|
|
40
68
|
CSV.open(caminho, 'a+', col_sep: ';', force_quotes: true) do |csv|
|
@@ -1,32 +1,58 @@
|
|
1
|
+
# lib/bddgenx/validator.rb
|
2
|
+
# encoding: utf-8
|
3
|
+
#
|
4
|
+
# Este arquivo define a classe Validator, responsável por validar a estrutura
|
5
|
+
# de uma história antes de gerar cenários ou arquivos .feature.
|
6
|
+
# Verifica presença de cabeçalho obrigatório e integridade dos grupos de passos.
|
7
|
+
|
1
8
|
module Bddgenx
|
9
|
+
# Valida objetos de história garantindo que possuam campos e blocos corretos.
|
2
10
|
class Validator
|
11
|
+
# Valida o hash de história fornecido.
|
12
|
+
#
|
13
|
+
# Verifica:
|
14
|
+
# - Presença das chaves :como, :quero, :para no cabeçalho
|
15
|
+
# - Presença de pelo menos um grupo em :grupos
|
16
|
+
# - Cada grupo deve ter ao menos passos ou exemplos
|
17
|
+
# - Grupos do tipo "EXAMPLES" devem conter uma tabela de exemplos válida
|
18
|
+
#
|
19
|
+
# @param historia [Hash] Objeto de história com chaves :como, :quero, :para e :grupos
|
20
|
+
# @return [Boolean] Retorna true se a história for válida; caso contrário, false
|
3
21
|
def self.validar(historia)
|
4
22
|
erros = []
|
5
23
|
|
24
|
+
# Verificação do cabeçalho obrigatório
|
6
25
|
unless historia[:como] && historia[:quero] && historia[:para]
|
7
26
|
erros << "❌ Cabeçalho incompleto (Como, Quero, Para obrigatórios)"
|
8
27
|
end
|
9
28
|
|
10
|
-
|
29
|
+
# Verificação de grupos de passos
|
30
|
+
if historia[:grupos].nil? || historia[:grupos].empty?
|
11
31
|
erros << "❌ Nenhum grupo de blocos detectado"
|
12
32
|
else
|
13
33
|
historia[:grupos].each_with_index do |grupo, idx|
|
14
|
-
|
34
|
+
# Cada grupo deve conter passos ou exemplos
|
35
|
+
if (grupo[:passos].nil? || grupo[:passos].empty?) &&
|
36
|
+
(grupo[:exemplos].nil? || grupo[:exemplos].empty?)
|
15
37
|
erros << "❌ Grupo #{idx + 1} do tipo [#{grupo[:tipo]}] está vazio"
|
16
38
|
end
|
17
39
|
|
18
|
-
|
40
|
+
# Validação específica para blocos de exemplos
|
41
|
+
if grupo[:tipo].casecmp('EXAMPLES').zero? &&
|
42
|
+
grupo[:exemplos].none? { |l| l.strip.start_with?('|') }
|
19
43
|
erros << "❌ Grupo de EXAMPLES no bloco #{idx + 1} não contém tabela válida"
|
20
44
|
end
|
21
45
|
end
|
22
46
|
end
|
23
47
|
|
48
|
+
# Exibe erros e retorna false se houver falhas
|
24
49
|
if erros.any?
|
25
50
|
puts "⚠️ Erros encontrados no arquivo:"
|
26
51
|
erros.each { |e| puts " - #{e}" }
|
27
52
|
return false
|
28
53
|
end
|
29
54
|
|
55
|
+
# História válida
|
30
56
|
true
|
31
57
|
end
|
32
58
|
end
|
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.44
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Nascimento
|
@@ -24,6 +24,34 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: prawn-table
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: prawn-svg
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
27
55
|
description: Transforma arquivos .txt com histórias em arquivos .feature, com steps,
|
28
56
|
rastreabilidade e integração com CI/CD.
|
29
57
|
email:
|
@@ -49,7 +77,6 @@ files:
|
|
49
77
|
- lib/bddgenx/utils/fontLoader.rb
|
50
78
|
- lib/bddgenx/utils/parser.rb
|
51
79
|
- lib/bddgenx/utils/pdf_exporter.rb
|
52
|
-
- lib/bddgenx/utils/tipo_param.rb
|
53
80
|
- lib/bddgenx/utils/tracer.rb
|
54
81
|
- lib/bddgenx/utils/validator.rb
|
55
82
|
- lib/bddgenx/version.rb
|
@@ -1,16 +0,0 @@
|
|
1
|
-
|
2
|
-
module Bddgenx
|
3
|
-
# Módulo para inferir o tipo de parâmetro a partir de exemplos
|
4
|
-
class TipoParam
|
5
|
-
# Retorna 'string', 'int', 'float' ou 'boolean'
|
6
|
-
def self.determine_type(nome, estrutura)
|
7
|
-
return 'string' unless estrutura[:cabecalho].include?(nome)
|
8
|
-
idx = estrutura[:cabecalho].index(nome)
|
9
|
-
vals = estrutura[:linhas].map { |l| l[idx] }
|
10
|
-
return 'boolean' if vals.all? { |v| %w[true false].include?(v.downcase) }
|
11
|
-
return 'int' if vals.all? { |v| v.match?(/^\d+$/) }
|
12
|
-
return 'float' if vals.all? { |v| v.match?(/^\d+\.\d+$/) }
|
13
|
-
'string'
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|