-
Notifications
You must be signed in to change notification settings - Fork 0
/
README.md
160 lines (116 loc) · 5.29 KB
/
README.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# Tapdance
[![Build Status](https://img.shields.io/travis/daliwali/tapdance/master.svg?style=flat-square)](https://travis-ci.org/daliwali/tapdance)
[![npm Version](https://img.shields.io/npm/v/tapdance.svg?style=flat-square)](https://www.npmjs.com/package/tapdance)
[![License](https://img.shields.io/npm/l/tapdance.svg?style=flat-square)](https://raw.githubusercontent.com/daliwali/tapdance/master/LICENSE)
Test harness that emits the [Test Anything Protocol (TAP)](https://testanything.org). It runs in Node.js and web browsers, and is written in literate Parenscript (Common Lisp).
```
$ npm install tapdance
```
## Usage
When the test exits, Tapdance will return a `0` exit 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 `runTest` get collected synchronously and run in sequential order in the next tick, which is useful when splitting up asynchronous tests in different files. The `fn` accepts two arguments, `assert` and `comment`.
### assert(value, [message])
Check if a value is truthy or not.
### comment([message])
Output a comment line.
### runTest.assert
This can be overridden to use a different assert function.
## Building
You will need a Common Lisp implementation like [SBCL](http://www.sbcl.org), and `wget`. If installing the developer dependencies didn't work the first time, you may need to `cd node_modules/sigil-cli && make`.
## License
This software is licensed under the [MIT License](//github.com/daliwali/tapdance/blob/master/LICENSE).
## Code
Here's the full source code.
"use strict"
(defvar *assert* (require "assert"))
Check if it's running in Node.js, this determines whether or not to call
Node.js specific APIs.
(defvar *is-node*
(and (not (eq (typeof process) 'undefined))
(eq (typeof (@ process exit)) 'function)
(eq (typeof (@ process next-tick)) 'function)))
Initialize the local state of the program which is read & written later.
(defvar *start-time* ((@ *date now)))
(defvar *stack* (array))
(defvar *count* 0)
(defvar *passing* 0)
A bit a ceremony is needed to kick off the TAP output. This kicks off the
running of tests.
(println "TAP version 13")
(if *is-node*
(progn ((@ process on) 'exit exit)
((@ process next-tick) flush))
(set-timeout flush 0))
Define the main public function, it accepts a test function that takes two
arguments, `assert` and `comment`.
(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))))
This is an implementation detail which runs all of the testing functions in
the order that they are declared.
(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)))))
Upon exiting, print the results as well as some useful information such as
how many tests failed and how long it took.
(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 pretty print errors that happen while running tests.
(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 " ..."))
Internal function to print lines while escaping new lines in the input.
(defun println (str)
((@ console log)
(if str ((@ str replace) #\linefeed "") "")))