2 Comments

Scripting Vim to Help Run Ember Tests

Years of using the testing framework RSpec has spoiled me. Because you can pass a filename and line number to the RSpec command and it will run only that test, it’s trivial to set up a Vim key binding that runs the test under the cursor. For example,

nnoremap <buffer> <LocalLeader>a :exe "!rspec ".expand("%").":".line(".")<CR>

Running the test I’m currently looking at only requires muscle memory. I don’t have to examine and re-type any filenames or line numbers.

Ember-CLI, unlike RSpec, can’t run a single test given only a filename and line number. Instead it requires the name of the test. So if your test in CoffeeScript looks like this,


test 'does not break when given invalid arguments', ->
  # ...assertions...

You can run only that test with the command


ember test --filter 'does not break when given invalid arguments'

Since re-typing a test’s name is inconvenient, slow, and error-prone, it’s better to copy and paste the test’s name to the ember test command.

This still isn’t as convenient as a single key binding, but a bit of Vimscript can get us there. Here’s a function that matches a CoffeeScript test declaration, grabs the name, and runs the ember test command.

function! RunTest()
  let test = matchlist(getline(line('.')), '^test \(.*\), ->$')
  exe "!ember test --filter ".substitute(test[1], '"', '\\"', 'g'))
endfunction

If you set up a key binding to call this function, you can invoke it from a test declaration, and it will suspend Vim to run that test.

This isn’t good enough, however. RSpec doesn’t just accept the line numbers where test definitions start, but also any line number within a test definition. Let’s extend the above function to work similarly.

function! RunTest()
  let lineNumber = line('.')
 
  while lineNumber > 0
    let test = matchlist(getline(lineNumber), '^test \(.*\), ->$')
    if len(test) > 0
      exe "!ember test --filter ".substitute(test[1], '"', '\\"', 'g'))
    else
      let lineNumber -= 1
    endif
  endwhile
endfunction

Instead of just looking on the current line for a test declaration, our revised function searches line-by-line up the file until it finds one, giving us the same habitual workflow as running RSpec tests.

If you have any tips for scripting improvements to your workflow, I’m happy to hear them.