UNPKG

12.2 kBMarkdownView Raw
1# tap-parser
2
3parse the [test anything protocol](http://testanything.org/)
4
5This parser implements [version 14 of the TAP
6Specification](https://testanything.org/tap-version-14-specification.html).
7
8# example
9
10```js
11// cjs style
12const { 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'
17const p = new Parser(results => console.dir(results))
18process.stdin.pipe(p)
19```
20
21given some [TAP](http://testanything.org/)-formatted input:
22
23```
24$ node test.js
25TAP version 14
26# beep
27ok 1 should be equal
28ok 2 should be equivalent
29# boop
30ok 3 should be equal
31ok 4 (unnamed assert)
32
331..4
34# tests 4
35# pass 4
36
37# ok
38```
39
40parse 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
47If you have a string, you can also turn it into an array of parse events,
48and 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
58const { parse, stringify } = require('tap-parser')
59const fs = require('fs')
60const tapData = fs.readFileSync('previous-test-output.tap')
61const result = parse(tapData)
62console.dir(result)
63const reEncodedAsTap = stringify(result)
64console.log(reEncodedAsTap)
65```
66
67# usage
68
69This package also has a `tap-parser` command.
70
71```
72Usage:
73 tap-parser <options>
74
75Parses TAP data from stdin, and outputs the parsed result
76in the format specified by the options. Default output
77uses node's `util.inspect()` method.
78
79Options:
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
123const { Parser } = require('tap-parser')
124```
125
126## `const p = new Parser(options, cb)`
127
128Return a writable stream `p` that emits parse events.
129
130If `cb` is given it will listen for the `'complete'` event.
131
132If `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
157The `parent`, `level` and `buffered` options are reserved for internal
158use.
159
160# events
161
162## `p.on('complete', function (results) {})`
163
164The `results` object contains a summary of the number of tests
165skipped, failed, passed, etc., as well as a boolean `ok` member which
166is 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
171As each line of input is parsed, a `line` event is emitted.
172
173"Synthetic" line events will be emitted to support the `bail`
174behavior, and to inject `1..0` plan lines in subtests that have no
175test points. They can be used as a sort of "passthrough stream" to
176sanitize and filter a TAP stream, with the caveat that, while `line`
177events will be semantically equivalent to the TAP input, they will not
178be a perfect replica of the input.
179
180## `p.on('assert', function (assert) {})`
181
182Every `/^(not )?ok\b/` line will emit an `'assert'` event.
183
184Every `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
190and 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
201Every `/^# (.+)/` line will emit the string contents of `comment`.
202
203## `p.on('plan', function (plan) {})`
204
205Every `/^\d+\.\.\d+/` line emits a `'plan'` event for the test numbers
206`plan.start` through `plan.end`, inclusive.
207
208If the test is [completely
209skipped](http://podwiki.hexten.net/TAP/TAP.html?page=TAP#Skippingeverything)
210the 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
225A `/^TAP version (\d+)/` line emits a `'version'` event with a version
226number or string.
227
228## `p.on('bailout', function (reason) {})`
229
230A `bail out!` line will cause the parser to completely stop doing
231anything. Child parser bailouts will bail out their parents as well.
232
233## `p.on('child', function (childParser) {})`
234
235If a child test set is embedded in the stream like this:
236
237```
238TAP Version 14
2391..2
240# nesting
241 1..2
242 ok 1 - true is ok
243 ok 2 - doag is also okay
244ok 1 - nesting
245ok 2 - second
246```
247
248then the child stream will be parsed and events will be raised on the
249`childParser` object.
250
251Since TAP streams with child tests _must_ follow child test sets
252with a pass or fail assert based on the child test's results, failing
253to handle child tests should always result in the same end result.
254However, additional information from those child tests will obviously
255be lost.
256
257See `Subtests` below for more information on which sorts of subtest
258formats are supported by this parser.
259
260## `p.on('result', function (assert) {})`
261
262This is the same as the `assert` event, except that it only emits on the root
263parser, whenever it or any child parser has an `assert` event that is not
264merely 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
274Emitted 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
276of the appropriate type.
277
278## `p.on('extra', function (extra) {})`
279
280All other lines will trigger an `'extra'` event with the line text.
281
282# static method: `const results = Parser.parse(string, options = {})`
283
284This will return an array of all the events encountered in the parsed TAP
285string.
286
287Any options to the `Parser` constructor may be provided, in addition to the
288following:
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
301Turn a `results` list of the sort returned by `Parser.parse()` into a TAP
302string.
303
304The 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
315The `indent` and `id` options are used internally, and should not be
316modified.
317
318# install
319
320With [npm](https://npmjs.org) do:
321
322```
323npm install tap-parser
324```
325
326You can use [browserify](http://browserify.org) to `require('tap-parser')` in
327the browser.
328
329# license
330
331MIT
332
333# subtests
334
3355 flavors of Subtests are suppored by this parser.
336
3371. 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
3482. 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
3613. 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
3744. 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
3875. 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
403In all cases, the parsed behavior is identical:
404
4051. The parent emits a `child` event with the `childParser` as an
406 argument.
4072. The `childParser` emits a `comment` with `# Subtest: <name>` (or
408 `(anonymous)` for Unadorned subtests.)
4093. When the test is over, the closing test point is emitted on parent
410 test.
411
412That is, buffered and nonindented/indented comment subtests are parsed
413as if they are identical input, since their semantics are the same. This
414simplifies implementation of test harness and reporter modules.
415
416Since unadorned subtests have no introduction, a child event is not
417emitted until the first "relevant tap" line is encountered. This can
418cause confusion if the test output contains a spurious " 1..2" line
419or something, but such cases are rare.
420
421Similarly, this means that a test point ending in `{` needs to wait to
422emit _either_ the 'assert' or 'child' events until an indented line is
423encountered. _Any_ test point with yaml diagnostics needs to wait to
424see if it will be followed by a `{` indicating a subtest.
425
\No newline at end of file