bddgenx 0.1.43 → 0.1.45
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 +54 -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 +30 -3
- data/lib/bddgenx/utils/tipo_param.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd8038688d2588671b6f69f67410b65bef4883160f91c1cee24e92d8b33ef959
|
4
|
+
data.tar.gz: 02277bee62f245aa0419ab0e9adf40986d09db1fe497bed70519e9fd196104b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac24edf1de0092569cd84ce8809b8bf0d4ede815070410870baf0e3cc0eec01f62c52a034479c4952c2206cbb3e235db2fbb1a3e5ece42bba76ded1b4a1573b4
|
7
|
+
data.tar.gz: 6ad785d7ecc283e2931c705c0b1e5d95b7cd627e84a58a79d8760f3b2d70d1a10a62e3b8a7350f9058f3e3c67aa2eaccddf083092ec85f9f6f95a7bf45db4d29
|
data/README.md
CHANGED
@@ -1,142 +1,82 @@
|
|
1
|
-
#
|
1
|
+
# Gerador Automático de BDD em Ruby
|
2
2
|
[](https://badge.fury.io/rb/bddgenx)
|
3
3
|
|
4
|
-
|
4
|
+
## Visão Geral
|
5
5
|
|
6
|
-
|
6
|
+
Ferramenta Ruby para gerar automaticamente arquivos Gherkin (`.feature`) e definições de passos (`steps.rb`) a partir de histórias em texto. Atende aos padrões ISTQB, suporta parametrização com blocos de exemplos e fornece relatórios de QA (rastreabilidade, backups e PDF).
|
7
7
|
|
8
|
-
##
|
9
|
-
```txt
|
10
|
-
bddgenx/
|
11
|
-
├── bin/bddgenx # CLI executável
|
12
|
-
├── input/ # .txt de histórias de usuário
|
13
|
-
├── features/ # .feature geradas
|
14
|
-
├── features/<nome>/steps/ # step definitions por feature (se existir)
|
15
|
-
├── reports/ # todos os artefatos de saída
|
16
|
-
│ ├── backup/ # versões antigas de .feature
|
17
|
-
│ ├── output/ # rastreabilidade.csv
|
18
|
-
│ └── pdf/ # relatórios camelCase
|
19
|
-
├── lib/
|
20
|
-
│ ├── bddgenx/
|
21
|
-
│ │ ├── parser.rb
|
22
|
-
│ │ ├── validator.rb
|
23
|
-
│ │ ├── generator.rb
|
24
|
-
│ │ ├── steps_generator.rb
|
25
|
-
│ │ ├── tracer.rb
|
26
|
-
│ │ ├── backup.rb
|
27
|
-
│ │ └── pdf_exporter.rb
|
28
|
-
│ └── bddgenx.rb # Runner que orquestra tudo
|
29
|
-
├── Gemfile
|
30
|
-
├── bddgenx.gemspec
|
31
|
-
├── Rakefile
|
32
|
-
├── VERSION
|
33
|
-
├── bump_version.sh
|
34
|
-
└── README.md
|
35
|
-
```
|
36
|
-
## ▶️ Como Executar
|
8
|
+
## Instalação
|
37
9
|
|
38
|
-
|
39
|
-
- Ruby 3.x
|
40
|
-
- `bundle install` (caso use gems como `prawn` ou `jira-ruby`)
|
10
|
+
Adicione ao seu `Gemfile`:
|
41
11
|
|
42
|
-
|
12
|
+
```ruby
|
13
|
+
gem 'bddgenx'
|
14
|
+
```
|
15
|
+
|
16
|
+
Execute:
|
43
17
|
|
44
18
|
```bash
|
45
|
-
|
19
|
+
bundle install
|
46
20
|
```
|
47
21
|
|
48
|
-
|
22
|
+
Ou instale diretamente:
|
23
|
+
|
49
24
|
```bash
|
50
|
-
|
25
|
+
gem install bddgenx
|
51
26
|
```
|
52
27
|
|
53
|
-
|
54
|
-
```txt
|
55
|
-
# language: pt
|
56
|
-
Como um usuario do sistema
|
57
|
-
Quero fazer login com sucesso
|
58
|
-
Para acessar minha conta
|
59
|
-
|
60
|
-
[FAILURE]
|
61
|
-
Quando preencho email e senha válidos
|
62
|
-
Então vejo a tela inicial
|
28
|
+
## Uso no Código
|
63
29
|
|
64
|
-
|
65
|
-
|
66
|
-
Então recebo "<resultado>"
|
30
|
+
```ruby
|
31
|
+
require 'bddgenx'
|
67
32
|
|
68
|
-
|
69
|
-
|
70
|
-
| user@site.com | 123456 | login realizado |
|
71
|
-
| errado@site.com | senha | credenciais inválidas |
|
33
|
+
# Gera todas as features e steps a partir dos .txt em input/
|
34
|
+
Bddgenx::Runner.execute
|
72
35
|
```
|
73
|
-
✅ Blocos Suportados
|
74
|
-
[CONTEXT] – contexto comum
|
75
36
|
|
76
|
-
|
37
|
+
## Tarefa Rake (opcional)
|
77
38
|
|
78
|
-
|
39
|
+
Em um projeto Rails ou Ruby com Rake, adicione ao `Rakefile`:
|
79
40
|
|
80
|
-
|
41
|
+
```ruby
|
42
|
+
require 'bddgenx'
|
43
|
+
require 'rake'
|
81
44
|
|
82
|
-
|
45
|
+
namespace :bddgenx do
|
46
|
+
desc 'Gera .feature e steps a partir de histórias em input/'
|
47
|
+
task :gerar do
|
48
|
+
Bddgenx::Runner.execute
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
83
52
|
|
84
|
-
|
53
|
+
## Formato do Arquivo de Entrada (`.txt`)
|
85
54
|
|
86
|
-
|
87
|
-
```gherkin
|
55
|
+
```txt
|
88
56
|
# language: pt
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
Quero adicionar produtos ao carrinho
|
93
|
-
Para finalizar minha compra com praticidade
|
57
|
+
Como um usuário do sistema
|
58
|
+
Quero fazer login
|
59
|
+
Para acessar minha conta
|
94
60
|
|
95
|
-
|
96
|
-
|
61
|
+
[SUCCESS]
|
62
|
+
Quando preencho <email> e <senha>
|
63
|
+
Então vejo a tela inicial
|
97
64
|
|
98
|
-
|
99
|
-
|
100
|
-
|
65
|
+
[EXAMPLES]
|
66
|
+
| email | senha | resultado |
|
67
|
+
| user@site.com | 123456 | login realizado |
|
68
|
+
| errado@site.com | senha | credenciais inválidas |
|
69
|
+
```
|
101
70
|
|
102
|
-
|
103
|
-
Cenário: Teste Positivo - adiciono um produto ao carrinho - ele aparece na listagem do carrinho
|
104
|
-
Quando adiciono um produto ao carrinho
|
105
|
-
Então ele aparece na listagem do carrinho
|
71
|
+
## Artefatos de QA
|
106
72
|
|
107
|
-
|
108
|
-
|
109
|
-
Então vejo o total <total esperado>
|
73
|
+
* **Backup**: versões antigas de `.feature` em `reports/backup` com timestamp
|
74
|
+
* **PDF**: exporta features em P/B para `reports/pdf` via `PDFExporter`
|
110
75
|
|
111
|
-
|
112
|
-
| produto | quantidade | total esperado |
|
113
|
-
| Camiseta Azul | 2 | 100 |
|
114
|
-
| Tênis Branco | 1 | 250 |
|
115
|
-
```
|
76
|
+
## Integração CI/CD
|
116
77
|
|
117
|
-
|
118
|
-
```ruby
|
119
|
-
Quando('adiciono "<produto>" com quantidade <quantidade>') do |produto, quantidade|
|
120
|
-
pending 'Implementar passo: adiciono "<produto>" com quantidade <quantidade>'
|
121
|
-
end
|
78
|
+
Exemplo de GitHub Actions:
|
122
79
|
|
123
|
-
Então('vejo o total <total esperado>') do |total_esperado|
|
124
|
-
pending 'Implementar passo: vejo o total <total esperado>'
|
125
|
-
end
|
126
|
-
```
|
127
|
-
🧾 Rastreabilidade
|
128
|
-
- Gera automaticamente um CSV em output/rastreabilidade.csv com:
|
129
|
-
- Nome do cenário
|
130
|
-
- Tipo (SUCCESS, FAILURE, etc.)
|
131
|
-
- Caminho do .feature
|
132
|
-
- Origem do .txt
|
133
|
-
|
134
|
-
🔄 Backup
|
135
|
-
Toda vez que um .feature existente for sobrescrito, a versão anterior é salva em:
|
136
|
-
```
|
137
|
-
backup/
|
138
|
-
```
|
139
|
-
✅ Execução em CI/CD (GitHub Actions)
|
140
80
|
```yaml
|
141
81
|
jobs:
|
142
82
|
gerar_bdd:
|
@@ -145,26 +85,11 @@ jobs:
|
|
145
85
|
- uses: actions/checkout@v3
|
146
86
|
- uses: ruby/setup-ruby@v1
|
147
87
|
with:
|
148
|
-
ruby-version: '3.
|
149
|
-
- run:
|
88
|
+
ruby-version: '3.x'
|
89
|
+
- run: bundle install
|
90
|
+
- run: bundle exec ruby -e "require 'bddgenx'; Bddgenx::Runner.execute(only_new: true)"
|
150
91
|
```
|
151
|
-
⚙️ Alternativa: Usar via Rake
|
152
|
-
|
153
|
-
Você também pode executar a gem bddgenx com Rake, como em projetos Rails:
|
154
|
-
|
155
|
-
Crie um arquivo Rakefile:
|
156
|
-
```ruby
|
157
|
-
require "bddgenx"
|
158
|
-
require "rake"
|
159
92
|
|
160
|
-
|
161
|
-
desc "Gera arquivos .feature e steps a partir de arquivos .txt"
|
162
|
-
task :gerar do
|
163
|
-
Bddgenx::Runner.execute
|
164
|
-
end
|
165
|
-
end
|
166
|
-
```
|
93
|
+
## Licença
|
167
94
|
|
168
|
-
|
169
|
-
David Nascimento – Projeto de automação BDD com Ruby – 2025
|
170
|
-
---
|
95
|
+
MIT © 2025 David Nascimento
|
data/Rakefile
CHANGED
@@ -1,54 +1,55 @@
|
|
1
|
+
# Rakefile
|
2
|
+
# Tarefas para geração automática de BDD e relatórios com bddgenx
|
3
|
+
|
1
4
|
require 'rake'
|
2
|
-
|
3
|
-
|
4
|
-
require_relative 'lib/bddgenx/integrations/jira'
|
5
|
-
require_relative 'lib/bddgenx/integrations/testlink'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'bddgenx'
|
6
7
|
|
7
8
|
namespace :bddgenx do
|
9
|
+
desc 'Gerar arquivos .feature, steps, rastreabilidade e backups'
|
10
|
+
task :gerar, [:only_new, :input_dir] do |t, args|
|
11
|
+
# Parâmetros: only_new (true/false), input_dir (pasta com .txt)
|
12
|
+
args.with_defaults(only_new: 'false', input_dir: 'input')
|
13
|
+
only_new = args.only_new == 'true'
|
14
|
+
input_dir = args.input_dir
|
15
|
+
|
16
|
+
files = FileList["#{input_dir}/*.txt"]
|
17
|
+
if files.empty?
|
18
|
+
puts "❌ Nenhum arquivo encontrado em \#{input_dir}"
|
19
|
+
next
|
20
|
+
end
|
21
|
+
|
22
|
+
files.each do |file|
|
23
|
+
puts "🔄 Processando história: \#{file}"
|
24
|
+
historia = Bddgenx::Parser.ler_historia(file)
|
25
|
+
next unless Bddgenx::Validator.validar(historia)
|
26
|
+
|
27
|
+
feature_path, conteudo = Bddgenx::Generator.gerar_feature(historia)
|
28
|
+
|
29
|
+
Bddgenx::Backup.salvar_versao_antiga(feature_path)
|
30
|
+
Bddgenx::Generator.salvar_feature(feature_path, conteudo)
|
8
31
|
|
9
|
-
|
10
|
-
|
11
|
-
arquivos = Bddgenx::CLI.todos_arquivos('input')
|
12
|
-
arquivos.each do |arquivo|
|
13
|
-
puts "🔁 Executando: ruby bddgen.rb"
|
14
|
-
system("ruby lib/bddgen.rb")
|
32
|
+
Bddgenx::StepsGenerator.gerar_passos(feature_path)
|
33
|
+
Bddgenx::Tracer.adicionar_entrada(historia, feature_path)
|
15
34
|
end
|
16
35
|
|
36
|
+
puts "✅ Geração concluída#{' (apenas novos)' if only_new}!"
|
17
37
|
end
|
18
38
|
|
19
|
-
desc
|
39
|
+
desc 'Exportar todos os arquivos .feature para PDF'
|
20
40
|
task :pdf do
|
21
|
-
puts
|
22
|
-
Bddgenx::PDFExporter.exportar_todos
|
41
|
+
puts '📦 Exportando features para PDF...'
|
42
|
+
result = Bddgenx::PDFExporter.exportar_todos
|
43
|
+
puts " Gerados: \#{result[:generated].size}, Pulados: \#{result[:skipped].size}"
|
23
44
|
end
|
24
45
|
|
25
|
-
desc
|
26
|
-
task :
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
site: ENV['JIRA_SITE'],
|
31
|
-
project_key: ENV['JIRA_PROJECT']
|
32
|
-
)
|
33
|
-
|
34
|
-
Dir.glob("features/*.feature") do |arquivo|
|
35
|
-
conteudo = File.read(arquivo)
|
36
|
-
titulo = File.basename(arquivo, ".feature").gsub('_', ' ').capitalize
|
37
|
-
jira.enviar_cenario(titulo, conteudo)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
desc "Enviar todos os cenários para o TestLink"
|
42
|
-
task :testlink do
|
43
|
-
testlink = Bddgen::Integrations::TestLink.new(
|
44
|
-
ENV['TESTLINK_TOKEN'],
|
45
|
-
ENV['TESTLINK_URL']
|
46
|
-
)
|
47
|
-
|
48
|
-
Dir.glob("features/*.feature") do |arquivo|
|
49
|
-
conteudo = File.readlines(arquivo).reject { |l| l.strip.start_with?("#") || l.strip.empty? }
|
50
|
-
titulo = File.basename(arquivo, ".feature").gsub('_', ' ').capitalize
|
51
|
-
testlink.criar_caso_teste(ENV['TESTLINK_PLAN_ID'].to_i, titulo, conteudo)
|
46
|
+
desc 'Remover diretórios gerados (reports/ e features/)'
|
47
|
+
task :clean do
|
48
|
+
%w[reports features].each do |dir|
|
49
|
+
FileUtils.rm_rf(dir)
|
50
|
+
puts "🗑️ Diretório removido: \#{dir}"
|
52
51
|
end
|
53
52
|
end
|
54
53
|
end
|
54
|
+
|
55
|
+
task default: 'bddgenx:gerar'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.45
|
data/lib/bddgenx/generator.rb
CHANGED
@@ -1,37 +1,82 @@
|
|
1
|
+
# lib/bddgenx/generator.rb
|
2
|
+
# encoding: utf-8
|
3
|
+
#
|
4
|
+
# Este arquivo define a classe Generator, responsável por gerar arquivos
|
5
|
+
# .feature a partir de um hash de história ou de um arquivo de história em texto.
|
6
|
+
# Suporta Gherkin em Português e Inglês, inclusão de tags, cenários simples
|
7
|
+
# e esquemas de cenário com exemplos.
|
8
|
+
|
1
9
|
require 'fileutils'
|
2
|
-
require_relative 'utils/tipo_param'
|
3
10
|
|
4
11
|
module Bddgenx
|
12
|
+
# Gera cenários e arquivos .feature baseados em histórias e grupos de passos.
|
5
13
|
class Generator
|
14
|
+
# Palavras-chave Gherkin em Português
|
15
|
+
# @return [Array<String>]
|
6
16
|
GHERKIN_KEYS_PT = %w[Dado Quando Então E Mas].freeze
|
17
|
+
|
18
|
+
# Palavras-chave Gherkin em Inglês
|
19
|
+
# @return [Array<String>]
|
7
20
|
GHERKIN_KEYS_EN = %w[Given When Then And But].freeze
|
21
|
+
|
22
|
+
# Mapeamento PT -> EN
|
23
|
+
# @return [Hash{String=>String}]
|
8
24
|
GHERKIN_MAP_PT_EN = GHERKIN_KEYS_PT.zip(GHERKIN_KEYS_EN).to_h
|
25
|
+
|
26
|
+
# Mapeamento EN -> PT
|
27
|
+
# @return [Hash{String=>String}]
|
9
28
|
GHERKIN_MAP_EN_PT = GHERKIN_KEYS_EN.zip(GHERKIN_KEYS_PT).to_h
|
29
|
+
|
30
|
+
# Conjunto de todas as palavras-chave suportadas (PT + EN)
|
31
|
+
# @return [Array<String>]
|
10
32
|
ALL_KEYS = GHERKIN_KEYS_PT + GHERKIN_KEYS_EN
|
11
33
|
|
12
|
-
#
|
34
|
+
# Seleciona apenas as linhas que representam exemplos do cenário
|
35
|
+
#
|
36
|
+
# @param raw [Array<String>] Lista de linhas brutas do bloco de exemplos
|
37
|
+
# @return [Array<String>] Linhas que começam com '|' representando a tabela de exemplos
|
13
38
|
def self.dividir_examples(raw)
|
14
39
|
raw.select { |l| l.strip.start_with?('|') }
|
15
40
|
end
|
16
41
|
|
17
|
-
# Gera .feature a partir de hash ou
|
18
|
-
#
|
42
|
+
# Gera conteúdo de um arquivo .feature a partir de um hash de história ou caminho para arquivo
|
43
|
+
#
|
44
|
+
# @param input [Hash, String]
|
45
|
+
# Objeto de história com chaves :idioma, :quero, :como, :para, :grupos
|
46
|
+
# Ou caminho para um arquivo de história que será lido via Parser.ler_historia
|
47
|
+
# @param override_path [String, nil]
|
48
|
+
# Caminho de saída alternativo para o arquivo .feature
|
49
|
+
# @raise [ArgumentError] Se Parser.ler_historia lançar erro ao ler arquivo
|
50
|
+
# @return [Array(String, String)] Array com caminho e conteúdo gerado
|
19
51
|
def self.gerar_feature(input, override_path = nil)
|
20
52
|
historia = input.is_a?(String) ? Parser.ler_historia(input) : input
|
21
|
-
idioma
|
22
|
-
|
23
|
-
|
24
|
-
|
53
|
+
idioma = historia[:idioma] || 'pt'
|
54
|
+
|
55
|
+
# Geração do nome base do arquivo
|
56
|
+
nome_base = historia[:quero]
|
57
|
+
.gsub(/[^a-z0-9]/i, '_')
|
58
|
+
.downcase
|
59
|
+
.split('_', 3)
|
60
|
+
.first(3)
|
61
|
+
.join('_')
|
62
|
+
|
63
|
+
caminho = if override_path.is_a?(String)
|
64
|
+
override_path
|
65
|
+
else
|
66
|
+
"features/#{nome_base}.feature"
|
67
|
+
end
|
25
68
|
|
69
|
+
# Definição das palavras-chave Gherkin conforme idioma
|
26
70
|
palavras = {
|
27
|
-
feature:
|
28
|
-
contexto:
|
29
|
-
cenario:
|
30
|
-
esquema:
|
31
|
-
exemplos:
|
32
|
-
regra:
|
71
|
+
feature: idioma == 'en' ? 'Feature' : 'Funcionalidade',
|
72
|
+
contexto: idioma == 'en' ? 'Background' : 'Contexto',
|
73
|
+
cenario: idioma == 'en' ? 'Scenario' : 'Cenário',
|
74
|
+
esquema: idioma == 'en' ? 'Scenario Outline' : 'Esquema do Cenário',
|
75
|
+
exemplos: idioma == 'en' ? 'Examples' : 'Exemplos',
|
76
|
+
regra: idioma == 'en' ? 'Rule' : 'Regra'
|
33
77
|
}
|
34
78
|
|
79
|
+
# Cabeçalho do arquivo .feature
|
35
80
|
conteudo = <<~GHK
|
36
81
|
# language: #{idioma}
|
37
82
|
#{palavras[:feature]}: #{historia[:quero].sub(/^Quero\s*/i,'')}
|
@@ -41,56 +86,51 @@ module Bddgenx
|
|
41
86
|
|
42
87
|
GHK
|
43
88
|
|
44
|
-
pt_map
|
45
|
-
en_map
|
46
|
-
detect
|
89
|
+
pt_map = GHERKIN_MAP_PT_EN
|
90
|
+
en_map = GHERKIN_MAP_EN_PT
|
91
|
+
detect = ALL_KEYS
|
47
92
|
|
48
|
-
historia[:grupos].
|
49
|
-
passos
|
93
|
+
historia[:grupos].each do |grupo|
|
94
|
+
passos = grupo[:passos] || []
|
50
95
|
exemplos = grupo[:exemplos] || []
|
51
96
|
next if passos.empty?
|
52
97
|
|
53
|
-
|
98
|
+
# Linha de tags para o grupo
|
99
|
+
tag_line = ["@#{grupo[:tipo].downcase}",
|
100
|
+
("@#{grupo[:tag]}" if grupo[:tag])]
|
101
|
+
.compact.join(' ')
|
54
102
|
|
55
103
|
if exemplos.any?
|
56
|
-
#
|
104
|
+
# Cenário com Esquema
|
57
105
|
conteudo << " #{tag_line}\n"
|
58
106
|
conteudo << " #{palavras[:esquema]}: #{historia[:quero]}\n"
|
59
107
|
|
60
|
-
#
|
108
|
+
# Passos do cenário
|
61
109
|
passos.each do |p|
|
62
|
-
|
63
|
-
parts = line.split(' ', 2)
|
64
|
-
# match connector case-insensitive
|
110
|
+
parts = p.strip.split(' ', 2)
|
65
111
|
con_in = detect.find { |k| k.casecmp(parts[0]) == 0 } || parts[0]
|
66
|
-
text
|
67
|
-
out_conn = idioma=='en' ? pt_map[con_in] || con_in : en_map[con_in] || con_in
|
68
|
-
conteudo << " #{out_conn} #{text}
|
69
|
-
"
|
112
|
+
text = parts[1] || ''
|
113
|
+
out_conn = idioma == 'en' ? pt_map[con_in] || con_in : en_map[con_in] || con_in
|
114
|
+
conteudo << " #{out_conn} #{text}\n"
|
70
115
|
end
|
71
116
|
|
72
|
-
#
|
73
|
-
# Renderiza o bloco de Examples exatamente como veio no TXT
|
117
|
+
# Bloco de exemplos original
|
74
118
|
conteudo << "\n #{palavras[:exemplos]}:\n"
|
75
119
|
exemplos.select { |l| l.strip.start_with?('|') }.each do |line|
|
76
|
-
# Remove aspas apenas das células, mantendo todas as colunas e valores originais
|
77
120
|
cleaned = line.strip.gsub(/^"|"$/, '')
|
78
121
|
conteudo << " #{cleaned}\n"
|
79
122
|
end
|
80
123
|
conteudo << "\n"
|
81
124
|
else
|
82
|
-
#
|
125
|
+
# Cenário simples
|
83
126
|
conteudo << " #{tag_line}\n"
|
84
127
|
conteudo << " #{palavras[:cenario]}: #{grupo[:tipo].capitalize}\n"
|
85
128
|
passos.each do |p|
|
86
|
-
|
87
|
-
parts = line.split(' ', 2)
|
88
|
-
# match connector case-insensitive
|
129
|
+
parts = p.strip.split(' ', 2)
|
89
130
|
con_in = detect.find { |k| k.casecmp(parts[0]) == 0 } || parts[0]
|
90
|
-
text
|
91
|
-
out_conn = idioma=='en' ? pt_map[con_in] || con_in : en_map[con_in] || con_in
|
92
|
-
conteudo << " #{out_conn} #{text}
|
93
|
-
"
|
131
|
+
text = parts[1] || ''
|
132
|
+
out_conn = idioma == 'en' ? pt_map[con_in] || con_in : en_map[con_in] || con_in
|
133
|
+
conteudo << " #{out_conn} #{text}\n"
|
94
134
|
end
|
95
135
|
conteudo << "\n"
|
96
136
|
end
|
@@ -99,10 +139,15 @@ module Bddgenx
|
|
99
139
|
[caminho, conteudo]
|
100
140
|
end
|
101
141
|
|
142
|
+
# Salva o conteúdo gerado em arquivo .feature no disco
|
143
|
+
#
|
144
|
+
# @param caminho [String] Caminho completo para salvar o arquivo
|
145
|
+
# @param conteudo [String] Conteúdo do arquivo .feature
|
146
|
+
# @return [nil]
|
102
147
|
def self.salvar_feature(caminho, conteudo)
|
103
148
|
FileUtils.mkdir_p(File.dirname(caminho))
|
104
149
|
File.write(caminho, conteudo)
|
105
150
|
puts "✅ Arquivo .feature gerado: #{caminho}"
|
106
151
|
end
|
107
152
|
end
|
108
|
-
end
|
153
|
+
end
|
data/lib/bddgenx/runner.rb
CHANGED
@@ -1,4 +1,10 @@
|
|
1
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
|
+
|
2
8
|
require 'fileutils'
|
3
9
|
require_relative 'utils/parser'
|
4
10
|
require_relative 'generator'
|
@@ -8,17 +14,23 @@ require_relative 'utils/validator'
|
|
8
14
|
require_relative 'utils/backup'
|
9
15
|
|
10
16
|
module Bddgenx
|
17
|
+
# Ponto de entrada da gem: coordena todo o processo de geração BDD.
|
11
18
|
class Runner
|
12
|
-
#
|
19
|
+
# Seleciona arquivos de entrada para processamento.
|
20
|
+
# Se houver argumentos em ARGV, usa-os como nomes de arquivos .txt;
|
21
|
+
# caso contrário, exibe prompt interativo para escolha.
|
22
|
+
#
|
23
|
+
# @param input_dir [String] Diretório onde estão os arquivos .txt de histórias
|
24
|
+
# @return [Array<String>] Lista de caminhos para os arquivos a serem processados
|
13
25
|
def self.choose_files(input_dir)
|
14
|
-
|
15
|
-
selecionar_arquivos_txt(input_dir)
|
16
|
-
else
|
17
|
-
choose_input(input_dir)
|
18
|
-
end
|
26
|
+
ARGV.any? ? selecionar_arquivos_txt(input_dir) : choose_input(input_dir)
|
19
27
|
end
|
20
28
|
|
21
|
-
#
|
29
|
+
# Mapeia ARGV para paths de arquivos .txt em input_dir.
|
30
|
+
# Adiciona extensão '.txt' se necessário e filtra arquivos inexistentes.
|
31
|
+
#
|
32
|
+
# @param input_dir [String] Diretório de entrada
|
33
|
+
# @return [Array<String>] Caminhos válidos para processamento
|
22
34
|
def self.selecionar_arquivos_txt(input_dir)
|
23
35
|
ARGV.map do |arg|
|
24
36
|
nome = arg.end_with?('.txt') ? arg : "#{arg}.txt"
|
@@ -31,26 +43,42 @@ module Bddgenx
|
|
31
43
|
end.compact
|
32
44
|
end
|
33
45
|
|
34
|
-
#
|
46
|
+
# Exibe prompt interativo para o usuário escolher qual arquivo processar
|
47
|
+
# entre todos os .txt disponíveis em input_dir.
|
48
|
+
#
|
49
|
+
# @param input_dir [String] Diretório de entrada
|
50
|
+
# @exit [1] Se nenhum arquivo for encontrado ou escolha inválida
|
51
|
+
# @return [Array<String>] Um único arquivo escolhido ou todos se ENTER
|
35
52
|
def self.choose_input(input_dir)
|
36
53
|
files = Dir.glob(File.join(input_dir, '*.txt'))
|
37
54
|
if files.empty?
|
38
|
-
warn "❌ Não há arquivos .txt no diretório #{input_dir}"
|
39
|
-
exit 1
|
55
|
+
warn "❌ Não há arquivos .txt no diretório #{input_dir}"; exit 1
|
40
56
|
end
|
41
57
|
|
42
58
|
puts "Selecione o arquivo de história para processar:"
|
43
|
-
files.each_with_index { |f,i| puts "#{i+1}. #{File.basename(f)}" }
|
59
|
+
files.each_with_index { |f, i| puts "#{i+1}. #{File.basename(f)}" }
|
44
60
|
print "Digite o número correspondente (ou ENTER para todos): "
|
45
61
|
choice = STDIN.gets.chomp
|
62
|
+
|
46
63
|
return files if choice.empty?
|
47
64
|
idx = choice.to_i - 1
|
48
|
-
unless idx.between?(0, files.size-1)
|
65
|
+
unless idx.between?(0, files.size - 1)
|
49
66
|
warn "❌ Escolha inválida."; exit 1
|
50
67
|
end
|
51
68
|
[files[idx]]
|
52
69
|
end
|
53
70
|
|
71
|
+
# Executa todo o fluxo de geração BDD.
|
72
|
+
# - Cria pasta 'input' se não existir
|
73
|
+
# - Seleciona arquivos de histórias
|
74
|
+
# - Para cada arquivo:
|
75
|
+
# - Lê e valida a história
|
76
|
+
# - Gera arquivo .feature e salva backup da versão anterior
|
77
|
+
# - Gera definitions de steps
|
78
|
+
# - Exporta PDFs novos via PDFExporter
|
79
|
+
# - Exibe resumo final com estatísticas
|
80
|
+
#
|
81
|
+
# @return [void]
|
54
82
|
def self.execute
|
55
83
|
input_dir = 'input'
|
56
84
|
Dir.mkdir(input_dir) unless Dir.exist?(input_dir)
|
@@ -60,6 +88,7 @@ module Bddgenx
|
|
60
88
|
warn "❌ Nenhum arquivo de história para processar."; exit 1
|
61
89
|
end
|
62
90
|
|
91
|
+
# Inicializa contadores
|
63
92
|
total = features = steps = ignored = 0
|
64
93
|
skipped_steps = []
|
65
94
|
generated_pdfs = []
|
@@ -71,29 +100,31 @@ module Bddgenx
|
|
71
100
|
|
72
101
|
historia = Parser.ler_historia(arquivo)
|
73
102
|
unless Validator.validar(historia)
|
74
|
-
ignored += 1
|
103
|
+
ignored += 1
|
104
|
+
puts "❌ História inválida: #{arquivo}"
|
105
|
+
next
|
75
106
|
end
|
76
107
|
|
77
|
-
#
|
108
|
+
# Geração de feature
|
78
109
|
feature_path, feature_content = Generator.gerar_feature(historia)
|
79
110
|
Backup.salvar_versao_antiga(feature_path)
|
80
111
|
features += 1 if Generator.salvar_feature(feature_path, feature_content)
|
81
112
|
|
82
|
-
#
|
113
|
+
# Geração de steps
|
83
114
|
if StepsGenerator.gerar_passos(feature_path)
|
84
115
|
steps += 1
|
85
116
|
else
|
86
117
|
skipped_steps << feature_path
|
87
118
|
end
|
88
119
|
|
89
|
-
#
|
120
|
+
# Exportação de PDF (apenas novos)
|
90
121
|
FileUtils.mkdir_p('reports')
|
91
|
-
|
92
|
-
generated_pdfs.concat(
|
93
|
-
skipped_pdfs.concat(
|
122
|
+
result = PDFExporter.exportar_todos(only_new: true)
|
123
|
+
generated_pdfs.concat(result[:generated])
|
124
|
+
skipped_pdfs.concat(result[:skipped])
|
94
125
|
end
|
95
126
|
|
96
|
-
#
|
127
|
+
# Exibe relatório final
|
97
128
|
puts "\n✅ Processamento concluído"
|
98
129
|
puts "- Total de histórias: #{total}"
|
99
130
|
puts "- Features geradas: #{features}"
|
@@ -101,7 +132,7 @@ module Bddgenx
|
|
101
132
|
puts "- Steps ignorados: #{skipped_steps.size}"
|
102
133
|
puts "- PDFs gerados: #{generated_pdfs.size}"
|
103
134
|
puts "- PDFs já existentes: #{skipped_pdfs.size}"
|
104
|
-
puts "-
|
135
|
+
puts "- Histórias ignoradas: #{ignored}"
|
105
136
|
end
|
106
137
|
end
|
107
138
|
end
|