Module: Bddgenx::Utils

Defined in:
lib/bddgenx/utils/language_helper.rb,
lib/bddgenx/utils/gherkin_cleaner_helper.rb,
lib/bddgenx/utils/remover_steps_duplicados_helper.rb

Constant Summary collapse

GHERKIN_KEYS_PT =

Palavras-chave do Gherkin em Português

%w[Dado Quando Então E Mas].freeze
GHERKIN_KEYS_EN =

Palavras-chave do Gherkin em Inglês

%w[Given When Then And But].freeze
GHERKIN_MAP_PT_EN =

Mapeamento PT → EN

GHERKIN_KEYS_PT.zip(GHERKIN_KEYS_EN).to_h
GHERKIN_MAP_EN_PT =

Mapeamento EN → PT

GHERKIN_KEYS_EN.zip(GHERKIN_KEYS_PT).to_h
ALL_KEYS =

Todas as palavras-chave reconhecidas

GHERKIN_KEYS_PT + GHERKIN_KEYS_EN

Class Method Summary collapse

Class Method Details

.canonicalize_step(linha, keywords) ⇒ Object

Gera uma versão canônica (normalizada) do passo para facilitar a identificação de duplicatas mesmo com variações menores de texto.

Exemplo: Dado “usuario” fez login e Dado <usuario> fez login gerarão o mesmo canonical para evitar repetição.

Passos:

  • Remove a keyword (Given, When, etc) do começo

  • Substitui textos entre aspas, placeholders <> e números por <param>

  • Remove acentuação e pontuação para normalizar

  • Converte para minúsculas e remove espaços extras

Parâmetros:

  • linha: string com o passo completo

  • keywords: array com as keywords para remoção

Retorna uma string normalizada representando o passo



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/bddgenx/utils/remover_steps_duplicados_helper.rb', line 58

