On Emacs and Node.js Testing Workflow

Making it easy to lint your JavaScript is a good step, but it's pretty helpful to be able to execute your tests from within your editor. I'm on a certifiable emacs kick lately, so here's how I got it to work!

I wanted to get testing to feel similar to compilation, where I can hit a key and see the result. Basing testing on compilation gives me a good starting point: a simple command that tries to execute a shell command and show you some output. The output could be to a temporary buffer, and be easily dismissed with q when done. Likewise, it would be helpful to save the current buffer if it has been changed. With all of this in mind, I was able to find a great article to help me along that path.

(defvar testing-command "echo No testing command configured!")

(defun execute-tests ()
  (interactive)
  (when (and (buffer-modified-p)
             (y-or-n-p (format "Save file %s? " (buffer-file-name))))
    (save-buffer))
  (with-output-to-temp-buffer "*automated tests*"
    (shell-command (concat "echo Running: " testing-command) "*automated tests*")
    (shell-command testing-command
                   "*automated tests*")
    (pop-to-buffer "*automated tests*")))

(global-set-key (kbd "M-<f5>") 'execute-tests)

The testing-command variable in the above code is what lets this behave similarly to the compilation functionality. By default it will just tell you that there is no configuration, and we'll need a way to provide it one.

The execute-tests function does all of the things I talked about before, saving the file, executing the testing command, and popping it to a temporary buffer to show you the output. Lastly, it gets a global key binding: M-<f5>.

With a generalized way to execute tests in place, it is pretty easy to wire in testing for any mode, just like I did with wiring JavaScript linters into the compilation functionality:

(defun test-javascript ()
  (concat "npm run test --prefix " (file-at-git-root "")))

(add-hook 'js2-mode-hook
          (lambda ()
            (set (make-local-variable 'testing-command)
                 (test-javascript))))

This code uses the same recursive (locate-dominating-file default-directory ".git") approach to figure out what directory the testing should be pointed to. Here, I'm saying that it will try to tell npm to execute the test script from directory at the root of the current repository.

Lastly, the whole thing gets wired into js2-mode-hook so that any JavaScript file I am working with will have a way to execute mocha tests.

It would be pretty awesome to find a way to navigate to failed tests, similar to how the compilation part works. But we'll see how far this gets me!