Test harness that emits the Test Anything Protocol (TAP). It runs in Node.js and web browsers, and is written in literate Parenscript (Common Lisp).
$ npm install tapdance
UsageWhen the test exits, Tapdance will return a
0exit code if nothing went wrong, and a non-zero exit code otherwise. There is no need to manually call when a test ends.
tapdance(fn)Run a function which may return a Promise. Calls to
runTestget collected synchronously and run in sequential order in the next tick, which is useful when splitting up asynchronous tests in different files. The
fnaccepts two arguments,
assert(value, message)Check if a value is truthy or not.
comment(message)Output a comment line.
runTest.assertThis can be overridden to use a different assert function.
BuildingYou will need a Common Lisp implementation like SBCL, and
wget. If installing the developer dependencies didn't work the first time, you may need to
cd node_modules/sigil-cli && make.
LicenseThis software is licensed under the MIT License.
CodeHere's the full source code.
Check if it's running in Node.js, this determines whether or not to call Node.js specific APIs.
"use strict" (defvar *assert* (require "assert"))
Initialize the local state of the program which is read & written later.
(defvar *is-node* (and (not (eq (typeof process) 'undefined)) (eq (typeof (@ process exit)) 'function) (eq (typeof (@ process next-tick)) 'function)))
A bit a ceremony is needed to kick off the TAP output. This kicks off the running of tests.
(defvar *start-time* ((@ *date now))) (defvar *stack* (array)) (defvar *count* 0) (defvar *passing* 0)
Define the main public function, it accepts a test function that takes two arguments,
(println "TAP version 13") (if *is-node* (progn ((@ process on) 'exit exit) ((@ process next-tick) flush)) (set-timeout flush 0))
This is an implementation detail which runs all of the testing functions in the order that they are declared.
(setf (@ module exports) run-test) (setf (@ run-test assert) *assert*) (defun run-test (fn) (defun assert (exp message) (incf *count*) (try (progn ((@ run-test assert) exp) (incf *passing*) (println (+ "ok " *count* " " message))) (:catch (error) (println (+ "not ok " *count* " " message)) (show-error error)))) (defun comment (message) (println (+ "# " message))) ((@ *stack* push) (lambda () (fn assert comment))))
Upon exiting, print the results as well as some useful information such as how many tests failed and how long it took.
(defun flush () ((@ ((@ *stack* reduce) (lambda (chain fn) ((@ ((@ chain then) fn) catch) (lambda (error) (progn (incf *count*) (println (+ "not ok " *count* " " (@ error message))) (show-error error))))) ((@ *promise resolve))) then) (lambda () (if *is-node* ((@ process exit)) (exit)))))
Internal function to pretty print errors that happen while running tests.
(defun exit () (if *is-node* (setf (@ process exit-code) (if (and *count* (eq *count* *passing*)) 0 1))) (if (not *count*) (progn (incf *count*) (println "not ok 1 no tests found"))) (println (+ "1.." *count*)) (println) (if (eq *count* *passing*) (println "# all tests passed") (let ((failing (- *count* *passing*))) (println (+ "# " failing " test" (if (> failing 1) "s" "") " failed")))) (println (+ "# test finished in " (- ((@ *date now)) *start-time*) " ms")) (println))
Internal function to print lines while escaping new lines in the input.
(defun show-error (error) (println " ---") (println (+ " name: " (@ error name))) (if ((@ (regex "\\n") test) (@ error message)) (progn (println " message:") ((@ ((@ error message split) #\linefeed) map) (lambda (line) (println (+ " - " line))))) (progn (println (+ " message: " (@ error message))) (if (@ error stack) (progn (println " stack:") ((@ ((@ error stack split) #\linefeed) for-each) (lambda (line) (setf line ((@ line trim))) (if (not (eq ((@ line index-of) (@ error name)) 0)) (println (+ " - " line))))))))) (println " ..."))
(defun println (str) ((@ console log) (if str ((@ str replace) #\linefeed "") "")))