def self.canonicalize_step(linha, keywords)
  texto = linha.dup.strip

  # Remove a keyword do início, se existir
  keywords.each do |kw|
    texto.sub!(/^#{kw}\s+/i, '')
  end

  # Substitui textos entre aspas, placeholders e números por <param>
  texto.gsub!(/"[^"]*"|<[^>]*>|\b\d+\b/, '<param>')

  # Remove acentos usando Unicode Normalization Form KD (decompõe caracteres)
  texto = Unicode.normalize_KD(texto).gsub(/\p{Mn}/, '')

  # Remove pontuação, deixando apenas letras, números, espaços e <>
  texto.gsub!(/[^a-zA-Z0-9\s<>]/, '')

  # Converte para minúsculas, remove espaços extras e retorna
  texto.downcase.strip.squeeze(" ")
end

.corrigir_indentacao(texto) ⇒ Object

Corrige a indentação das linhas para seguir o padrão Gherkin:

Feature e Funcionalidade no nível 0 (sem indentação). Scenario e Scenario Outline com 2 espaços. Passos (Given, When, Then, And e equivalentes PT) com 4 espaços. Tabelas (linhas que começam com ‘|’) com 6 espaços.

Outras linhas recebem indentação padrão de 2 espaços.

Essa padronização melhora legibilidade e compatibilidade com parsers Gherkin.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/bddgenx/utils/gherkin_cleaner_helper.rb', line 85

def self.corrigir_indentacao(texto)
  linhas = texto.lines.map do |linha|
    if linha.strip.start_with?('Feature', 'Funcionalidade')
      linha.strip + "\n"
    elsif linha.strip.start_with?('Scenario', 'Cenário', 'Scenario Outline', 'Esquema do Cenário')
      "  #{linha.strip}\n"
    elsif linha.strip.start_with?('Given', 'When', 'Then', 'And', 'Dado', 'Quando', 'Então', 'E')
      "    #{linha.strip}\n"
    elsif linha.strip.start_with?('|')
      "      #{linha.strip}\n"
    else
      "  #{linha.strip}\n"
    end
  end
  linhas.join
end

.corrigir_language(texto) ⇒ Object

Garante que o arquivo contenha exatamente uma linha “# language: xx” no topo.

Passos:

  • Procura a primeira ocorrência da linha de language no texto.

  • Remove todas as outras linhas duplicadas de language.

  • Se encontrar, move essa linha para o início do texto.

  • Se não encontrar, detecta o idioma do texto e adiciona a linha no topo.

Isso evita erros de parsing em ferramentas BDD que exigem essa diretiva.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/bddgenx/utils/gherkin_cleaner_helper.rb', line 42

def self.corrigir_language(texto)
  linhas = texto.lines
  primeira_language = linhas.find { |linha| linha.strip.start_with?('# language:') }

  # Remove todas as linhas duplicadas de language
  linhas.reject! { |linha| linha.strip.start_with?('# language:') }

  if primeira_language
    # Insere a linha de language original no topo
    linhas.unshift(primeira_language.strip + "\n")
  else
    # Se não existir, detecta o idioma e insere padrão
    idioma = detectar_idioma(linhas.join)
    linhas.unshift("# language: #{idioma}\n")
  end

  linhas.join
end

.detecta_idioma_de_texto(texto) ⇒ String

Detecta o idioma a partir de um texto (como conteúdo de arquivo ou string).

Parameters:

  • texto (String)

    O texto onde o idioma será detectado

Returns:

  • (String)

    O idioma detectado (‘pt’ por padrão)



38
39
40
41
42
43
# File 'lib/bddgenx/utils/language_helper.rb', line 38

def self.detecta_idioma_de_texto(texto)
  if texto =~ /^#\s*language:\s*(\w{2})/i
    return $1.downcase
  end
  'pt' # Idioma padrão se o idioma não for detectado
end

.detectar_idioma(texto) ⇒ Object

Detecta o idioma do conteúdo baseado nas palavras-chave Gherkin presentes.

Retorna:

  • ‘pt’ se encontrar palavras-chave em português (Dado, Quando, Então, E).

  • ‘en’ se encontrar palavras-chave em inglês (Given, When, Then, And).

  • ‘pt’ como padrão se não detectar.

Isso ajuda a definir a diretiva # language: corretamente.



69
70
71
72
73
# File 'lib/bddgenx/utils/gherkin_cleaner_helper.rb', line 69

def self.detectar_idioma(texto)
  return 'pt' if texto =~ /Dado|Quando|Então|E /i
  return 'en' if texto =~ /Given|When|Then|And /i
  'pt' # padrão
end

.limpar(texto) ⇒ Object

Método principal para limpar o texto Gherkin recebido. Executa uma sequência de operações para deixar o texto formatado e correto.

Passos:

  • Remove blocos de código markdown (“‘).

  • Garante que exista somente uma linha # language: correta.

  • Corrige a indentação dos blocos Gherkin para o padrão esperado.

  • Remove espaços em branco no início/fim do texto.

Retorna o texto limpo e formatado.



13
14
15
16
17
18
# File 'lib/bddgenx/utils/gherkin_cleaner_helper.rb', line 13

def self.limpar(texto)
  texto = remover_blocos_markdown(texto)
  texto = corrigir_language(texto)
  texto = corrigir_indentacao(texto)
  texto.strip
end

.obter_idioma_do_arquivo(caminho_arquivo) ⇒ String

Extrai o idioma do arquivo .txt, a partir da linha “# language:”.

Parameters:

  • txt_file (String)

    Caminho do arquivo .txt

Returns:

  • (String)

    O idioma extraído ou ‘pt’ como padrão



22
23
24
25
26
27
28
29
30
31
32
# File 'lib/bddgenx/utils/language_helper.rb', line 22

def self.obter_idioma_do_arquivo(caminho_arquivo)
  return 'pt' unless File.exist?(caminho_arquivo)

  File.foreach(caminho_arquivo) do |linha|
    if linha =~ /^#\s*language:\s*(\w{2})/i
      return $1.downcase
    end
  end

  'pt' # idioma padrão caso não encontre
end

.remover_blocos_markdown(texto) ⇒ Object

Remove blocos markdown que usam as crases tripas (“‘). Muitas vezes a IA retorna os textos dentro desses blocos, que precisam ser limpos.

Exemplo: “‘gherkin Feature: Exemplo “`

Essa função remove as linhas contendo as crases, deixando só o conteúdo.



29
30
31
# File 'lib/bddgenx/utils/gherkin_cleaner_helper.rb', line 29

def self.remover_blocos_markdown(texto)
  texto.gsub(/```[a-z]*\n?/i, '').gsub(/```/, '')
end

.remover_steps_duplicados(texto, idioma) ⇒ Object

Remove passos duplicados em um texto de cenários BDD, levando em conta o idioma para identificar as keywords (Given, When, Then, And / Dado, Quando, Então, E)

Parâmetros:

  • texto: string contendo o texto do cenário BDD

  • idioma: ‘en’ para inglês ou qualquer outro para português

Retorna o texto com passos duplicados removidos, preservando a ordem original



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/bddgenx/utils/remover_steps_duplicados_helper.rb', line 11

def self.remover_steps_duplicados(texto, idioma)
  # Define as keywords principais para o idioma
  keywords = idioma == 'en' ? %w[Given When Then And] : %w[Dado Quando Então E]

  # Conjunto para rastrear passos já vistos (versão canônica)
  seen = Set.new
  resultado = []

  # Percorre linha a linha
  texto.each_line do |linha|
    # Verifica se a linha começa com uma das keywords
    if keywords.any? { |kw| linha.strip.start_with?(kw) }
      # Canonicaliza o passo para comparação sem variações irrelevantes
      canonical = Utils::canonicalize_step(linha, keywords)

      # Só adiciona se ainda não viu o passo canônico
      unless seen.include?(canonical)
        seen.add(canonical)
        resultado << linha
      end
    else
      # Linhas que não são passos são adicionadas normalmente
      resultado << linha
    end
  end

  # Retorna o texto reconstruído sem duplicatas
  resultado.join
end