1 | # tap-parser
|
2 |
|
3 | parse the [test anything protocol](http://testanything.org/)
|
4 |
|
5 | This parser implements [version 14 of the TAP
|
6 | Specification](https://testanything.org/tap-version-14-specification.html).
|
7 |
|
8 | # example
|
9 |
|
10 | ```js
|
11 | // cjs style
|
12 | const { Parser } = require('tap-parser')
|
13 | // or, you can do it this way:
|
14 | // import { Parser } from 'tap-parser'
|
15 | // or this way:
|
16 | // import { Parser } from 'https://unpkg.com/tap-parser@latest/dist/esm/index.js'
|
17 | const p = new Parser(results => console.dir(results))
|
18 | process.stdin.pipe(p)
|
19 | ```
|
20 |
|
21 | given some [TAP](http://testanything.org/)-formatted input:
|
22 |
|
23 | ```
|
24 | $ node test.js
|
25 | TAP version 14
|
26 | # beep
|
27 | ok 1 should be equal
|
28 | ok 2 should be equivalent
|
29 | # boop
|
30 | ok 3 should be equal
|
31 | ok 4 (unnamed assert)
|
32 |
|
33 | 1..4
|
34 | # tests 4
|
35 | # pass 4
|
36 |
|
37 | # ok
|
38 | ```
|
39 |
|
40 | parse the output:
|
41 |
|
42 | ```
|
43 | $ node test.js | node parse.js
|
44 | { ok: true, count: 4, pass: 4, plan: { start: 1, end: 4 } }
|
45 | ```
|
46 |
|
47 | If you have a string, you can also turn it into an array of parse events,
|
48 | and turn such an array back into a TAP string with `Parser.parse()` and
|
49 | `Parser.stringify()`, respectively.
|
50 |
|
51 | ```js
|
52 | // JSON.parse/stringify style
|
53 |
|
54 | // Note that stringifying arbitrary object types isn't supported,
|
55 | // because TAP is a streaming line-based protocol, not an object
|
56 | // serialization notation like JSON or YAML.
|
57 |
|
58 | const { parse, stringify } = require('tap-parser')
|
59 | const fs = require('fs')
|
60 | const tapData = fs.readFileSync('previous-test-output.tap')
|
61 | const result = parse(tapData)
|
62 | console.dir(result)
|
63 | const reEncodedAsTap = stringify(result)
|
64 | console.log(reEncodedAsTap)
|
65 | ```
|
66 |
|
67 | # usage
|
68 |
|
69 | This package also has a `tap-parser` command.
|
70 |
|
71 | ```
|
72 | Usage:
|
73 | tap-parser <options>
|
74 |
|
75 | Parses TAP data from stdin, and outputs the parsed result
|
76 | in the format specified by the options. Default output
|
77 | uses node's `util.inspect()` method.
|
78 |
|
79 | Options:
|
80 |
|
81 | -j [<indent>] | --json[=indent]
|
82 | Output event data as JSON with the specified indentation (default=2)
|
83 |
|
84 | -t | --tap
|
85 | Output data as reconstituted TAP based on parsed results
|
86 |
|
87 | -l | --lines
|
88 | Output each parsed line as it is recognized by the parser
|
89 |
|
90 | -b | --bail
|
91 | Emit a `Bail out!` at the first failed test point encountered
|
92 |
|
93 | -B | --no-bail
|
94 | Do not bail out at the first failed test point encountered
|
95 | (Default)
|
96 |
|
97 | -f | --flat
|
98 | Flatten all assertions to the top level parser
|
99 |
|
100 | -F | --no-flat
|
101 | Do not flatten all assertions to the top level parser
|
102 | (Default)
|
103 |
|
104 | -w | --ignore-all-whitespace
|
105 | Skip over blank lines outside of YAML blocks
|
106 |
|
107 | -o | --omit-version
|
108 | Ignore the `TAP version 13` or `TAP version 14` line at the start of tests
|
109 |
|
110 | --strict
|
111 | Run the parser in strict mode
|
112 |
|
113 | --no-strict
|
114 | Do not run the parser in strict mode
|
115 |
|
116 | -s | --silent
|
117 | Do not print output, just exit success/failure based on TAP stream
|
118 | ```
|
119 |
|
120 | # methods
|
121 |
|
122 | ```js
|
123 | const { Parser } = require('tap-parser')
|
124 | ```
|
125 |
|
126 | ## `const p = new Parser(options, cb)`
|
127 |
|
128 | Return a writable stream `p` that emits parse events.
|
129 |
|
130 | If `cb` is given it will listen for the `'complete'` event.
|
131 |
|
132 | If `options` is given, it may contain the following flags:
|
133 |
|
134 | - `preserveWhitespace` boolean which is `false` by default and will
|
135 | cause the parser to emit `line` events even for lines containing
|
136 | only whitespace. (Whitespace lines in yaml blocks are always
|
137 | emitted, because whitespace is semantically relevant for yaml.)
|
138 |
|
139 | - `strict` boolean which is `false` by default and causes the parser
|
140 | to treat non-TAP input as a failure. Strictness is heritable to
|
141 | child subtests. You can also turn strictness on or off by using the
|
142 | `pragma +strict` line in the TAP data to turn strictness on, or
|
143 | `pragma -strict` to turn strictness off.
|
144 |
|
145 | - `bail` boolean which is `false` by default and will cause the parser
|
146 | to bail out (including emitting a synthetic `Bail out!` line)
|
147 | whenever a failed test point is encountered.
|
148 |
|
149 | - `omitVersion` boolean which is `false` by default and will cause the
|
150 | parser to ignore `TAP version 13` or `TAP version 14` lines.
|
151 | Version lines in subtests cause problems with some parsers, so
|
152 | they are always ignored.
|
153 |
|
154 | - `passes` boolean which is false by default and will add "passes" property
|
155 | for that contains the result of all passed tests
|
156 |
|
157 | The `parent`, `level` and `buffered` options are reserved for internal
|
158 | use.
|
159 |
|
160 | # events
|
161 |
|
162 | ## `p.on('complete', function (results) {})`
|
163 |
|
164 | The `results` object contains a summary of the number of tests
|
165 | skipped, failed, passed, etc., as well as a boolean `ok` member which
|
166 | is true if and only if the planned test were all found, and either
|
167 | "ok" or marked as "TODO".
|
168 |
|
169 | ## `p.on('line', function (line) {})`
|
170 |
|
171 | As each line of input is parsed, a `line` event is emitted.
|
172 |
|
173 | "Synthetic" line events will be emitted to support the `bail`
|
174 | behavior, and to inject `1..0` plan lines in subtests that have no
|
175 | test points. They can be used as a sort of "passthrough stream" to
|
176 | sanitize and filter a TAP stream, with the caveat that, while `line`
|
177 | events will be semantically equivalent to the TAP input, they will not
|
178 | be a perfect replica of the input.
|
179 |
|
180 | ## `p.on('assert', function (assert) {})`
|
181 |
|
182 | Every `/^(not )?ok\b/` line will emit an `'assert'` event.
|
183 |
|
184 | Every `assert` object has these keys:
|
185 |
|
186 | - `assert.ok` - true if the assertion succeeded, false if failed
|
187 | - `assert.id` - the assertion number
|
188 | - `assert.name` - optional short description of the assertion
|
189 |
|
190 | and may also have
|
191 |
|
192 | - `assert.todo` - optional description of why the assertion failure is
|
193 | not a problem. (Boolean `true` if no explaination provided)
|
194 | - `assert.skip` - optional description of why this assertion was
|
195 | skipped (boolean `true` if no explanation provided)
|
196 | - `assert.diag` - a diagnostic object with additional information
|
197 | about the test point.
|
198 |
|
199 | ## `p.on('comment', function (comment) {})`
|
200 |
|
201 | Every `/^# (.+)/` line will emit the string contents of `comment`.
|
202 |
|
203 | ## `p.on('plan', function (plan) {})`
|
204 |
|
205 | Every `/^\d+\.\.\d+/` line emits a `'plan'` event for the test numbers
|
206 | `plan.start` through `plan.end`, inclusive.
|
207 |
|
208 | If the test is [completely
|
209 | skipped](http://podwiki.hexten.net/TAP/TAP.html?page=TAP#Skippingeverything)
|
210 | the result will look like
|
211 |
|
212 | ```
|
213 | { ok: true,
|
214 | count: 0,
|
215 | pass: 0,
|
216 | plan:
|
217 | { start: 1,
|
218 | end: 0,
|
219 | skipAll: true,
|
220 | skipReason: 'This code has no seat belt' } }
|
221 | ```
|
222 |
|
223 | ## `p.on('version', function (version) {})`
|
224 |
|
225 | A `/^TAP version (\d+)/` line emits a `'version'` event with a version
|
226 | number or string.
|
227 |
|
228 | ## `p.on('bailout', function (reason) {})`
|
229 |
|
230 | A `bail out!` line will cause the parser to completely stop doing
|
231 | anything. Child parser bailouts will bail out their parents as well.
|
232 |
|
233 | ## `p.on('child', function (childParser) {})`
|
234 |
|
235 | If a child test set is embedded in the stream like this:
|
236 |
|
237 | ```
|
238 | TAP Version 14
|
239 | 1..2
|
240 | # nesting
|
241 | 1..2
|
242 | ok 1 - true is ok
|
243 | ok 2 - doag is also okay
|
244 | ok 1 - nesting
|
245 | ok 2 - second
|
246 | ```
|
247 |
|
248 | then the child stream will be parsed and events will be raised on the
|
249 | `childParser` object.
|
250 |
|
251 | Since TAP streams with child tests _must_ follow child test sets
|
252 | with a pass or fail assert based on the child test's results, failing
|
253 | to handle child tests should always result in the same end result.
|
254 | However, additional information from those child tests will obviously
|
255 | be lost.
|
256 |
|
257 | See `Subtests` below for more information on which sorts of subtest
|
258 | formats are supported by this parser.
|
259 |
|
260 | ## `p.on('result', function (assert) {})`
|
261 |
|
262 | This is the same as the `assert` event, except that it only emits on the root
|
263 | parser, whenever it or any child parser has an `assert` event that is not
|
264 | merely closing a child test block.
|
265 |
|
266 | ## `p.on('pass', function (assert) {})`
|
267 |
|
268 | ## `p.on('fail', function (assert) {})`
|
269 |
|
270 | ## `p.on('skip', function (assert) {})`
|
271 |
|
272 | ## `p.on('todo', function (assert) {})`
|
273 |
|
274 | Emitted on the root parser object, whenever it or any child parser has an
|
275 | `assert` event that is not merely closing a child test block, if the result is
|
276 | of the appropriate type.
|
277 |
|
278 | ## `p.on('extra', function (extra) {})`
|
279 |
|
280 | All other lines will trigger an `'extra'` event with the line text.
|
281 |
|
282 | # static method: `const results = Parser.parse(string, options = {})`
|
283 |
|
284 | This will return an array of all the events encountered in the parsed TAP
|
285 | string.
|
286 |
|
287 | Any options to the `Parser` constructor may be provided, in addition to the
|
288 | following:
|
289 |
|
290 | - `flat`: Boolean, default false, flatten nested child tests into a single
|
291 | level. Note that this will lose child test information, and will result
|
292 | in a `complete` event that may not match the counts of assertions in the
|
293 | list. This is useful if you are transforming TAP strings for use by a
|
294 | parser that does not support child tests, or just simply don't care about
|
295 | that level of detail. Result `id` values will be coerced to an
|
296 | incrementing numeric values, and a valid `plan` will be generated at the
|
297 | end of the stream.
|
298 |
|
299 | # static method: `const tap = Parser.stringify(results, options = {})`
|
300 |
|
301 | Turn a `results` list of the sort returned by `Parser.parse()` into a TAP
|
302 | string.
|
303 |
|
304 | The following options are supported:
|
305 |
|
306 | - `flat`: Boolean, default false, flatten nested child tests into a single
|
307 | level. Note that this will lose child test information, and will result
|
308 | in a `complete` event that may not match the counts of assertions in the
|
309 | list. This is useful if you are transforming TAP strings for use by a
|
310 | parser that does not support child tests, or just simply don't care about
|
311 | that level of detail. Result `id` values will be coerced to an
|
312 | incrementing numeric values, and a valid `plan` will be generated at the
|
313 | end of the stream.
|
314 |
|
315 | The `indent` and `id` options are used internally, and should not be
|
316 | modified.
|
317 |
|
318 | # install
|
319 |
|
320 | With [npm](https://npmjs.org) do:
|
321 |
|
322 | ```
|
323 | npm install tap-parser
|
324 | ```
|
325 |
|
326 | You can use [browserify](http://browserify.org) to `require('tap-parser')` in
|
327 | the browser.
|
328 |
|
329 | # license
|
330 |
|
331 | MIT
|
332 |
|
333 | # subtests
|
334 |
|
335 | 5 flavors of Subtests are suppored by this parser.
|
336 |
|
337 | 1. Unadorned.
|
338 | Indented TAP data with no comment, followed by a test
|
339 | point at the parent level.
|
340 |
|
341 | ```
|
342 | ok 1
|
343 | 1..1
|
344 | ok 1 - child test
|
345 | 1..1
|
346 | ```
|
347 |
|
348 | 2. Indented comment.
|
349 | An indented `# Subtest: <name>` comment, followed by indented TAP
|
350 | data, and then a not-indented test point with a matching name.
|
351 | The summary test point may have yaml diagnostics.
|
352 |
|
353 | ```
|
354 | # Subtest: child test
|
355 | ok 1
|
356 | 1..1
|
357 | ok 1 - child test
|
358 | 1..1
|
359 | ```
|
360 |
|
361 | 3. Unindented comment.
|
362 | A not-indented `# Subtest: <name>` comment, followed by indented TAP
|
363 | content, followed by a test point with a matching name.
|
364 | The summary test point may have yaml diagnostics.
|
365 |
|
366 | ```
|
367 | # Subtest: child test
|
368 | ok 1
|
369 | 1..1
|
370 | ok 1 - child test
|
371 | 1..1
|
372 | ```
|
373 |
|
374 | 4. Buffered, without diagnostics.
|
375 | A test point line ending in {, followed by indented TAP content, ended
|
376 | with a } to close the block. todo/skip directives may come _either_
|
377 | before or after the `{` character. Yaml diagnostics are not allowed.
|
378 |
|
379 | ```
|
380 | ok 1 - child test {
|
381 | ok 1
|
382 | 1..1
|
383 | }
|
384 | 1..1
|
385 | ```
|
386 |
|
387 | 5. Buffered, with diagnostics.
|
388 | A test point line with yaml diagnostics, followed by `{` alone on a
|
389 | line, indented TAP data, and then a `}`.
|
390 |
|
391 | ```
|
392 | ok 1 - child test
|
393 | ---
|
394 | some: diagnostic
|
395 | data: true
|
396 | ...
|
397 | {
|
398 | ok 1
|
399 | 1..1
|
400 | }
|
401 | ```
|
402 |
|
403 | In all cases, the parsed behavior is identical:
|
404 |
|
405 | 1. The parent emits a `child` event with the `childParser` as an
|
406 | argument.
|
407 | 2. The `childParser` emits a `comment` with `# Subtest: <name>` (or
|
408 | `(anonymous)` for Unadorned subtests.)
|
409 | 3. When the test is over, the closing test point is emitted on parent
|
410 | test.
|
411 |
|
412 | That is, buffered and nonindented/indented comment subtests are parsed
|
413 | as if they are identical input, since their semantics are the same. This
|
414 | simplifies implementation of test harness and reporter modules.
|
415 |
|
416 | Since unadorned subtests have no introduction, a child event is not
|
417 | emitted until the first "relevant tap" line is encountered. This can
|
418 | cause confusion if the test output contains a spurious " 1..2" line
|
419 | or something, but such cases are rare.
|
420 |
|
421 | Similarly, this means that a test point ending in `{` needs to wait to
|
422 | emit _either_ the 'assert' or 'child' events until an indented line is
|
423 | encountered. _Any_ test point with yaml diagnostics needs to wait to
|
424 | see if it will be followed by a `{` indicating a subtest.
|
425 |
|
\ | No newline at end of file |