I have been trying to set up a shortcut for jumping between a JavaScript implementation and test. I have particular requirements that are making examples I see harder to apply.

I currently work with many very small javascript projects. The structure of the project normally has its tests in a mirrored directory structure (like so: )

lib/foo.js lib/someDir/bar.js test/fooSpec.js test/someDir/barSpec.js

So the spec files are not in the same directory, as the implementation

I first was looking at examples for ff-find-other-file, but they were for situations when the files were located in the same directories. I'm looking to configure it a little more dynamically (walking up the directory tree until lib, then adding a similar path with test replacing lib)

I also know projectile has a function for doing just this. I use projectile in my setup, but when attempting to use its built in method (C-c p t) I get the message "Project type not supported!". Interestingly enough, projectile is able to list all my test files via (C-c p T). Is there way of configuring projectile for javascript, so that it knows how to find a corresponding spec file?

I also saw the project toggle test, but am looking for a more automatic solution. It requires a configuration for each project to enable toggling between tests. This is rather inconvenient to me because when working with Node/JS I bounce between many projects (it's very common to divide an application into many very small projects with their own tests). I also often look at external dependencies, and then their specs. More often then not they have this structure. Having to configure toggle test each time I need to use this feature makes it not very useful.

  • Projectile has the command projectile-find-other-file that works the same as ff-find-other-file, except that it lists all possible other files in your project. You can custom with projectile-other-file-alist variable, – Tu Do Jan 12 '15 at 19:10
  • FWIW, the ff-search-directories variable provides limited functionality for finding files in other directories (albeit not arbitrary directory walking). See stackoverflow.com/questions/23660637/smarter-ff-find-other-file – phils Jan 12 '15 at 20:54

I know nothing about projectile, but if I needed something like what you described, I'd possibly go with something like this:

(defun jump-to-test ()
   (cl-loop with parts = (reverse (split-string (buffer-file-name) "/"))
            with fname = (file-name-sans-extension (cl-first parts))
            for (name . rest) on (cl-rest parts)
            until (string-equal name "lib")
            collect name into names
            finally (cl-return
                     (mapconcat 'identity
                                (nconc (reverse rest)
                                       (list "test")
                                       (reverse names)
                                       (list (format "%sSpec.js" fname))) "/")))))
  • thanks, this is nearly exactly what i'm looking for. And is educating me on elisp. I made a gist containing the opposite side (jump to implementation from test). How about a means to determine which path structure I'm currently in, so that I can call the appropriate function using the same keystroke – Mike McFarland Jan 12 '15 at 20:01
  • 1
    never-mind, updated the gist containing the function. Thanks for the help. You've made my day! – Mike McFarland Jan 12 '15 at 20:18
  • @MikeMcFarland ha! Glad I could help :) – wvxvw Jan 12 '15 at 23:07

Tweaked wvxvw's answer to provide it as a toggle between files. It also doesn't open it in a new window.

(defun js-jump-to (current from to format-name)
   (cl-loop with parts = (reverse current)
            with fname = (file-name-sans-extension (cl-first parts))
            for (name . rest) on (cl-rest parts)
            until (string-equal name from)
            collect name into names
            finally (cl-return
                     (mapconcat 'identity
                                (nconc (reverse rest)
                                       (list to)
                                       (reverse names)
                                       (list (funcall format-name fname) )) "/" )))))

(defun js-format-impl-name (fname)
  (format "%s.js" (replace-regexp-in-string "Spec" "" fname)))

(defun js-format-test-name (fname)
  (format "%sSpec.js" fname))

(defun js-jump-to-implementation-or-test ()
  (let ((current (split-string (buffer-file-name) "/")))
     ((member "test" current) (js-jump-to current "test" "lib" 'js-format-impl-name))
     ((member "lib" current)  (js-jump-to current "lib" "test" 'js-format-test-name))
     (t (error "not within a test or lib directory"))

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.