/ tests / run_features.lua
run_features.lua
 1  #!/usr/bin/env lua
 2  -- Run all .feature files with byfeature-style output (Feature:/Scenario:/steps with ✓/✗ and colors).
 3  -- Usage: from repo root, lua tests/run_features.lua
 4  -- Step definitions must be required before running (they register with the runner).
 5  
 6  local repo_root = os.getenv('DOTFILES_HOME') or (os.getenv('HOME') and (os.getenv('HOME') .. '/.dotfiles')) or '.'
 7  package.path = package.path .. ';' .. repo_root .. '/?.lua;' .. repo_root .. '/tests/?.lua'
 8  
 9  local runner = require('tests.runner.feature_runner')
10  local lfs = require('lfs')
11  local colors = require('tests.runner.colors')
12  
13  -- Load step definitions (they register with runner when required)
14  local step_dir = repo_root .. '/tests/step_definitions'
15  for name in lfs.dir(step_dir) do
16    if name:match('%.lua$') then
17      local mod = name:gsub('%.lua$', '')
18      pcall(require, 'tests.step_definitions.' .. mod)
19    end
20  end
21  
22  local features_dir = repo_root .. '/tests/features'
23  local total_passed, total_failed = 0, 0
24  local all_errors = {}
25  
26  -- Skip feature files that have no step definitions yet (e.g. nvim_keymaps)
27  local skip_features = { ['nvim_keymaps.feature'] = true }
28  
29  print(colors.blue('=== RUN   Lua/Features'))
30  print('')
31  
32  for name in lfs.dir(features_dir) do
33    if name:match('%.feature$') and not skip_features[name] then
34      local path = features_dir .. '/' .. name
35      local p, f, errs = runner.run_feature_file_byfeature(path, {})
36      total_passed = total_passed + p
37      total_failed = total_failed + f
38      for _, e in ipairs(errs or {}) do table.insert(all_errors, e) end
39    end
40  end
41  
42  -- Summary
43  print('')
44  if #all_errors > 0 then
45    print('--- ' .. colors.red('Failed steps:') .. '')
46    print('')
47    for _, e in ipairs(all_errors) do
48      print('  ' .. e)
49    end
50    print('')
51  end
52  
53  print('--- Summary')
54  local total = total_passed + total_failed
55  if total == 0 then
56    print('No scenarios')
57  else
58    local parts = {}
59    if total_passed > 0 then table.insert(parts, colors.green(total_passed .. ' passed')) end
60    if total_failed > 0 then table.insert(parts, colors.red(total_failed .. ' failed')) end
61    print(total .. ' scenarios (' .. table.concat(parts, ', ') .. ')')
62  end
63  
64  if total_failed > 0 then
65    os.exit(1)
66  end
67  os.exit(0)