From 9fcfd5291e504d223e7527be0ef6ce1897f91966 Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 30 Nov 2017 16:22:57 -0800 Subject: [PATCH] Migrate docs into main repository --- docs/100/index.md | 52 ++ docs/CNAME | 1 + docs/Gemfile | 2 + docs/Makefile | 2 + docs/_config.yml | 49 ++ docs/_layouts/layout.html | 165 ++++ docs/advanced/index.md | 157 ++++ docs/api/index.md | 231 ++++++ docs/asserts/index.md | 260 +++++++ .../coverage-example-1/lcov-report/base.css | 212 +++++ .../coverage-example-1/lcov-report/index.html | 93 +++ .../lcov-report/prettify.css | 1 + .../lcov-report/prettify.js | 1 + .../lcov-report/root/index.html | 93 +++ .../root/my-awesome-module.js.html | 101 +++ .../lcov-report/sort-arrow-sprite.png | Bin 0 -> 209 bytes .../coverage-example-1/lcov-report/sorter.js | 158 ++++ docs/basics/coverage-example-1/lcov.info | 28 + docs/basics/index.md | 404 ++++++++++ docs/changelog/index.md | 396 ++++++++++ docs/cli/index.md | 278 +++++++ docs/grep/index.md | 290 +++++++ docs/index.md | 155 ++++ docs/mochalike/index.md | 117 +++ docs/only/index.md | 200 +++++ docs/parallel/index.md | 175 +++++ docs/params.json | 1 + docs/promises/index.md | 124 +++ docs/reporting/index.md | 87 +++ docs/snapshots/index.md | 151 ++++ docs/static/prism.css | 196 +++++ docs/static/prism.js | 728 ++++++++++++++++++ docs/static/tapjs.png | Bin 0 -> 57234 bytes docs/subtests/index.md | 131 ++++ docs/tap-format/index.md | 333 ++++++++ 35 files changed, 5372 insertions(+) create mode 100644 docs/100/index.md create mode 100644 docs/CNAME create mode 100644 docs/Gemfile create mode 100644 docs/Makefile create mode 100644 docs/_config.yml create mode 100755 docs/_layouts/layout.html create mode 100644 docs/advanced/index.md create mode 100644 docs/api/index.md create mode 100644 docs/asserts/index.md create mode 100644 docs/basics/coverage-example-1/lcov-report/base.css create mode 100644 docs/basics/coverage-example-1/lcov-report/index.html create mode 100644 docs/basics/coverage-example-1/lcov-report/prettify.css create mode 100644 docs/basics/coverage-example-1/lcov-report/prettify.js create mode 100644 docs/basics/coverage-example-1/lcov-report/root/index.html create mode 100644 docs/basics/coverage-example-1/lcov-report/root/my-awesome-module.js.html create mode 100644 docs/basics/coverage-example-1/lcov-report/sort-arrow-sprite.png create mode 100644 docs/basics/coverage-example-1/lcov-report/sorter.js create mode 100644 docs/basics/coverage-example-1/lcov.info create mode 100644 docs/basics/index.md create mode 100644 docs/changelog/index.md create mode 100644 docs/cli/index.md create mode 100644 docs/grep/index.md create mode 100644 docs/index.md create mode 100644 docs/mochalike/index.md create mode 100644 docs/only/index.md create mode 100644 docs/parallel/index.md create mode 100644 docs/params.json create mode 100644 docs/promises/index.md create mode 100644 docs/reporting/index.md create mode 100644 docs/snapshots/index.md create mode 100644 docs/static/prism.css create mode 100644 docs/static/prism.js create mode 100644 docs/static/tapjs.png create mode 100644 docs/subtests/index.md create mode 100644 docs/tap-format/index.md diff --git a/docs/100/index.md b/docs/100/index.md new file mode 100644 index 000000000..5efd0c0da --- /dev/null +++ b/docs/100/index.md @@ -0,0 +1,52 @@ +--- +layout: layout +--- + +# The TAP 100 + +These modules use the `--100` flag to run tests with 100% +[coverage](/coverage/) of all lines, branches, statements, and +functions. + +To add yours to the list, send a [pull +request](https://github.com/tapjs/node-tap/blob/gh-pages/100/index.md) +to add it to the docs. + +* [abbrev](https://www.npmjs.com/package/abbrev) +* [casern](https://www.npmjs.com/package/casern) +* [color-support](https://www.npmjs.com/package/color-support) +* [contentfs](https://www.npmjs.com/package/contentfs) +* [events-to-array](https://www.npmjs.com/package/events-to-array) +* [express-jwt-permissions](https://www.npmjs.com/package/express-jwt-permissions) +* [fs-exists-cached](https://www.npmjs.com/package/fs-exists-cached) +* [fs-minipass](https://www.npmjs.com/package/fs-minipass) +* [fs-readstream-seek](https://www.npmjs.com/package/fs-readstream-seek) +* [function-loop](https://www.npmjs.com/package/function-loop) +* [hoodie](https://www.npmjs.com/package/hoodie) +* [icepick](https://www.npmjs.com/package/icepick) +* [ignore-walk](https://www.npmjs.com/package/ignore-walk) +* [inflight](https://www.npmjs.com/package/inflight) +* [ini](https://www.npmjs.com/package/ini) +* [isexe](https://www.npmjs.com/package/isexe) +* [lru-cache](https://www.npmjs.com/package/lru-cache) +* [lucass](https://www.npmjs.com/package/lucass) +* [minipass](https://www.npmjs.com/package/minipass) +* [minizlib](https://www.npmjs.com/package/minizlib) +* [mutate-fs](https://www.npmjs.com/package/mutate-fs) +* [natives](https://www.npmjs.com/package/natives) +* [npm-bundled](https://www.npmjs.com/package/npm-bundled) +* [npm-packlist](https://www.npmjs.com/package/npm-packlist) +* [stack-utils](https://www.npmjs.com/package/stack-utils) +* [t-up](https://www.npmjs.com/package/t-up) +* [tap-parser](https://www.npmjs.com/package/tap-parser) +* [tap](/) +* [tapromise](https://www.npmjs.com/package/tapromise) +* [tapsert](https://www.npmjs.com/package/tapsert) +* [tapshot](https://www.npmjs.com/package/tapshot) +* [tar](https://www.npmjs.com/package/tar) +* [tmatch](https://www.npmjs.com/package/tmatch) +* [touch](https://www.npmjs.com/package/touch) +* [trivial-deferred](https://www.npmjs.com/package/trivial-deferred) +* [tsame](https://www.npmjs.com/package/tsame) +* [yallist](https://www.npmjs.com/package/yallist) +* [yapool](https://www.npmjs.com/package/yapool) diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 000000000..a3e4c91b4 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +www.node-tap.org diff --git a/docs/Gemfile b/docs/Gemfile new file mode 100644 index 000000000..053c27dc3 --- /dev/null +++ b/docs/Gemfile @@ -0,0 +1,2 @@ +source 'https://rubygems.org' +gem 'github-pages' diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 000000000..a54da7e90 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,2 @@ +run: + bundle exec jekyll serve diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 000000000..f50fcd0c5 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,49 @@ +title: "Node Tap" +description: A Test-Anything-Protocol library for Node.js +links: + - name: "Index" + url: "/" + - name: "Getting Started" + url: "/basics/" + - name: "API" + url: "/api/" + links: + - name: "Asserts" + url: "/asserts/" + - name: "Promises" + url: "/promises/" + - name: "Subtests" + url: "/subtests/" + - name: "Parallel Tests" + url: "/parallel/" + - name: "Snapshot Testing" + url: "/snapshots/" + - name: "Filtering Tests: grep" + url: "/grep/" + - name: "Filtering Tests: only" + url: "/only/" + - name: "Mocha-like DSL" + url: "/mochalike/" + - name: "Advanced" + url: "/advanced/" + - name: "CLI" + url: "/cli/" + - name: "The Protocol" + url: "/tap-format/" + - name: "Reporting" + url: "/reporting/" + - name: "Coverage" + url: "/coverage/" + - name: "Change Log" + url: "/changelog/" + - name: "GitHub Repo" + url: "https://github.com/tapjs/node-tap" + +# Build settings +markdown: kramdown +markdown_ext: md + +kramdown: + input: GFM + hard_wrap: false + syntax_highlighter: rouge diff --git a/docs/_layouts/layout.html b/docs/_layouts/layout.html new file mode 100755 index 000000000..54e6d1f85 --- /dev/null +++ b/docs/_layouts/layout.html @@ -0,0 +1,165 @@ + +{{ site.title }} + + + + + + +
+

Test Anything JS

+

{{ site.description }}

+ +
npm install tap
+tap test/*.js
+
+
+{{ content }} +
+ + diff --git a/docs/advanced/index.md b/docs/advanced/index.md new file mode 100644 index 000000000..8ef8d3c79 --- /dev/null +++ b/docs/advanced/index.md @@ -0,0 +1,157 @@ +--- +layout: layout +--- + +# Advanced Usage + +These methods are primarily for internal use, but can be handy in some +unusual situations. If you find yourself using them frequently, you +*may* be Doing It Wrong. However, if you find them useful, you should +feel perfectly comfortable using them. + +Please [let us know](https://github.com/isaacs/node-tap/issues) if you +frequently encounter situations requiring advanced usage, because this +may indicate a shortcoming in the "non-advanced" [API](/api/). + +## Class: t.Spawn() + +Similar to the `Test` class, but instead of a callback that gets a +object with assertion methods, it starts a child process and parses its +output. + +## Class: t.Stdin() + +Similar to the `Test` class, but instead of a callback that gets a +object with assertion methods, it reads the process standard input, +and parses that as [TAP](/tap-format)-formatted data. + +## t.stdin() + +Parse standard input as if it was a child test named `/dev/stdin`. + +Returns a Promise which resolves with the parent when the input stream +is completed. + +This is primarily for use in the test runner, so that you can do +`some-tap-emitting-program | tap other-file.js - -Rnyan`. + +## t.spawn(command, arguments, [options], [name]) + +Sometimes, instead of running a child test directly inline, you might +want to run a TAP producting test as a child process, and treat its +standard output as the TAP stream. + +Returns a Promise which resolves with the parent when the child +process is completed. + +That's what this method does. + +It is primarily used by the executable runner, to run all of the +filename arguments provided on the command line. + +The `options` object is passed to `child_process.spawn`, and can +contain stuff like stdio directives and environment vars. It's also +where you put the same fields that would be passed to any assertion or +child test: + +* `bail`: Set to `true` to bail out on the first failure. This is + done by checking the output and then forcibly killing the process, + but also sets the `TAP_BAIL` environment variable, which node-tap + uses to set this field internally as well. +* `timeout`: The number of ms to allow the child process to continue. + If it goes beyond this time, the child process will be forcibly + killed. +* `todo` Set to boolean `true` or a String to mark this as pending. +* `skip` Set to boolean `true` or a String to mark this as skipped. +* `bail` Set to boolean `true` to bail out on the first test failure. +* `diagnostic` Set to `true` to show a yaml diagnostic block even if + the test passes. Set to `false` to never show a yaml diagnostic + block. +* `buffered` Set to `true` to run as a buffered [subtest](/subtests/). + Set to `false` to run as an indented subtest. The default is + `false` unless `TAP_BUFFER=1` is set in the environment. + +## t.addAssert(name, length, fn) + +This is used for creating assertion methods on the `Test` class. + +It's a little bit advanced, but it's also super handy sometimes. All +of the assert methods below are created using this function, and it +can be used to create application-specific assertions in your tests. + +The name is the method name that will be created. The length is the +number of arguments the assertion operates on. (The `message` and +`extra` arguments will always be appended to the end.) + +For example, you could have a file at `test/setup.js` that does the +following: + +```javascript +var tap = require('tap') + +// convenience +if (module === require.main) { + tap.pass('ok') + return +} + +// Add an assertion that a string is in Title Case +// It takes one argument (the string to be tested) +tap.Test.prototype.addAssert('titleCase', 1, function (str, message, extra) { + message = message || 'should be in Title Case' + // the string in Title Case + // A fancier implementation would avoid capitalizing little words + // to get `Silence of the Lambs` instead of `Silence Of The Lambs` + // But whatever, it's just an example. + var tc = str.toLowerCase().replace(/\b./, function (match) { + return match.toUpperCase() + }) + + // should always return another assert call, or + // this.pass(message) or this.fail(message, extra) + return this.equal(str, tc, message, extra) +}) +``` + +Then in your individual tests, you'd do this: + +```javascript +require('./setup.js') // adds the assert +var tap = require('tap') +tap.titleCase('This Passes') +tap.titleCase('however, tHis tOTaLLy faILS') +``` + +## t.endAll() + +Call the `end()` method on all child tests, and then on this one. + +## t.assertAt, t.assertStack, extra.at, extra.stack + +The Test object will try to work out the most useful `stack` and `at` +options to tell you where a failing assertion was made. + +In very rare and interesting cases, you _may_ wish to override this +for some reason. For example, you may be wrapping tap.Test object +methods, and wish to show the user where they called your method, +rather than showing where your method called into tap. + +You can do this in two possible ways: + +1. Set the `at` and/or `stack` properties on the `extra` object passed to + assert methods. +2. Set the `t.assertAt` and/or `t.assertStack` properties on the + Test object immediately before calling the assertion method. The + values are consumed and deleted when the next assertion is called. + +The `at` property should be an object with the following properties at +minimum: + +* `file` - The file name where the assertion is called +* `line` - The line number where the assertion is called + +The `stack` property should be a string stack trace similar to those +found on `Error` objects. + +For best results, calculate these values using the +[stack-utils](http://npm.im/stack-utils) module. diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 000000000..cb623f33e --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,231 @@ +--- +layout: layout +--- + +# API + +This is the API that you interact with when writing tests using +node-tap. + +See also: + +- [Getting Started](/basics/) +- [Asserts](/asserts/) +- [Snapshot Testing](/snapshots/) +- [Promises](/promises/) +- [Subtests](/subtests/) +- [Parallel Tests](/parallel/) +- [Filtering Tests with Grep](/grep/) +- [Filtering Tests with Only](/only/) +- [Mocha-like DSL](/mochalike/) +- [Advanced Usage](/advanced/) + +## tap = require('tap') + +The root `tap` object is an instance of the Test class with a few +slight modifications. + +1. By default, it pipes to stdout, so running a test directly just + dumps the TAP data for inspection. This piping behavior is a + _little_ bit magic -- it only pipes when you do something that + triggers output, so there's no need to manually unpipe if you never + actually use it to run tests. +2. Various other things are hung onto it for convenience, since it is + the main package export. +3. The test ends automatically when `process.on('exit')` fires, so + there is no need to call `tap.end()` explicitly. +4. Adding a `tearDown` function triggers `autoend` behavior. + Otherwise, the `end` would potentially never arrive, if for example + `tearDown` is used to close a server or cancel some long-running + process, because `process.on('exit')` would never fire of its own + accord. + +## tap.Test + +The `Test` class is the main thing you'll be touching when you use +this module. + +The most common way to instantiate a `Test` object by calling the +`test` method on the root or any other `Test` object. The callback +passed to `test(name, fn)` will receive a child `Test` object as its +argument. + +A `Test` object is a Readable Stream. Child tests automatically send +their data to their parent, and the root `require('tap')` object pipes +to stdout by default. However, you can instantiate a `Test` object +and then pipe it wherever you like. The only limit is your imagination. + +Whenever you see `t.` in this documentation, it refers to a +Test object, but applies equally well in most cases to the root test. + +### t.test([name], [options], [function]) + +Create a subtest. Returns a [Promise](/promises/) which resolves with +the parent when the child test is completed. + +If the function is omitted, then it will be marked as a "todo" or +"pending" test. + +If the function has a name, and no name is provided, then the function +name will be used as the test name. If no test name is provided, then +the name will be `(unnamed test)`. + +The function gets a Test object as its only argument. From there, you +can call the `t.end()` method on that object to end the test, or use +the `t.plan()` method to specify how many child tests or +[asserts](/asserts/) the test will have. + +If the function returns a [Promise](/promises/) object (that is, an +object with a `then` method), then when the promise is rejected or +fulfilled, the test will be either ended or failed. Note that this +means that an `async` function will automatically end when it's done, +because of the implicit promise. + +If the function is not provided, then this will be treated as a `todo` +test. + +The options object is the same as would be passed to [any +assert](/asserts/), with some additional fields that are only relevant +for child tests: + +* `todo` Set to boolean `true` or a String to mark this as pending. + (Note that this is always the case if no function is provided.) +* `skip` Set to boolean `true` or a String to mark this as skipped. +* `timeout`: The number of ms to allow the test to run. +* `bail`: Set to `true` to bail out on the first test failure. +* `autoend`: Automatically `end()` the test on the next turn of the + event loop after its internal queue is drained. +* `diagnostic` Set to boolean `true` to show a yaml diagnostic block + even if the test passes. Set to `false` to never show a yaml + diagnostic block. (Failing tests show yaml diagnostics by default.) +* `buffered` Set to `true` to run as a buffered [subtest](/subtests/). + Set to `false` to run as an indented subtest. The default is + `false` unless `TAP_BUFFER=1` is set in the environment. +* `jobs` Set to an integer to assign the `t.jobs` property. +* `grep` Set to an array of regular expressions to [filter subtests + with patterns](/grep/) +* `only` Set to `true` to run this test when in `runOnly` mode. + See [filtering tests using only](/only/) +* `runOnly` Set to `true` to only run tests with `only:true` set. + +### t.todo([name], [options], [function]) + +Exactly the same as `t.test()`, but adds `todo: true` in the options. + +### t.skip([name], [options], [function]) + +Exactly the same as `t.test()`, but adds `skip: true` in the options. + +### t.only([name], [options], [function]) + +Exactly the same as `t.test()`, but adds `only: true` in the options. + +See [filtering tests using only](/only/) + +### t.runOnly + +Set to `true` to only run child tests that have `only: true` set in +their options (or are run with `t.only()`, which is the same thing). + +### t.jobs + +If you set the `t.jobs` property to a number greater than 1, then it +will enable [parallel execution](/parallel/) of all of this test's +children. + +### t.tearDown(function) + +Run the supplied function when `t.end()` is called, or when the `plan` +is met. + +Note that when called on the root `tap` export, this also triggers +`autoend` behavior. + +### t.beforeEach(function (done) {}) + +Call the supplied function before every subsequent descendent test. + +The `done` callback is a function to call when finished. You can also +return a [Promise](/promises/) rather than using the `done` callback. + +### t.afterEach(function (done) {}) + +Call the supplied function after every subsequent descendent test. + +The `done` callback is a function to call when finished. You can also +return a [Promise](/promises/) rather than using the `done` callback. + +### t.plan(number) + +Specify that a given number of tests are going to be run. + +This may only be called *before* running any [asserts](/asserts/) or +child tests. + +### t.end() + +Call when tests are done running. This is not necessary if `t.plan()` +was used, or if the test function returns a [Promise](/promises/). + +If you call `t.end()` explicitly more than once, an error will be +raised. + +### t.bailout([reason]) + +Fire the proverbial ejector seat. + +Use this when things are severely broken, and cannot be reasonably +handled. Immediately terminates the entire test run. + +### t.passing() + +Return true if everything so far is ok. + +Note that all assert methods also return `true` if they pass. + +### t.comment(message) + +Print the supplied message as a TAP comment. + +Note that you can always use `console.error()` for debugging (or +`console.log()` as long as the message doesn't look like TAP formatted +data). + +### t.fail(message, extra) + +Emit a failing test point. This method, and `pass()`, are the basic +building blocks of all fancier assertions. + +### t.pass(message) + +Emit a passing test point. This method, and `fail()`, are the basic +building blocks of all fancier assertions. + +### t.pragma(set) + +Sets a `pragma` switch for a set of boolean keys in the argument. + +The only pragma currently supported by the TAP parser is `strict`, +which tells the parser to treat non-TAP output as a failure. + +Example: + +``` +var t = require('tap') +console.log('this non-TAP output is ok') +t.pragma({ strict: true }) +console.log('but this will cause a failure') +``` + +### t.threw(error) + +When an uncaught exception is raised in the context of a test, then +this method is used to handle the error. It fails the test, and +prints out appropriate information about the stack, message, current +test, and so on. + +Generally, you never need to worry about this directly. + +However, this method can also be called explicitly in cases where an +error would be handled by something else (for example, a default +[Promise](/promises/) `.catch(er)` method.) diff --git a/docs/asserts/index.md b/docs/asserts/index.md new file mode 100644 index 000000000..eae1d7618 --- /dev/null +++ b/docs/asserts/index.md @@ -0,0 +1,260 @@ +--- +layout: layout +--- + +# Asserts + +The `Test` object has a collection of assertion methods, many of which +are given several synonyms for compatibility with other test runners +and the vagaries of human expectations and spelling. When a synonym +is multi-word in `camelCase` the corresponding lower case and +`snake_case` versions are also created as synonyms. + +All assertion methods take optional `message` and `extra` arguments as +the last two params. The `message` is the name of the test. The +`extra` argument can contain any arbitrary data about the test, but +the following fields are "special". + +* `todo` Set to boolean `true` or a String to mark this as pending +* `skip` Set to boolean `true` or a String to mark this as skipped +* `diagnostic` Set to boolean `true` to show a yaml diagnostic block + even if the test point passes. (Failing asserts always show yaml + diagnostics.) +* `at` Generated by the framework. The location where the assertion + was called. Do not set this field unless you know what you are + doing. +* `stack` Generated by the framework. The stack trace to the point + where the assertion was called. Do not set this field unless you + know what you are doing. + +**Note**: There's no requirement that you use tap's built-in +assertions. You can also use any Error-throwing assertion library, +including Node.js's built-in `assert` module. A throw fails a test, +so not-throwing passes. That does, however, mean that you won't +generate a test point in your output for each assertion run. You do +you. + +## t.ok(obj, message, extra) + +Verifies that the object is truthy. + +Synonyms: `t.true`, `t.assert` + +## t.notOk(obj, message, extra) + +Verifies that the object is not truthy. + +Synonyms: `t.false`, `t.assertNot` + +## t.error(obj, message, extra) + +If the object is an error, then the assertion fails. + +Note: if an error is encountered unexpectedly, it's often better to +simply throw it. The Test object will handle this as a failure. + +Synonyms: `t.ifErr`, `t.ifError` + +## t.rejects(promise | fn, [expectedError], message, extra) + +Verifies that the promise (or promise-returning function) rejects. If +an expected error is provided, then also verify that the rejection +matches the expected error. + +Note: since promises always reject and resolve asynchronously, this +assertion is actually implemented using a subtest. As such, it does +not return a boolean to indicate its passing status. Instead, it +returns a Promise that resolves when it is completed. + +## t.resolves(promise | fn, message, extra) + +Verifies that the promise (or promise-returning function) resolves, +making no expectation about the value that the promise resolves to. + +Note: since promises always reject and resolve asynchronously, this +assertion is actually implemented using a subtest. As such, it does +not return a boolean to indicate its passing status. Instead, it +returns a Promise that resolves when it is completed. + +## t.resolveMatch (promise | fn, wanted, message, extra) + +Verifies that the promise (or promise-returning function) resolves, +and furthermore that the value of the promise matches the `wanted` +pattern using `t.match`. + +Note: since promises always reject and resolve asynchronously, this +assertion is actually implemented using a subtest. As such, it does +not return a boolean to indicate its passing status. Instead, it +returns a Promise that resolves when it is completed. + + +## t.throws(fn, [expectedError], message, extra) + +Expect the function to throw an error. If an expected error is +provided, then also verify that the thrown error matches the expected +error. + +If the expected error is an object, then it's matched against the +thrown error using `t.match(er, expectedError)`. If it's a function, +then the error is asserted to be a member of that class. + +If the function has a name, and the message is not provided, then the +function name will be used as the message. + +If the function is not provided, then this will be treated as a `todo` +test. + +Caveat: if you pass a `extra` object to t.throws, then you MUST also +pass in an expected error, or else it will read the diag object as the +expected error, and get upset when your thrown error doesn't match +`{skip:true}` or whatever. + +For example, this will not work as expected: + +```javascript +// anti-example, do not use! +t.throws(function() {throw new Error('x')}, { skip: true }) +``` + +But this is fine: + +```javascript +// this example is ok to use. +// note the empty 'expected error' object. +// since it has no fields, it'll only verify that the thrown thing is +// an object, not the value of any properties +t.throws(function() {throw new Error('x')}, {}, { skip: true }) +``` + +The expected error is tested against the throw error using `t.match`, +so regular expressions and the like are fine. If the expected error +is an `Error` object, then the `stack` field is ignored, since that +will generally never match. + +Synonyms: `t.throw` + +## t.doesNotThrow(fn, message, extra) + +Verify that the provided function does not throw. + +If the function has a name, and the message is not provided, then the +function name will be used as the message. + +If the function is not provided, then this will be treated as a `todo` +test. + +Note: if an error is encountered unexpectedly, it's often better to +simply throw it. The Test object will handle this as a failure. + +Synonyms: `t.notThrow` + +## t.equal(found, wanted, message, extra) + +Verify that the object found is exactly the same (that is, `===`) to +the object that is wanted. + +Synonyms: `t.equals`, `t.isEqual`, `t.is`, `t.strictEqual`, +`t.strictEquals`, `t.strictIs`, `t.isStrict`, `t.isStrictly` + +## t.notEqual(found, notWanted, message, extra) + +Inverse of `t.equal()`. + +Verify that the object found is not exactly the same (that is, `!==`) as +the object that is wanted. + +Synonyms: `t.inequal`, `t.notEqual`, `t.notEquals`, +`t.notStrictEqual`, `t.notStrictEquals`, `t.isNotEqual`, `t.isNot`, +`t.doesNotEqual`, `t.isInequal` + +## t.same(found, wanted, message, extra) + +Verify that the found object is deeply equivalent to the wanted +object. Use non-strict equality for scalars (ie, `==`). See: +[tsame](http://npm.im/tsame) + +Synonyms: `t.equivalent`, `t.looseEqual`, `t.looseEquals`, +`t.deepEqual`, `t.deepEquals`, `t.isLoose`, `t.looseIs` + +## t.notSame(found, notWanted, message, extra) + +Inverse of `t.same()`. + +Verify that the found object is not deeply equivalent to the +unwanted object. Uses non-strict inequality (ie, `!=`) for scalars. + +Synonyms: `t.inequivalent`, `t.looseInequal`, `t.notDeep`, +`t.deepInequal`, `t.notLoose`, `t.looseNot` + +## t.strictSame(found, wanted, message, extra) + +Strict version of `t.same()`. + +Verify that the found object is deeply equivalent to the wanted +object. Use strict equality for scalars (ie, `===`). + +Synonyms: `t.strictEquivalent`, `t.strictDeepEqual`, `t.sameStrict`, +`t.deepIs`, `t.isDeeply`, `t.isDeep`, `t.strictDeepEquals` + +## t.strictNotSame(found, notWanted, message, extra) + +Inverse of `t.strictSame()`. + +Verify that the found object is not deeply equivalent to the unwanted +object. Use strict equality for scalars (ie, `===`). + +Synonyms: `t.strictInequivalent`, `t.strictDeepInequal`, +`t.notSameStrict`, `t.deepNot`, `t.notDeeply`, `t.strictDeepInequals`, +`t.notStrictSame` + +## t.match(found, pattern, message, extra) + +Verify that the found object matches the pattern provided. + +If pattern is a regular expression, and found is a string, then verify +that the string matches the pattern. + +If the pattern is a string, and found is a string, then verify that +the pattern occurs within the string somewhere. + +If pattern is an object, then verify that all of the (enumerable) +fields in the pattern match the corresponding fields in the object +using this same algorithm. For example, the pattern `{x:/a[sdf]{3}/}` +would successfully match `{x:'asdf',y:'z'}`. + +This is useful when you want to verify that an object has a certain +set of required fields, but additional fields are ok. + +See [tmatch](http://npm.im/tmatch) for the full details on how this +works. + +Synonyms: `t.has`, `t.hasFields`, `t.matches`, `t.similar`, `t.like`, +`t.isLike`, `t.includes`, `t.include`, `t.contains` + +## t.notMatch(found, pattern, message, extra) + +Interse of `match()` + +Verify that the found object does not match the pattern provided. + +Synonyms: `t.dissimilar`, `t.unsimilar`, `t.notSimilar`, `t.unlike`, +`t.isUnlike`, `t.notLike`, `t.isNotLike`, `t.doesNotHave`, +`t.isNotSimilar`, `t.isDissimilar` + +## t.type(object, type, message, extra) + +Verify that the object is of the type provided. + +Type can be a string that matches the `typeof` value of the object, or +the string name of any constructor in the object's prototype chain, or +a constructor function in the object's prototype chain. + +For example, all the following will pass: + +```javascript +t.type(new Date(), 'object') +t.type(new Date(), 'Date') +t.type(new Date(), Date) +``` + +Synonyms: `t.isa`, `t.isA` diff --git a/docs/basics/coverage-example-1/lcov-report/base.css b/docs/basics/coverage-example-1/lcov-report/base.css new file mode 100644 index 000000000..0c0571dad --- /dev/null +++ b/docs/basics/coverage-example-1/lcov-report/base.css @@ -0,0 +1,212 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px;; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } + + +.medium .chart { border:1px solid #666; } +.medium .cover-fill { background: #666; } + +.cbranch-no { background: yellow !important; color: #111; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } +.medium { background: #eaeaea; } + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/docs/basics/coverage-example-1/lcov-report/index.html b/docs/basics/coverage-example-1/lcov-report/index.html new file mode 100644 index 000000000..09329a566 --- /dev/null +++ b/docs/basics/coverage-example-1/lcov-report/index.html @@ -0,0 +1,93 @@ + + + + Code coverage report for All files + + + + + + + +
+
+

+ / +

+
+
+ 55.56% + Statements + 5/9 +
+
+ 37.5% + Branches + 3/8 +
+
+ 100% + Functions + 1/1 +
+
+ 55.56% + Lines + 5/9 +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
__root__/
55.56%5/937.5%3/8100%1/155.56%5/9
+
+
+ + + + + + + diff --git a/docs/basics/coverage-example-1/lcov-report/prettify.css b/docs/basics/coverage-example-1/lcov-report/prettify.css new file mode 100644 index 000000000..b317a7cda --- /dev/null +++ b/docs/basics/coverage-example-1/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/docs/basics/coverage-example-1/lcov-report/prettify.js b/docs/basics/coverage-example-1/lcov-report/prettify.js new file mode 100644 index 000000000..ef51e0386 --- /dev/null +++ b/docs/basics/coverage-example-1/lcov-report/prettify.js @@ -0,0 +1 @@ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/docs/basics/coverage-example-1/lcov-report/root/index.html b/docs/basics/coverage-example-1/lcov-report/root/index.html new file mode 100644 index 000000000..5569fc7bd --- /dev/null +++ b/docs/basics/coverage-example-1/lcov-report/root/index.html @@ -0,0 +1,93 @@ + + + + Code coverage report for __root__/ + + + + + + + +
+
+

+ all files __root__/ +

+
+
+ 55.56% + Statements + 5/9 +
+
+ 37.5% + Branches + 3/8 +
+
+ 100% + Functions + 1/1 +
+
+ 55.56% + Lines + 5/9 +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
my-awesome-module.js
55.56%5/937.5%3/8100%1/155.56%5/9
+
+
+ + + + + + + diff --git a/docs/basics/coverage-example-1/lcov-report/root/my-awesome-module.js.html b/docs/basics/coverage-example-1/lcov-report/root/my-awesome-module.js.html new file mode 100644 index 000000000..61ea83a64 --- /dev/null +++ b/docs/basics/coverage-example-1/lcov-report/root/my-awesome-module.js.html @@ -0,0 +1,101 @@ + + + + Code coverage report for my-awesome-module.js + + + + + + + +
+
+

+ all files / __root__/ my-awesome-module.js +

+
+
+ 55.56% + Statements + 5/9 +
+
+ 37.5% + Branches + 3/8 +
+
+ 100% + Functions + 1/1 +
+
+ 55.56% + Lines + 5/9 +
+
+
+
+

+
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13  +1× +2× +1× +1× +1× +  +  +  +  +  +  + 
// my-awesome-module.js
+module.exports = function (x) {
+  if (x % 2 === 0) {
+    return 'even'
+  } else Eif (x % 2 === 1) {
+    return 'odd'
+  } else if (x > 100) {
+    return 'big'
+  } else if (x < 0) {
+    return 'negative'
+  }
+}
+ 
+
+
+ + + + + + + diff --git a/docs/basics/coverage-example-1/lcov-report/sort-arrow-sprite.png b/docs/basics/coverage-example-1/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..03f704a609c6fd0dbfdac63466a7d7c958b5cbf3 GIT binary patch literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^>_9Bd!3HEZxJ@+%Qj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>Jii$m5978H@?Fn+^JD|Y9yzj{W`447Gxa{7*dM7nnnD-Lb z6^}Hx2)'; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function (a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function (a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function () { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i =0 ; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function () { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(cols); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/docs/basics/coverage-example-1/lcov.info b/docs/basics/coverage-example-1/lcov.info new file mode 100644 index 000000000..b08ca0f72 --- /dev/null +++ b/docs/basics/coverage-example-1/lcov.info @@ -0,0 +1,28 @@ +TN: +SF:./my-awesome-module.js +FN:2,(anonymous_1) +FNF:1 +FNH:1 +FNDA:2,(anonymous_1) +DA:2,1 +DA:3,2 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,0 +DA:8,0 +DA:9,0 +DA:10,0 +LF:9 +LH:5 +BRDA:3,1,0,1 +BRDA:3,1,1,1 +BRDA:5,2,0,1 +BRDA:5,2,1,0 +BRDA:7,3,0,0 +BRDA:7,3,1,0 +BRDA:9,4,0,0 +BRDA:9,4,1,0 +BRF:8 +BRH:3 +end_of_record diff --git a/docs/basics/index.md b/docs/basics/index.md new file mode 100644 index 000000000..12d6ad2cd --- /dev/null +++ b/docs/basics/index.md @@ -0,0 +1,404 @@ +--- +layout: layout +--- + +# tap basics + +This tutorial will teach you just enough to get up and running with +tap in your Node.js programs. + +## install tap + +Use npm to install tap: + +```bash +npm install tap --save-dev +``` + +The `save-dev` part makes it saved to your package.json's +`devDependencies` list. + +Next, update your package.json so that the test script invokes tap: + +```json +{ + "name": "my-awesome-module", + "version": "1.2.3", + "devDependencies": { + "tap": "^11.0.0" + }, + + "scripts": { + "test": "tap test/*.js" + } + +} +``` + +## test files + +Create a folder for your tests. Call the folder `test` so that people +can guess what it's for: + +```bash +mkdir test/ +``` + +It's a good practice to break up a big test suite into multiple files. +Each file should cover a feature or concept. For small Node modules, +often a single test file is enough. + +I usually call the first one `test/basic.js`, because it covers the +basic functionality. + +## "hello world" test program + +The root tap object is a member of tap's `Test` class. That means it +has all the same properties as child tests. + +Here's a very basic test program: + +```javascript +// test/hello-world.js +var tap = require('tap') +tap.pass('this is fine') +``` + +If we run this with node, we'll see the raw TAP output: + +```bash +$ node test/hello-world.js +``` + +```tap +TAP version 13 +ok 1 - this is fine +1..1 +# time=26.792ms +``` + +You can always run a tap test program directly to see what it is +doing. This is especially handy in debugging test failures + +That output is "TAP" or "Test Anything Protocol". It has a long +history in the Perl community, and there are many tools in many +languages that can generate and parse this format. + +Node-tap is one of those tools, so let's have it create something +prettier for us. Because we installed tap as a devDependency, and +added it as a script in package.json, we can run `npm test` to run all +our tests with the `tap` built-in cli. + +```bash +$ npm test + +> my-awesome-module@1.2.3 test /home/isaacs/my-awesome-module +> tap test/*.js + +test/hello-world.js ................................... 1/1 +total ................................................. 1/1 + + 1 passing (227.745ms) + + ok + +``` + +## coverage + +Test coverage makes it a lot easier to know that we're testing what we +think we're testing. + +So, let's create a module to test. Let's say that we want a function +that returns 'even' if the number is even, or 'odd' if it's odd, +unless it's greater than 100, in which case it should return 'big', +and if it's less than 0, it should return 'negative'. + +```javascript +// my-awesome-module.js +module.exports = function (x) { + if (x % 2 === 0) { + return 'even' + } else if (x % 2 === 1) { + return 'odd' + } else if (x > 100) { + return 'big' + } else if (x < 0) { + return 'negative' + } +} +``` + +Probably no bugs! + +Now, we can create a test file that pulls it in and verifies the +result: + +```javascript +// test/basic.js +var tap = require('tap') +var mam = require('../my-awesome-module.js') + +// Always call as (found, wanted) by convention +tap.equal(mam(1), 'odd') +tap.equal(mam(2), 'even') +``` + +Looks good to me! + +```bash +$ npm test + +> my-awesome-module@1.2.3 test /home/isaacs/my-awesome-module +> tap test/*.js + +test/basic.js ......................................... 2/2 +test/hello-world.js ................................... 1/1 +total ................................................. 3/3 + + 3 passing (451.79ms) + + ok +``` + +Let's run with test coverage turned on, just to be sure: + +```bash +$ npm test -- --cov + +> my-awesome-module@1.2.3 test /home/isaacs/my-awesome-module +> tap test/*.js "--cov" + +test/basic.js ......................................... 2/2 575ms +test/hello-world.js ................................... 1/1 +total ................................................. 3/3 + + 3 passing (889.135ms) + + ok +-----------------------|----------|----------|----------|----------|----------------| +File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines | +-----------------------|----------|----------|----------|----------|----------------| + __root__/ | 55.56 | 37.5 | 100 | 55.56 | | + my-awesome-module.js | 55.56 | 37.5 | 100 | 55.56 | 7,8,9,10 | +-----------------------|----------|----------|----------|----------|----------------| +All files | 55.56 | 37.5 | 100 | 55.56 | | +-----------------------|----------|----------|----------|----------|----------------| +``` + +Ouch, only 50% coverage. That's not very good. Let's see what lines +are covered: + +```bash +$ npm test -- --cov --coverage-report=lcov +``` + +This runs the tests and opens a [pretty coverage +report](/basics/coverage-example-1/lcov-report/root/index.html) in a +web browser. This shows that the second half of our function isn't +being called. + +Ok, add some more tests then: + +```js +// test/basic.js +var tap = require('tap') +var mam = require('../my-awesome-module.js') + +// Always call as (found, wanted) by convention +tap.equal(mam(1), 'odd') +tap.equal(mam(2), 'even') +tap.equal(mam(200), 'big') +tap.equal(mam(-10), 'negative') +``` + +Now the test output gets a lot more interesting: + +```bash +$ npm t + +> my-awesome-module@1.2.3 test /home/isaacs/my-awesome-module +> tap test/*.js + +test/basic.js ......................................... 2/4 + not ok should be equal + +++ found + --- wanted + -big + +even + compare: === + at: + file: test/basic.js + line: 8 + column: 5 + source: | + tap.equal(mam(200), 'big') + stack: | + Object. (test/basic.js:8:5) + node.js:951:3 + + not ok should be equal + +++ found + --- wanted + -negative + +even + compare: === + at: + file: test/basic.js + line: 9 + column: 5 + source: | + tap.equal(mam(-10), 'negative') + stack: | + Object. (test/basic.js:9:5) + node.js:951:3 + +test/hello-world.js ................................... 1/1 +total ................................................. 3/5 + + + 3 passing (440.796ms) + 2 failing + +npm ERR! Test failed. See above for more details. +``` + +Let's update our code so that it makes our tests pass: + +```js +// my-awesome-module.js +module.exports = function (x) { + if (x > 100) { + return 'big' + } else if (x < 0) { + return 'negative' + } else if (x % 2 === 0) { + return 'even' + } else { + return 'odd' + } +} +``` + +And now our coverage report is much happier: + +```bash +$ npm t -- --cov + +> my-awesome-module@1.2.3 test /home/isaacs/my-awesome-module +> tap test/*.js "--cov" + +test/basic.js ......................................... 4/4 +test/hello-world.js ................................... 1/1 257ms +total ................................................. 5/5 + + 5 passing (886.473ms) + + ok +-----------------------|----------|----------|----------|----------|----------------| +File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines | +-----------------------|----------|----------|----------|----------|----------------| + __root__/ | 100 | 100 | 100 | 100 | | + my-awesome-module.js | 100 | 100 | 100 | 100 | | +-----------------------|----------|----------|----------|----------|----------------| +All files | 100 | 100 | 100 | 100 | | +-----------------------|----------|----------|----------|----------|----------------| +``` + +## async stuff + +If your module has some async stuff, you can test that using a child +test. (You can also just use child tests to group a bunch of +assertions into a block so it's easier to manage.) + +Create a child test with the `tap.test(...)` function. The child +tests look just like the main `tap` object. + +You can call the `.end()` method on a child test object when it's +done. + +```javascript +// test/async.js +// this is a silly test. +var tap = require('tap') +var fs = require('fs') +tap.test('some async stuff', function (childTest) { + fs.readdir(__dirname, function (er, files) { + if (er) { + throw er // tap will handle this + } + childTest.match(files.join(','), /\basync\.js\b/) + childTest.end() + }) +}) + +tap.test('this waits until after', function (childTest) { + // no asserts? no problem! + // the lack of throwing means "success" + childTest.end() +}) +``` + +If you run this test with Node, you'll see that the [child +tests](/subtests/) are indented: + +```bash +$ node test/async.js +``` + +```tap +TAP version 13 +# Subtest: some async stuff + ok 1 - should match pattern provided + 1..1 +ok 1 - some async stuff # time=9.647ms + +# Subtest: this waits until after + 1..0 +ok 2 - this waits until after # time=6ms + +1..2 +# time=36.53ms +``` + +If you run it with tap, it'll look just like the others + +```bash +$ npm t + +> my-awesome-module@1.2.3 test /home/isaacs/my-awesome-module +> tap test/*.js + +test/async.js ......................................... 2/2 +test/basic.js ......................................... 4/4 +test/hello-world.js ................................... 1/1 +total ................................................. 7/7 + + 7 passing (658.444ms) + + ok +``` + +Tap's [promise](/promises/) support means it plays great with +async/await. Stuff like this will Just Work out of the box if you +have a JS engine that supports async functions: + +```js +var tap = require('tap') +tap.test(async function (t) { + var result = await doSomethingAsync() + t.match(result, { ok: true, message: /dogs/ }, 'dogs are ok') + // Or you can use any assertion lib you like. as long as this + // code doesn't throw an error, it's a pass! +}) +``` + +## bonus points + +You can do these things for extra credit. + +1. Put `--cov` in your package.json test script to always run tests + with [coverage](/coverage/). +2. Install `tap` globally to [run it](/cli/) directly. + +See the [API reference](/api/) to learn more. diff --git a/docs/changelog/index.md b/docs/changelog/index.md new file mode 100644 index 000000000..a99476818 --- /dev/null +++ b/docs/changelog/index.md @@ -0,0 +1,396 @@ +--- +layout: layout +--- + +## 11.0 2017-11-26 + +Significant refactoring and speed improvements. + +Add [`t.skip()`](/api/#tskipname-options-function) and +[`t.todo()`](/api/#tskipname-options-function) methods. + +Add +[`t.resolves(promise)`](/asserts/#tresolvespromise--fn-message-extra) +to assert that a Promise object (or function that returns a Promise) +will resolve. + +Add [`t.resolveMatch(promise, +pattern)`](/asserts/#tresolvematch-promise--fn-wanted-message-extra) +to assert that a Promise object (or function that returns a Promise) +will resolve to a value matching the supplied pattern. + +Add support for [snapshot testing](/snapshots/). + +Improved implementation of [Mocha-like DSL](/mochalike/) + +### BREAKING CHANGES: + +- Classes are true ECMAScript classes now, so constructors cannot be + called without `new`. +- Unnamed subtests are not given the name `(unnamed test)` +- The `t.current()` method is removed + +## 10.7 2017-06-24 + +Add support for [filtering tests using 'only'](/only/). + +Don't show grep/only skips in the default reporter output. + +## 10.6 2017-06-23 + +Add support for [filtering tests using regular expressions](/grep/). + +## 10.5 2017-06-20 + +Add support for Maps and Sets in `t.match()`, `t.same()`, and +`t.strictSame()`. + +## 10.4 2017-06-18 + +Add +[`t.rejects()`](/asserts/#trejectspromise--fn-expectederror-message-extra) +assertion. + +## 10.3 2017-03-01 + +* Add `-o` `--output-file` to put the raw TAP to a file. +* Never print Domain objects in YAML. They're humongous. +* Don't lose error messages in doesNotThrow + +## 10.2 2017-02-18 + +Variety of minor cleanup fixes, and a debug mode. + +* Respond to TAP_DEBUG and NODE_DEBUG environs +* Catch errors thrown in teardown handlers +* Improve root-level thrown error reporting +* don't let an occupied test slip past endAll +* Handle unhandledRejection as a root TAP error +* better inspect data +* If results are synthetically set, don't clobber when parser ends +* monkeypatch exit as well as reallyExit + +## 10.1 2017-02-07 + +Added support for source maps. Stack traces in your jsx and +coffeescript files will now be helpful! + +Added the `-J` option to auto-calculate the number of cores on your +system, and run that many parallel jobs. + +## 10.0 2017-01-28 + +Full rewrite to support [parallel tests](/parallel/). Pass `-j4` on +[the command-line](/cli/) to run 4 test files at once in parallel. + +This also refactors a lot of the grimier bits of the codebase, splits +the one mega-Test class into a proper OOP hierarchy, and pulls a bunch +of reusable stuff out into modules. + +Somehow, in the process, it also fixed an odd timing bug with +`beforeEach` functions that returned promises. + +It truly is a luxury to have a massive pile of tests when it's time to +refactor. + +The [mocha-like DSL](/mochalike/) is now much more functional, and +documented. + +Now supports passng `-T` or `--timeout=0` to the [CLI](/cli/) to not +impose a timeout on tests. + +## 9.0 2017-01-07 + +Buffered subtests! + +This adds support for outputting subtests in the +[buffered](/subtests/) format, where the summary test point _precedes_ +the indented subtest output, rather than coming afterwards. + +This sets the stage for parallel tests, coming in v10. Mostly, it's +just an update to [tap-parser](http://npm.im/tap-parser), and a lot of +internal clean-up. + +Update [nyc](http://npm.im/nyc) to v10, which includes some fixes for +covering newer JavaScript language features, and support for implicit +function names. + +Also: remove a lot of excess noise and repetitive stack traces in yaml +diagnostics. + +## 8.0 2016-10-25 + +Update `tmatch` to version 3. This makes regular expressions test +against the stringified versions of the test object in all `t.match()` +methods. It's a breaking change because it can cause tests to pass +that would have failed previously, or vice versa. However, it is more +expected, and strongly recommended. + +Handle unfinished promise-awaiting tests when the process exits. + +Show yaml diagnostics for the first "missing test" failure when a plan +is not met, so that the plan can be more easily debugged. +(Diagnostics are still excluded for the additional "missing test" +failures that are generated, to reduce unnecessary noise.) + +Make coverage MUCH FASTER by turning on nyc caching. + +## 7.1 2016-09-06 + +Remove a race condition in how `Bail out!` messages got printed when +setting the "bail on failure" option in child tests. Now, whether +it's a child process or just a nested subtest, it'll always print +`Bail out!` at the failure level, then again at the top level, with +nothing in between. + +Support `{ diagnostic: false }` in the options object for failing +tests to suppress yaml diagnostics. + +Diagnostics are now shown when a synthetic `timeout` failure is +generated for child test processes that ignore `SIGTERM` and must be +killed with `SIGKILL`. + +## 7.0 2016-08-27 + +Move `# Subtest` commands to the parent level rather than the child +level, more like Perl's `Test2` family of modules. This is more +readable for humans. + +Update to version 2 of the tap parser. (This adds support for putting +the `# Subtest` commands at the parent level.) + +Support use of a `--save` and `--bail` together. Any test files that +were skipped due to a bailout are considered "not yet passed", and so +get put in the save file. + +Forcibly kill any spawned child process tests when the root test exits +the parent process, preventing zombie test processes. + +Handle `SIGTERM` signals sent to the main process after the root test +has ended. This provides more useful output in the cases where the +root test object has explicitly ended or satisfied its plan, but a +timeout still occurs because of pending event loop activity. + +Prevent `for..in` loops from iterating inherited keys in many cases, +providing resilience against `Object.prototype` mutations. + +Add the `--100` flag to set statements, functions, lines, and branches +to 100% coverage required. + +## 6.3 2016-07-30 + +Let `t.doesNotThrow` take a string as the first argument. + +Bump `nyc` up to version 7. + +The tap `lib/` folder is excluded from all stack traces. + +## 6.2 2016-07-15 + +Add the `--test-arg=` option. + +## 6.1 2016-07-01 + +Add support for `{diagnostic: true}` in test and assert options, to +force a YAML diagnostic block after passing child tests and +assertions. + +## 6.0 2016-06-30 + +Only produce output on stdout if the root TAP test object is +interacted with in some way. Simply doing `require('tap')` no longer +prints out the minimum TAP output, which means that you can interact +with, for example, `var Test = require('tap').Test` without causing +output side effects. + +Add `~/.taprc` yaml config file support. + +Add the `--dump-config` command line flag to print out the config +options. + +Document environment variables used. + +Built-in CodeCov.io support has been removed. If you were relying on +this, you can add `codecov` as a devDependency, and then add this to +the scripts block in your `package.json` file: + + { + "scripts": { + "test": "tap test/*.js --coverage", + "posttest": "tap --coverage-report=lcov | codecov" + } + } + +## 5.8 2016-06-24 + +Make coverage piping errors non-fatal. + +Clean up argument ordering logic in `t.throws()`. This now works for +almost any ordering of arguments, which is unfortunately necessary for +historical reasons. Additionally, you can now pass in an `Error` +class to verify the type, which would previously not work properly in +some cases. + +## 5.7 2016-02-22 + +Report timeout errors in child test scripts much more diligently. + +On Unix systems, the child process handles `SIGTERM` signals by +assuming that things are taking too long, dumping a report of all +active handles and requests in process, and exiting in error. + +On Windows systems (where `SIGTERM` is always uncatchably fatal), or +if a Unix child test process doesn't exit within 1 second (causing a +fatal `SIGKILL` to be sent), the parent generates more comprehensive +output to indicate that the child test exited due to a timeout. + +## 5.6 2016-02-17 + +Update `tmatch` to version 2. You can now test objects by supplying +their constructor, so `t.match(x, { foo: Function, name: String })` +would verify that the object has a `name` string and a `foo` method. + +## 5.5 2016-02-15 + +Add the `t.assertAt` and `t.assertStack` properties, to override where +an assertion was effectively called from. + +## 5.4 2016-01-31 + +Support passing in a class to `t.throws`, rather than an Error sample +object. + +## 5.3 2016-01-31 + +Return a `Promise` object from `t.test()`, `t.spawn()`, and +`t.stdin()`. + +## 5.2 2016-01-26 + +Adds `t.beforeEach()` and `t.afterEach()`. + +## 5.1 2016-01-16 + +All about the cli flags! + +Support `--node-arg=...` and `--nyc-arg=...` command line flags. + +Add support for coverage checking using `--statements=95` etc. + +Test for executable-ness more consistently across platforms. + +## 5.0 2016-01-03 + +Make it impossible to `try/catch` out of plan/end abuses. Calling +`t.end()` more than once, or having a number of tests that doesn't +match the `plan()` number, is always a failure. + +Push thrown errors to the front of the action queue. This means that, +even if other things are pending, an uncaught exception or a plan/end +bug, will always take the highest priority in what gets output. + +Many updates to nyc, spawn-wrap, and foreground-child, so that tap now +reliably works on Windows (and a [ci to prove +it](https://ci.appveyor.com/project/isaacs/node-tap).) + +Moved into the [tapjs org](https://github.com/tapjs). + +## 4.0 2015-12-30 + +Raise an error if `t.end()` is explicitly called more than once. This +is a breaking change, because it can cause previously-passing tests to +fail, if they did `t.end()` in multiple places. + +Support promises returned by mochalike functions. + +## 3.1 2015-12-29 + +Support sending coverage output to both codecov.io and coveralls.io. + +## 3.0 2015-12-29 + +Upgrade to nyc 5. This means that `config.nyc.exclude` arrays in +`package.json` now take globs instead of regular expressions. + +## 2.3 2015-11-18 + +Use the name of the function supplied to `t.test(fn)` as the test name +if a string name is not provided. + +Better support for sparse arrays. + +## 2.2 2015-10-23 + +Add support for Codecov.io as well as Coveralls.io. + +Catch failures that come after an otherwise successful test set. + +Fix timing of `t.on('end')` so that event fires *before* the next +child test is started, instead of immediately after it. + +`t.throws()` can now be supplied a regexp for the expected Error +message. + +## 2.1 2015-10-06 + +Exit in failure on root test bailout. + +Support promises returned by `t.test(fn)` function. + +## 2.0 2015-09-27 + +Update matching behavior using [tmatch](http://npm.im/tmatch). This +is a breaking change to `t.match`, `t.similar`, `t.has`, etc., but +brings them more in line with what people epirically seem to expect +these functions to do. + +Deal with pending handles left open when a child process gets a +`SIGTERM` on timeout. + +Remove domains in favor of more reliable and less invasive state and +error-catching bookkeeping. + +## 1.4 2015-09-02 + +Add `t.contains()` alias for `t.match()`. + +Use `deeper` for deep object similarity testing. + +Treat unfinished tests as failures. + +Add support for pragmas in TAP output. + +## 1.3 2015-06-23 + +Bind all Test methods to object. + +Add `t.tearDown()`, `t.autoend()`, so that the root export is Just +Another Test Object, which just happens to be piping to stdout. + +Support getting an error object in bailout() + +## 1.2 2015-05-26 + +Better support for exit status codes. + +## 1.1 2015-05-20 + +Add coverage using nyc. + +If a `COVERALLS_REPO_TOKEN` is provided, then run tests with coverage, +and pipe to coveralls. + +## 1.0 2015-05-06 + +Complete rewrite from 0.x. + +Child tests implemented as nested TAP output, similar to Perl's +`Test::More`. + +## 0.x + +The 0.x versions used a "flattened" approach to child tests, which +requires some bookkeeping. + +It worked, mostly, but its primary success was inspiring +[tape](http://npm.im/tape) and tap v1 and beyond. diff --git a/docs/cli/index.md b/docs/cli/index.md new file mode 100644 index 000000000..9e859e530 --- /dev/null +++ b/docs/cli/index.md @@ -0,0 +1,278 @@ +--- +layout: layout +--- + +# CLI + +You can get help on tap's command line interface by running `tap -h`. + +``` +Usage: + tap [options] + +Executes all the files and interprets their output as TAP +formatted test result data. + +To parse TAP data from stdin, specify "-" as a filename. + +Short options are parsed gnu-style, so for example '-bCRspec' would be +equivalent to '--bail --no-color --reporter=spec' + +If the --check-coverage or --coverage-report options are provided, but +no test files are specified, then a coverage report or coverage check +will be run on the data from the last test run. + +Coverage is never enabled for stdin. + +Options: + + -j --jobs= Run up to test files in parallel + Note that this causes tests to be run in + "buffered" mode, so line-by-line results + cannot be reported, and older TAP + parsers may get upset. + + -J --jobs-auto Run test files in parallel (auto calculated) + Note that this causes tests to be run in + "buffered" mode, so line-by-line results + cannot be reported, and older TAP + parsers may get upset. + + -g Only run subtests tests matching the specified + --grep= pattern. + + Patterns are matched against top-level + subtests in each file. To filter tests + at subsequent levels, specify this + option multiple times. + + To specify regular expression flags, + format pattern like a JavaScript RegExp + literal. For example: '/xyz/i' for + case-insensitive matching. + + -i --invert Invert the matches to --grep patterns. + (Like grep -v) + + -c --color Use colors (Default for TTY) + + -C --no-color Do not use colors (Default for non-TTY) + + -b --bail Bail out on first failure + + -B --no-bail Do not bail out on first failure (Default) + + -O --only Only run tests with {only: true} option + + -R --reporter= Use the specified reporter. Defaults to + 'classic' when colors are in use, or 'tap' + when colors are disabled. + + Available reporters: + classic doc dot dump json jsonstream + landing list markdown min nyan progress + silent spec tap xunit + + -o Send the raw TAP output to the specified + --output-file= file. Reporter output will still be + printed to stdout, but the file will + contain the raw TAP for later reply or + analysis. + + -s --save= If exists, then it should be a line- + delimited list of test files to run. If + is not present, then all command-line + positional arguments are run. + + After the set of test files are run, any + failed test files are written back to the + save file. + + This way, repeated runs with -s will + re-run failures until all the failures are + passing, and then once again run all tests. + + It's a good idea to .gitignore the file + used for this purpose, as it will churn a + lot. + + --coverage --cov Capture coverage information using 'nyc' + + If a COVERALLS_REPO_TOKEN environment + variable is set, then coverage is + captured by default and sent to the + coveralls.io service. + + --no-coverage --no-cov Do not capture coverage information. + Note that if nyc is already loaded, then + the coverage info will still be captured. + + --coverage-report= Output coverage information using the + specified istanbul/nyc reporter type. + + Default is 'text' when running on the + command line, or 'text-lcov' when piping + to coveralls. + + If 'html' is used, then the report will + be opened in a web browser after running. + + This can be run on its own at any time + after a test run that included coverage. + + --no-coverage-report Do not output a coverage report. + + --no-browser Do not open a web browser after + generating an html coverage report. + + -t --timeout= Time out test files after seconds. + Defaults to 30, or the value of the + TAP_TIMEOUT environment variable. + Setting to 0 allows tests to run + forever. + + -T --no-timeout Do not time out tests. + Equivalent to --timeout=0 + + -h --help print this thing you're looking at + + -v --version show the version of this program + + --node-arg= Pass an argument to Node binary in all + child processes. Run 'node --help' to + see a list of all relevant arguments. + This can be specified multiple times to + pass multiple args to Node. + + -gc --expose-gc Expose the gc() function to Node tests + + --debug Run JavaScript tests with node --debug + + --debug-brk Run JavaScript tests with node --debug-brk + + --harmony Enable all Harmony flags in JavaScript tests + + --strict Run JS tests in 'use strict' mode + + --test-arg= Pass an argument to test files spawned + by the tap command line executable. + This can be specified multiple times to + pass multiple args to test scripts. + + --nyc-arg= Pass an argument to nyc when running + child processes with coverage enabled. + This can be specified multiple times to + pass multiple args to nyc. + + --check-coverage Check whether coverage is within + thresholds provided. Setting this + explicitly will default --coverage to + true. + + This can be run on its own any time + after a test run that included coverage. + + --branches what % of branches must be covered? + Setting this will default both + --check-coverage and --coverage to true. + [default: 0] + + --functions what % of functions must be covered? + Setting this explicitly will default both + --check-coverage and --coverage to true. + [default: 0] + + --lines what % of lines must be covered? + Setting this explicitly will default both + --check-coverage and --coverage to true. + [default: 90] + + --statements what % of statements must be covered? + Setting this explicitly will default both + --check-coverage and --coverage to true. + [default: 0] + + --100 Full coverage, 100%. + Sets branches, statements, functions, + and lines to 100. + + --nyc-help Print nyc usage banner. Useful for + viewing options for --nyc-arg. + + --nyc-version Print version of nyc used by tap. + + --dump-config Dump the config options in JSON format. + + -- Stop parsing flags, and treat any additional + command line arguments as filenames. + +Environment Variables: + + TAP_SNAPSHOT Set to '1' to generate snapshot files + for `t.matchSnapshot()` assertions. + + TAP_RCFILE A yaml formatted file which can set any + of the above options. Defaults to + $HOME/.taprc + + TAP_TIMEOUT Default value for --timeout option. + + TAP_COLORS Set to '1' to force color output, or '0' + to prevent color output. + + TAP_BAIL Bail out on the first test failure. + Used internally when '--bailout' is set. + + TAP Set to '1' to force standard TAP output, + and suppress any reporters. Used when + running child tests so that their output + is parseable by the test harness. + + TAP_DIAG Set to '1' to show diagnostics by + default for passing tests. Set to '0' + to NOT show diagnostics by default for + failing tests. If not one of these two + values, then diagnostics are printed by + default for failing tests, and not for + passing tests. + + TAP_BUFFER Set to '1' to run subtests in buffered + mode by default. + + TAP_DEV_LONGSTACK Set to '1' to include node-tap internals + in stack traces. By default, these are + included only when the current working + directory is the tap project itself. + Note that node internals are always + excluded. + + TAP_DEV_SHORTSTACK Set to '1' to exclude node-tap internals + in stack traces, even if the current + working directory is the tap project + itself. + + _TAP_COVERAGE_ Reserved for internal use. + + TAP_DEBUG Set to '1' to turn on debug mode. + + NODE_DEBUG Include 'tap' to turn on debug mode. + + TAP_GREP A '\n'-delimited list of grep patterns + to apply to root level test objects. + (This is an implementation detail for how + the '--grep' option works.) + + TAP_GREP_INVERT Set to '1' to invert the meaning of the + patterns in TAP_GREP. (Implementation + detail for how the '--invert' flag + works.) + +Config Files: + +You can create a yaml file with any of the options above. By default, +the file at ~/.taprc will be loaded, but the TAP_RCFILE environment +variable can modify this. + +Run 'tap --dump-config' for a listing of what can be set in that file. +Each of the keys corresponds to one of the options above. +``` diff --git a/docs/grep/index.md b/docs/grep/index.md new file mode 100644 index 000000000..d66295677 --- /dev/null +++ b/docs/grep/index.md @@ -0,0 +1,290 @@ +--- +layout: layout +--- + +# Filtering Tests with Grep Options + +Child tests can be filtered using regular expressions with the `grep` +option. + +Note: this is for filtering test functions within a test file. If you +want to filter which _files_ get run, just pass the appropriate +argument to the `tap` executable. That is, instead of `tap +test/*.js`, do `tap test/foo.js` to just run a single file. + +## Command Line Usage + +On the [command-line](/cli/), specify one or more patterns with +`--grep=` (or `-g` for short). + +You can provide multiple patterns by providing multiple `--grep` +options. The first pattern will filter tests at the top level of your +test files. The next pattern will filter child tests of that test, +and so on. + +Patterns can be either a simple string, or a JavaScript RegExp literal +like `/[asdf]/i`. Use the RegExp literal format if you need to use +regular expression flags such as `i` for case insensitive matching. + +To invert the matches (that is, run all tests that _don't_ match), use +the `--invert` or `-i` flag. + +For example, consider this test file: + +```javascript +// mytest.js +const t = require('tap') + +t.test('first', async t => { + t.test('apple', async t => { + t.pass('apples are tasty') + }) + t.test('banana', async t => { + t.pass('bananas are yellow') + }) +}) + +t.test('second', async t => { + t.test('this is fine', async t => { + t.pass('i think') + }) + t.test('i am ok with how things are proceeding', async t => { + t.pass('therefor I am') + }) +}) +``` + +To only run the first test branch, you could do this: + +``` +tap --grep=first mytest.js +``` + +Using the default classic reporter, we see this output: + +``` +$ tap --grep=first mytest.js +mytest.js ............................................. 2/3 + Skipped: 1 + +total ................................................. 2/3 + + 2 passing (230.078ms) + 1 pending +``` + +Looking at the underlying TAP output by specifying the `tap` reporter, +here's what's being generated: + +``` +tap --grep=first mytest.js -Rtap +``` + +```tap +TAP version 13 +# Subtest: mytest.js + # Subtest: first + # Subtest: apple + ok 1 - apples are tasty + 1..1 + ok 1 - apple # time=8.892ms + + # Subtest: banana + ok 1 - bananas are yellow + 1..1 + ok 2 - banana # time=1.297ms + + 1..2 + ok 1 - first # time=18.544ms + + ok 2 - second # SKIP filter: /first/ + 1..2 + # skip: 1 + # time=28.242ms +ok 1 - mytest.js # time=300.676ms + +1..1 +# time=320.615ms +``` + +We can get more granular by specifying multiple greps. Let's say that +we want to run all second-level tests with a `p` or `P` in the name. +Here's how to do that: + +``` +# +-- first grep, allow anything matching . +# | +# | +-- second grep, filter matching /p/, case-insensitive +# | | +# v v +$ tap -g. -g/p/i mytest.js -Rtap +``` + +Result: + +```tap +TAP version 13 +# Subtest: mytest.js + # Subtest: first + # Subtest: apple + ok 1 - apples are tasty + 1..1 + ok 1 - apple # time=7.449ms + + ok 2 - banana # SKIP filter: /p/ + 1..2 + # skip: 1 + ok 1 - first # time=16.035ms + + # Subtest: second + ok 1 - this is fine # SKIP filter: /p/ + # Subtest: i am ok with how things are proceeding + ok 1 - therefor I am + 1..1 + ok 2 - i am ok with how things are proceeding # time=0.982ms + + 1..2 + # skip: 1 + ok 2 - second # time=4.339ms + + 1..2 + # time=28.875ms +ok 1 - mytest.js # time=255.454ms + +1..1 +# time=267.758ms +``` + +## API Programmatic Usage + +While it's rare, you can also specify greps programmatically within +tests, either in child tests or at the top level, by providing an +array of regular expressions. + +Just like on the command line, the first pattern is matched against +the first level of child tests, and so on through subsequent levels. +When all the patterns are exhausted, the entire test is run. + +The array of regular expressions can be specified on the `t` object, +or in the `options` object passed to the `t.test()` method. + +For example: + +```js +// mytest.js +const t = require('tap') + +// set on a test object directly, after creation +t.grep = [/./, /p/i] + +t.test('first', async t => { + t.test('apple', async t => { + t.pass('apples are tasty') + }) + t.test('banana', async t => { + t.pass('bananas are yellow') + }) +}) + +// new greps override what's inherited from the parent +t.test('second', { grep: [ /fi[ln]e/ ] }, async t => { + t.test('this is fine', async t => { + t.pass('i think') + }) + t.test('i am ok with how things are proceeding', async t => { + t.pass('therefor I am') + }) +}) +``` + +Output: + +```tap +TAP version 13 +# Subtest: first + # Subtest: apple + ok 1 - apples are tasty + 1..1 + ok 1 - apple # time=5.166ms + + ok 2 - banana # SKIP filter: /p/i + 1..2 + # skip: 1 +ok 1 - first # time=11.805ms + +# Subtest: second + # Subtest: this is fine + ok 1 - i think + 1..1 + ok 1 - this is fine # time=0.86ms + + ok 2 - i am ok with how things are proceeding # SKIP filter: /fi[ln]e/ + 1..2 + # skip: 1 +ok 2 - second # time=3.277ms + +1..2 +# time=21.632ms +``` + +## Setting in the Environment + +To set greps on the root level test object, you can set the `TAP_GREP` +and `TAP_GREP_INVERT` environment variables. + +`TAP_GREP` is a `\n`-delimited list of patterns. + +`TAP_GREP_INVERT` can be set to `'1'` to invert the meaning of grep +matches. + +For example: + +``` +$ TAP_GREP=$'first\napple' node mytest.js +``` + +Output: + +```tap +TAP version 13 +# Subtest: first + # Subtest: apple + ok 1 - apples are tasty + 1..1 + ok 1 - apple # time=4.35ms + + ok 2 - banana # SKIP filter: /apple/ + 1..2 + # skip: 1 +ok 1 - first # time=10.378ms + +ok 2 - second # SKIP filter: /first/ +1..2 +# skip: 1 +# time=15.818ms +``` + +To invert the matches: + +``` +$ TAP_GREP_INVERT=1 TAP_GREP=$'first\nfine' node mytest.js | pbcopy +``` + +```tap +TAP version 13 +ok 1 - first # SKIP filter out: /first/ +# Subtest: second + ok 1 - this is fine # SKIP filter out: /fine/ + # Subtest: i am ok with how things are proceeding + ok 1 - therefor I am + 1..1 + ok 2 - i am ok with how things are proceeding # time=3.117ms + + 1..2 + # skip: 1 +ok 2 - second # time=9.441ms + +1..2 +# skip: 1 +# time=21.761ms +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..56a4ae3e0 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,155 @@ +--- +layout: layout +--- + +# node-tap + +A full-featured mature TAP-based test framework for Node.js. + +Install it by running: + +``` +npm install --save-dev tap +``` + +_Just wanna see some code? [Get started!](/basics/)_ + +`tap` includes out of the box: + +1. [A test framework](/api/) for writing tests in Node.js. +2. [A command-line interface](/cli/) for running tests and + reporting on their success or failure. +3. [Support for test-coverage](/coverage/), including coverage of + child processes, and integration with Coveralls.io and Codecov.io. +4. [Support for parallel tests](/parallel/), including running the + some tests in parallel, and others serially. + +See [the changelog](/changelog/) for recent updates, or just get +started with [the basics](/basics/). + +[![Build Status](https://travis-ci.org/tapjs/node-tap.svg?branch=master)](https://travis-ci.org/tapjs/node-tap) + +## Why TAP? + +Why should you use this thing!? **LET ME TELL YOU!** + +Just kidding. + +Most frameworks spend a lot of their documentation telling you why +they're the greatest. I'm not going to do that. + +### tutti i gusti, sono gusti + +Software testing is a software and user experience design challenge +that balances on the intersection of many conflicting demands. + +Node-tap is based on [my](http://izs.me) opinions about how a test +framework should work, and what it should let you do. I do _not_ have +any opinion about whether or not you share those opinions. If you do +share them, you will probably enjoy this test library. + +1. **Test files should be "normal" programs that can be run + directly.** + + That means that it can't require a special runner that puts magic + functions into a global space. `node test.js` is a perfectly ok + way to run a test, and it ought to function exactly the same as + when it's run by the fancy runner with reporting and such. + JavaScript tests should be JavaScript programs; not + english-language poems with weird punctuation. + +2. **Test output should be connected to the structure of the test file + in a way that is easy to determine.** + + That means not unnecessarily deferring test functions until + `nextTick`, because that would shift the order of `console.log` + output. Synchronous tests should be synchronous. + +3. **Test files should be run in separate processes.** + + That means that it can't use `require()` to load test files. Doing + `node ./test.js` must be the exact same sort of environment for the + test as doing `test-runner ./test.js`. Doing `node test/1.js; node + test/2.js` should be equivalent (from the test's point of view) to + doing `test-runner test/*.js`. This prevents tests from becoming + implicitly dependent on one anothers' globals. + +4. **Assertions should not normally throw (but throws MUST be handled + nicely).** + + I frequently write programs that have many hundreds of assertions + based on some list of test cases. If the first failure throws, + then I don't know if I've failed 100 tests or 1, without wrapping + everything in a try-catch. Furthermore, I usually want to see some + kind of output or reporting to verify that each one actually ran. + + Basically, it should be your decision whether you want to throw or + not. The test framework shouldn't force that on you, and should + make either case easy. + +5. **Test reporting should be separate from the test process, included + in the framework, and enabled by default for humans.** + + The [raw test output](/tap-format/) should be machine-parseable and + human-intelligible, and a separate process should consume test + output and turn it into a [pretty summarized report](/reporting/). + This means that test data can be stored and parsed later, dug into + for additional details, and so on. Also: nyan cat. + +6. **Writing tests should be easy, maybe even fun.** + + The lower the barrier to entry for writing new tests, the more + tests get written. That means that there should be a relatively + small vocabulary of actions that I need to remember as a test + author. There is no benefit to having a distinction between a + "suite" and a "subtest". Fancy DSLs are pretty, but more to + remember. + + That being said, if the you returns a Promise, or use a DSL that + throws a decorated error, then the test framework should Just Work + in a way that helps a human being understand the situation. + +7. **Tests should output enough data to diagnose a failure, and no + more or less.** + + Stack traces pointing at JS internals or the guts of the test + framework itself are not helpful. A test framework is a serious UX + challenge, and should be treated with care. + +8. **Test coverage should be included.** + + Running tests with coverage changes the way that you think about + your programs, and provides much deeper insight. Node-tap bundles + [nyc](https://istanbul.js.org/) for this. + + It's not enabled by default only because it _does_ necessarily + change the nature of the environment a little bit. But I strongly + encourage [enabling coverage](/coverage/). Just throw + [`--cov`](/coverage/) onto your test invocation (or + [`--100`](/100/) if you're up to the challenge). + +9. **Tests should be output in a predictable order.** + + Even if they are run in parallel, the test _output_ should be + consistent. + + As of version 10, tap supports [parallel tests](/parallel/), which + can make your tests run significantly faster if they are I/O bound + or if you have multiple cores on your machine. However, even when + run in parallel, the output is still serialized. + +10. **Tests should not require more building than your code.** + + Babel and Webpack are lovely and fine. But if your code doesn't + require compilation, then I think your tests shouldn't either. + Tap is extremely [promise-aware](/promises/). + +Software testing should help you build software. It should be a +security blanket and a quality ratchet, giving you the support to +undertake massive refactoring and fix bugs without worrying. It +shouldn't be a purification rite or a hazing ritual. + +There are many opinions left off of this list! Reasonable people can +disagree. But if you find yourself nodding along, [maybe tap is for +you](/basics/). diff --git a/docs/mochalike/index.md b/docs/mochalike/index.md new file mode 100644 index 000000000..6d5e595c2 --- /dev/null +++ b/docs/mochalike/index.md @@ -0,0 +1,117 @@ +--- +layout: layout +--- + +# Mocha-like DSL + +If you prefer to use a BDD-style DSL like +[mocha](http://mochajs.org/) instead of the traditional +`t.whatever()`, tap lets you do that! + +You can do this by using the methods on the `tap.mocha` object, or +dump them into the global namespace using `tap.mochaGlobals()`. + +So, instead of this: + +```javascript +// tap.js +var t = require('tap') +t.test('Array.indexOf', function (t) { + var array = [1, 2, 3] + t.test('when item is not found', function (t) { + t.test('does not throw an error', function (t) { + array.indexOf(4) + t.end() + }) + t.equal(array.indexOf(4), -1, 'returns -1') + t.end() + }) + t.end() +}) +``` + +You can do this: + +```javascript +// bdd.js +require('tap').mochaGlobals() +var should = require('should') +describe('Array.indexOf', function () { + var array = [1, 2, 3] + context('when item is not found', function () { + it('does not throw an error', function () { + array.indexOf(4) + }) + it('returns -1', function () { + array.indexOf(4).should.equal(-1) + }) + }) +}) +``` + +Running these with the `spec` reporter results in this output: + +``` +$ tap -Rspec tap.js bdd.js + +tap.js + Array.indexOf + when item is not found + ✓ does not throw an error + ✓ returns -1 + +bdd.js + Array.indexOf + when item is not found + ✓ does not throw an error + ✓ returns -1 + + + 4 passing (527.355ms) +``` + +The following functions are provided: + +* `describe(function () {})` + + Defines a test suite. Runs synchronously. + +* `context(function () {})` + + Alias for `describe`. + +* `it(function ([done]) {})` + + Defines a test block. As long as nothing throws, it is considered + passing. + + If a `Promise` is returned, then it'll wait for the Promise to + resolve before continuing to the next test block. + + If the function takes an argument, then it'll get a callback which + must be called to signal that the test block is complete. + + If the function does not take an argument, and does not return a + Promise, then it is assumed to be done immediately. + +* `before(function ([done]) {})` + + Similar to `it`, but doesn't get reported. Run immediately. + +* `after(function ([done]) {})` + + Similar to `it`, but doesn't get reported. Run after all test + blocks are completed. + +* `beforeEach(function ([done]) {})` + + Run before each test block. + +* `afterEach(function ([done]) {})` + + Run after each test block. + +Using the mocha-like BDD interface defines tests hanging off of the +root `tap` object, so tests defined in this way will always start at +the "top level", even if they are defined within a `t.test(...)` +function. diff --git a/docs/only/index.md b/docs/only/index.md new file mode 100644 index 000000000..f5326bc12 --- /dev/null +++ b/docs/only/index.md @@ -0,0 +1,200 @@ +--- +layout: layout +--- + +# Filtering Tests with Only Option + +Child tests can be filtered by setting the `only` flag in the options +object, or by using the `t.only` method. + +Because tests are run top-to-bottom synchronously, there's no way to +know at the start of a test file if a `t.only()` call is coming. To +activate this filtering, use the `--only` flag to the tap +[command-line interface](/cli/), or set `TAP_ONLY=1` in the +environment, or set the `t.runOnly = true` in a test file. + +Setting the `TAP_ONLY=1` environment variable or using `tap --only` +will restrict the root tap object from running tests that aren't +flagged as "only". To filter deeper in a test suite, set +`t.runOnly = true` at the appropriate level. + +Note: this is for filtering test functions within a test file. If you +want to run only one _file_, just pass the appropriate argument to the +`tap` executable. That is, instead of `tap test/*.js`, do `tap +test/foo.js` to just run a single file. + +Also, this only filters _subtests_. Individual assertions will always +be emitted if they are encountered. + +## Example + +Consider this test file: + +```js +const t = require('tap') + +t.only('only run this test', function (t) { + // all tests in here will be run + t.pass('this is fine') + + t.test('first child', function (t) { + t.pass('got here') + t.end() + }) + + t.test('second child', function (t) { + t.pass('got here, too') + t.end() + }) + + t.end() +}) + +t.test('a second top level test', function (t) { + t.pass('second top level test assertion') + t.end() +}) +``` + +If run with `node mytest.js`, it'll produce this output: + +```tap +TAP version 13 +# "only run this test" has `only` set but all tests run +# Subtest: only run this test + ok 1 - this is fine + # Subtest: first child + ok 1 - got here + 1..1 + ok 2 - first child # time=2.352ms + + # Subtest: second child + ok 1 - got here, too + 1..1 + ok 3 - second child # time=0.48ms + + 1..3 +ok 1 - only run this test # time=11.58ms + +# Subtest: a second top level test + ok 1 - second top level test assertion + 1..1 +ok 2 - a second top level test # time=0.337ms + +1..2 +# time=26.044ms +``` + +If run with `TAP_ONLY=1 node mytest.js`, then we see this instead: + +```tap +TAP version 13 +# Subtest: only run this test + ok 1 - this is fine + # Subtest: first child + ok 1 - got here + 1..1 + ok 2 - first child # time=3.062ms + + # Subtest: second child + ok 1 - got here, too + 1..1 + ok 3 - second child # time=0.577ms + + 1..3 +ok 1 - only run this test # time=15.972ms + +ok 2 - a second top level test # SKIP filter: only +1..2 +# skip: 1 +# time=24.457ms +``` + +Note that the second test was skipped. + +To only show the first child in the first test block, we could do +this: + +```js +const t = require('../tap') + +t.only('only run this test', function (t) { + // only run tests here with t.only() + t.runOnly = true + t.pass('this is fine') + + t.only('first child', function (t) { + t.pass('got here') + t.end() + }) + + t.test('second child', function (t) { + t.pass('got here, too') + t.end() + }) + + t.end() +}) + +t.test('a second top level test', function (t) { + t.pass('second top level test assertion') + t.end() +}) +``` + +Now when we run the test, we see this: + +```tap +TAP version 13 +# Subtest: only run this test + ok 1 - this is fine + # Subtest: first child + ok 1 - got here + 1..1 + ok 2 - first child # time=1.609ms + + ok 3 - second child # SKIP filter: only + 1..3 + # skip: 1 +ok 1 - only run this test # time=8.585ms + +ok 2 - a second top level test # SKIP filter: only +1..2 +# skip: 1 +# time=14.176ms +``` + +Note that the second child test was skipped along with the second top +level test. + +To get the same results with the tap command line, you can do this: + +``` +$ tap --only mytest.js +mytest.js ............................................. 2/4 + Skipped: 2 + +total ................................................. 2/4 + + 2 passing (277.134ms) + 2 pending +``` + +Using the `spec` reporter will show more detail about the tests being +skipped and run: + +``` +$ tap --only mytest.js -Rspec + +mytest.js + only run this test + ✓ this is fine + first child + ✓ got here + - second child + + - a second top level test + + 2 passing (231.681ms) + 2 pending +``` diff --git a/docs/parallel/index.md b/docs/parallel/index.md new file mode 100644 index 000000000..f2622829a --- /dev/null +++ b/docs/parallel/index.md @@ -0,0 +1,175 @@ +--- +layout: layout +--- + +# Parallel Tests + +Node-tap includes the ability to run buffered child tests in parallel. +There are two ways that this can be done: either via the command line +interface, or within a single test program. + +In both cases, you set a number of `jobs` that you want to allow it to +run in parallel, and then any buffered tests are run in a pool which +will execute that many test functions in parallel. (The default +`jobs` value is 1, which means that nothing is parallel by default.) + +## Parallel tests from the CLI + +This is the simplest way to run parallel tests. Just add `--jobs=` +to your test command (or `-j` if you prefer shorthands). + +You'll note that if you do this, it seems like the output from each +test file happens "all at once", when the test completes. That's +because parallel tests are always buffered, so the command-line +harness doesn't parse their output until they're fully complete. +(Since many of them may be running at once, it would be very confusing +otherwise.) + +### Enabling/Disabling Parallelism in the test runner + +If you set the `--jobs` option, then tests will be run in parallel by +default. + +However, you may want to have _some_ tests run in parallel, and make +others run serially. + +To prevent any tests in a given folder from running in parallel, add a +file to that directory named `tap-parallel-not-ok`. This will prevent +tests from being run in parallel in that folder or any sub-folders. + +To re-enable parallel tests in a given folder and its subfolders, +create a file named `tap-parallel-ok`. This is only required if +parallel tests had been disabled in a parent directory. + +For example, if you had this folder structure: + +``` +test +├── parallel/ +│   ├── all-my-ports-are-private-and-unique.js +│   ├── isolated.js +│   ├── no-external-deps.js +│   └── tap-parallel-ok +├── bar.js +├── foo.js +└── tap-parallel-not-ok +``` + +then running `tap -j4 test/` would cause it to run `bar.js` and +`foo.js` serially, and the contents of the `test/parallel/` folder +would be run in parallel. + +As test files are updated to be parallel-friendly (ie, not listening +on the same ports as any other tests, not depending on external +filesystem stuff, and so on), then they can be moved into the +`parallel` subfolder. + +## Parallel tests from the API + +To run child tests in parallel, set `t.jobs = ` in your +test program. This can be set either on the root tap object, or on +any child test. + +If `t.jobs` is set to a number greater than 1, then tests will be run +in `buffered` mode by default. To force a test to be serialized, set +`{ buffered: false }` in its options. You may also set +`TAP_BUFFER=0` in the environment to make tests non-buffered by +default. + +For example, imagine that you had some slow function that makes a +network request or processes a file or something, and you want to call +this function three times in your test program. + +```javascript +var t = require('tap') + +t.test(function one (t) { + someSlowFunction(function () { + t.pass('one worked') + t.end() + }) +}) + +t.test(function two (t) { + someSlowFunction(function () { + t.pass('two worked') + t.end() + }) +}) + +t.test(function three (t) { + someSlowFunction(function () { + t.pass('three worked') + t.end() + }) +}) +``` + +That produces this output: + +```tap +TAP version 13 +# Subtest: one + ok 1 - one worked + 1..1 +ok 1 - one # time=283.987ms + +# Subtest: two + ok 1 - two worked + 1..1 +ok 2 - two # time=352.492ms + +# Subtest: three + ok 1 - three worked + 1..1 +ok 3 - three # time=313.015ms + +1..3 +# time=957.096ms +``` + +If we update our test function to add `t.jobs = 3`, then the output +looks like this instead: + +```tap +TAP version 13 +ok 1 - one # time=215.87ms { + ok 1 - one worked + 1..1 +} + +ok 2 - two # time=97.694ms { + ok 1 - two worked + 1..1 +} + +ok 3 - three # time=374.099ms { + ok 1 - three worked + 1..1 +} + +1..3 +# time=382.468ms +``` + +Each test still takes a few hundred ms, but the overall time is way +down. Also, they're using the more streamlined `buffered` subtest +style, so that they can run in parallel. + +## Caveats about Parallel Tests + +Parallelism is not a magic sauce that makes everything go fast. + +It's a good fit when your test spends a lot of time waiting in sleep +mode (for example, waiting for I/O to complete), or if you have lots +of CPUs on your computer. But if your tests are on-CPU most of the +time, then there's often little benefit to running more of them than +you have CPUs available. + +Parallel testing also means that your tests have to be written in an +independent way. They can't depend on being run in a given order, +which means it's a bad idea to have them share pretty much _any_ state +at all. + +Experiment with where in your test suite it makes sense to throw a +little bit of parallelism. diff --git a/docs/params.json b/docs/params.json new file mode 100644 index 000000000..cf93988a6 --- /dev/null +++ b/docs/params.json @@ -0,0 +1 @@ +{"name":"Node TAP","tagline":"Test Anything Protocol tools for node","body":"[![Build Status](https://travis-ci.org/tapjs/node-tap.svg?branch=master)](https://travis-ci.org/tapjs/node-tap/) [![Build Status](https://ci.appveyor.com/api/projects/status/913p1ypf21gf4leu?svg=true)](https://ci.appveyor.com/project/isaacs/node-tap) [![Coverage Status](https://coveralls.io/repos/tapjs/node-tap/badge.svg?branch=master&service=github)](https://coveralls.io/github/tapjs/node-tap?branch=master)\r\n\r\nIncludes a command line test runner for consuming TAP-generating\r\ntest scripts, and a JavaScript framework for writing such scripts.\r\n\r\nBuilt-in support for code coverage (including instrumenting child\r\nprocesses). Coverage is printed to the command line in a terse table\r\nby default, but tap can also open up your web browser to a pretty\r\nreport if you add `--coverage-report=lcov` to the command.\r\n\r\nWorks with all exception-throwing assertion libraries (chai, should,\r\nnode's built-in `require('assert')`, or just throwing yourself) but\r\nalso has a [huge library of built-in assertions](#asserts) that you\r\ncan use if you want to have each one reported as successes.\r\n\r\nOutputs in a wide variety of formats using the\r\n[tap-mocha-reporter](http://npm.im/tap-mocha-reporter) module. (That\r\nis, you can get spec output by doing `-Rspec`. The default output is\r\ncalled 'classic', based on tap 0.x's output, but with color and timing\r\ninfo.)\r\n\r\n## USAGE\r\n\r\nWrite your tests in JavaScript\r\n\r\n```javascript\r\nvar tap = require('tap')\r\n\r\n// you can test stuff just using the top level object.\r\n// no suites or subtests required.\r\n\r\ntap.equal(1, 1, 'check if numbers still work')\r\ntap.notEqual(1, 2, '1 should not equal 2')\r\n\r\n// also you can group things into sub-tests.\r\n// Sub-tests will be run in sequential order always,\r\n// so they're great for async things.\r\n\r\ntap.test('first stuff', function (t) {\r\n t.ok(true, 'true is ok')\r\n t.similar({a: [1,2,3]}, {a: [1,2,3]})\r\n // call t.end() when you're done\r\n t.end()\r\n})\r\n\r\n// If you have a bunch of setup stuff that MUST work or else\r\n// the rest of the tests are not worth running, then you can\r\n// pass `{ bail: true }` to make it bail out on failure.\r\n\r\ntap.test('must succeed or all is lost', { bail: true }, function (t) {\r\n db = new DataBorscht()\r\n t.ok(db, 'borscht setup must succeed')\r\n t.end()\r\n})\r\n\r\n// You can also bail out based on specific conditions, or with a\r\n// different error message of your choosing.\r\ntap.test('must mostly succeed or all is lost', function (t) {\r\n db = new DataBorscht()\r\n\r\n t.ok(db, 'borscht setup')\r\n if (!db) {\r\n t.bailout('the borscht is lost. I cannot continue.')\r\n return\r\n }\r\n\r\n t.ok(db.connection, 'db must have connection')\r\n t.ok(db.username, 'db must have username')\r\n t.equal(db.color, 'red', 'borscht should be red')\r\n if (!t.passing())\r\n t.bailout('something weird with the data borscht.')\r\n\r\n t.end()\r\n})\r\n\r\n// you can specify a 'plan' if you know how many\r\n// tests there will be in advance. Handy when\r\n// order is irrelevant and things happen in parallel.\r\n\r\n// Note that the function name is used if no name is provided!\r\ntap.test(function planned (t) {\r\n t.plan(2)\r\n setTimeout(function () {\r\n t.ok(true, 'a timeout')\r\n })\r\n setTimeout(function () {\r\n t.ok(true, 'b timeout')\r\n })\r\n})\r\n\r\n// you can do `var test = require('tap').test` if you like\r\n// it's pre-bound to the root tap object.\r\n\r\nvar test = require('tap').test\r\n\r\n// subtests can have subtests\r\ntest(function parent (t) {\r\n t.test(function child (tt) {\r\n tt.throws(function () {\r\n throw new Error('fooblz')\r\n }, {\r\n message: 'fooblz'\r\n }, 'throw a fooblz')\r\n\r\n // throws also uses function name if no name provided\r\n tt.throws(function throw_whatever () { throw 1 })\r\n\r\n tt.end()\r\n })\r\n\r\n t.end()\r\n})\r\n\r\n// thrown errors just fail the current test, so you can\r\n// also use your own assert library if you like.\r\n// Of course, this means it won't be able to print out the\r\n// number of passing asserts, since passes will be silent.\r\n\r\ntest('my favorite assert lib', function (t) {\r\n var assert = require('assert')\r\n assert.ok(true, 'true is ok')\r\n assert.equal(1, 1, 'math works')\r\n\r\n // Since it can't read the plan, using a custom assert lib\r\n // means that you MUST use t.end()\r\n t.end()\r\n})\r\n\r\n// You can mark tests as 'todo' either using a conf object,\r\n// or simply by omitting the callback.\r\ntest('solve halting problem')\r\ntest('prove p=np', { todo: true }, function (t) {\r\n // i guess stuff goes here\r\n t.fail('traveling salesmen must pack their own bags')\r\n t.end()\r\n})\r\n\r\n// Prefer mocha/rspec/lab style global objects?\r\n// Got you covered. This is a little experimental,\r\n// patches definitely welcome.\r\ntap.mochaGlobals()\r\ndescribe('suite ride bro', function () {\r\n it('should have a wheel', function () {\r\n assert.ok(thingie.wheel, 'wheel')\r\n })\r\n it('can happen async', function (done) {\r\n setTimeout(function () {\r\n assert.ok('ok')\r\n done()\r\n })\r\n })\r\n})\r\n\r\n// Read on for a complete list of asserts, methods, etc.\r\n```\r\n\r\nYou can run tests using the `tap` executable. Put this in your\r\npackage.json file:\r\n\r\n```json\r\n{\r\n \"scripts\": {\r\n \"test\": \"tap test/*.js\"\r\n }\r\n}\r\n```\r\n\r\nand then you can run `npm test` to run your test scripts.\r\n\r\nCommand line behavior and flags:\r\n\r\n```\r\n$ tap -h\r\nUsage:\r\n tap [options] \r\n\r\nExecutes all the files and interprets their output as TAP\r\nformatted test result data.\r\n\r\nTo parse TAP data from stdin, specify \"-\" as a filename.\r\n\r\nShort options are parsed gnu-style, so for example '-bCRspec' would be\r\nequivalent to '--bail --no-color --reporter=spec'\r\n\r\nOptions:\r\n\r\n -c --color Use colors (Default for TTY)\r\n\r\n -C --no-color Do not use colors (Default for non-TTY)\r\n\r\n -b --bail Bail out on first failure\r\n\r\n -B --no-bail Do not bail out on first failure (Default)\r\n\r\n -R --reporter= Use the specified reporter. Defaults to\r\n 'classic' when colors are in use, or 'tap'\r\n when colors are disabled.\r\n\r\n Available reporters:\r\n classic doc dot dump html htmlcov json\r\n jsoncov jsonstream landing list markdown\r\n min nyan progress silent spec tap xunit\r\n\r\n -s --save= If exists, then it should be a line-\r\n delimited list of test files to run. If\r\n is not present, then all command-line\r\n positional arguments are run.\r\n\r\n After the set of test files are run, any\r\n failed test files are written back to the\r\n save file.\r\n\r\n This way, repeated runs with -s will\r\n re-run failures until all the failures are\r\n passing, and then once again run all tests.\r\n\r\n It's a good idea to .gitignore the file\r\n used for this purpose, as it will churn a\r\n lot.\r\n\r\n --coverage --cov Capture coverage information using 'nyc'\r\n\r\n If a COVERALLS_REPO_TOKEN environment\r\n variable is set, then coverage is\r\n captured by default and sent to the\r\n coveralls.io service. If a CODECOV_TOKEN\r\n environment variable is set, then coverage is\r\n captured by default and sent to the\r\n codecov.io service.\r\n\r\n --no-coverage --no-cov Do not capture coverage information.\r\n Note that if nyc is already loaded, then\r\n the coverage info will still be captured.\r\n\r\n --coverage-report= Output coverage information using the\r\n specified istanbul/nyc reporter type.\r\n\r\n Default is 'text' when running on the\r\n command line, or 'text-lcov' when piping\r\n to coveralls or codecov.\r\n\r\n If 'lcov' is used, then the report will\r\n be opened in a web browser after running.\r\n\r\n This can be run on its own at any time\r\n after a test run that included coverage.\r\n\r\n -t --timeout= Time out test files after seconds.\r\n Defaults to 30, or the value of the\r\n TAP_TIMEOUT environment variable.\r\n\r\n -h --help print this thing you're looking at\r\n\r\n -v --version show the version of this program\r\n\r\n -gc --expose-gc Expose the gc() function to Node tests\r\n\r\n --debug Run JavaScript tests with node --debug\r\n\r\n --debug-brk Run JavaScript tests with node --debug-brk\r\n\r\n --harmony Enable all Harmony flags in JavaScript tests\r\n\r\n --strict Run JS tests in 'use strict' mode\r\n\r\n -- Stop parsing flags, and treat any additional\r\n command line arguments as filenames.\r\n```\r\n\r\n## Coverage\r\n\r\nThis module uses [nyc](http://npm.im/nyc) to track code coverage, even\r\nacross subprocess boundaries. It is included by default, and there's\r\nnothing you need to do but enable it. Adding coverage *will* make\r\nyour tests run slightly slower, but that's to be expected.\r\n\r\nTo generate coverage information, run your tests with the `--cov`\r\nargument.\r\n\r\nTo specify a report format, you can use `--coverage-report=`.\r\nThe default type is `text`, which produces a pretty text-only table on\r\nthe terminal. If you specify `--coverage-report=lcov`, then tap will\r\nattempt to open a web browser to view the report after the test run.\r\n\r\nIf you use this a lot, you may want to add `coverage` and\r\n`.nyc_output` to your `.gitignore` and/or `.npmignore` files.\r\n\r\n### Travis-CI and Coveralls.io/CodeCov.io Integration\r\n\r\nYou can very easily take advantage of continuous test coverage reports\r\nby using [Travis-CI](https://travis-ci.org) and\r\n[Coveralls](https://coveralls.io).\r\n\r\n1. Enable Travis-CI by signing up, enabling tests on your repo, and\r\n adding a `.travis.yml` file to your repo. You can use [this\r\n module's .travis.yml file as an\r\n example](https://github.com/isaacs/node-tap/blob/master/.travis.yml)\r\n2. Enable Coveralls.io or CodeCov.io by signing up, and adding the\r\n repo. Note the repo API token.\r\n3. Back at Travis-CI, add a private environment variable. The name of\r\n the environment variable is `COVERALLS_REPO_TOKEN` for Coveralls,\r\n or `CODECOV_TOKEN` for CodeCov.io, and the value is the token you\r\n got from Coveralls or CodeCov.\r\n4. When that token is set in the environment variable, `tap` will\r\n automatically generate coverage information and send it to the\r\n appropriate place.\r\n\r\n## API\r\n\r\n### tap = require('tap')\r\n\r\nThe root `tap` object is an instance of the Test class with a few\r\nslight modifications.\r\n\r\n1. The `tearDown()`, `plan()`, and `test()` methods are pre-bound onto\r\n the root object, so that you don't have to call them as methods.\r\n2. By default, it pipes to stdout, so running a test directly just\r\n dumps the TAP data for inspection. (You can of course\r\n `tap.unpipe(process.stdout)` if you want to direct it elsewhere.)\r\n3. Various other things are hung onto it for convenience, since it is\r\n the main package export.\r\n4. The test ends automatically when `process.on('exit')` fires, so\r\n there is no need to call `tap.end()` explicitly.\r\n5. Adding a `tearDown` function triggers `autoend` behavior.\r\n Otherwise, the `end` would potentially never arrive, if for example\r\n `tearDown` is used to close a server or cancel some long-running\r\n process, because `process.on('exit')` would never fire of its own\r\n accord.\r\n\r\n### tap.synonyms\r\n\r\nA list of all of the canonical assert methods and their synonyms.\r\n\r\n### tap.mochaGlobals()\r\n\r\nMethod that injects `describe()` and `it()` into the global\r\nenvironment for mocha-like BDD style test definition.\r\n\r\nThis feature is incomplete, experimental, and may change drastically\r\nin the future. Feedback is welcome.\r\n\r\n### tap.Test\r\n\r\nThe `Test` class is the main thing you'll be touching when you use\r\nthis module.\r\n\r\nThe most common way to instantiate a `Test` object by calling the\r\n`test` method on the root or any other `Test` object. The callback\r\npassed to `test(name, fn)` will receive a child `Test` object as its\r\nargument.\r\n\r\nA `Test` object is a Readable Stream. Child tests automatically send\r\ntheir data to their parent, and the root `require('tap')` object pipes\r\nto stdout by default. However, you can instantiate a `Test` object\r\nand then pipe it wherever you like. The only limit is your imagination.\r\n\r\n#### t.test([name], [options], [function])\r\n\r\nCreate a subtest.\r\n\r\nIf the function is omitted, then it will be marked as a \"todo\" or\r\n\"pending\" test.\r\n\r\nIf the function has a name, and no name is provided, then the function\r\nname will be used as the test name. If no test name is provided, then\r\nthe name will be `(unnamed test)`.\r\n\r\nThe function gets a Test object as its only argument. From there, you\r\ncan call the `t.end()` method on that object to end the test, or use\r\nthe `t.plan()` method to specify how many child tests or asserts the\r\ntest will have.\r\n\r\nIf the function returns a `Promise` object (that is, an object with a\r\n`then` method), then when the promise is rejected or fulfilled, the\r\ntest will be either ended or failed.\r\n\r\nIf the function is not provided, then this will be treated as a `todo`\r\ntest.\r\n\r\nThe options object is the same as would be passed to any assert, with\r\ntwo additional fields that are only relevant for child tests:\r\n\r\n* `timeout`: The number of ms to allow the test to run.\r\n* `bail`: Set to `true` to bail out on the first test failure.\r\n* `autoend`: Automatically `end()` the test on the next turn of the\r\n event loop after its internal queue is drained.\r\n\r\n#### t.tearDown(function)\r\n\r\nRun the supplied function when `t.end()` is called, or when the `plan`\r\nis met.\r\n\r\nNote that when called on the root `tap` export, this also triggers\r\n`autoend` behavior.\r\n\r\n#### t.autoend()\r\n\r\nAutomatically end the test as soon as there is nothing pending in its\r\nqueue.\r\n\r\nThe automatic end is deferred with a `setTimeout`, and any new action\r\nwill cancel and re-schedule the timer. Nonetheless, calling this\r\nmethod means that any slow asynchronous behavior may be lost, if it\r\ncomes after the `end()` is auto-triggered.\r\n\r\nThis behavior is triggered on the root `tap` object when\r\n`tap.tearDown()` is called.\r\n\r\n#### t.plan(number)\r\n\r\nSpecify that a given number of tests are going to be run.\r\n\r\nThis may only be called *before* running any asserts or child tests.\r\n\r\n#### t.end()\r\n\r\nCall when tests are done running. This is not necessary if `t.plan()`\r\nwas used, or if the test function returns a Promise.\r\n\r\nIf you call `t.end()` explicitly more than once, an error will be\r\nraised.\r\n\r\n#### t.bailout([reason])\r\n\r\nPull the proverbial ejector seat.\r\n\r\nUse this when things are severely broken, and cannot be reasonably\r\nhandled. Immediately terminates the entire test run.\r\n\r\n#### t.passing()\r\n\r\nReturn true if everything so far is ok.\r\n\r\nNote that all assert methods also return `true` if they pass.\r\n\r\n#### t.comment(message)\r\n\r\nPrint the supplied message as a TAP comment.\r\n\r\nNote that you can always use `console.error()` for debugging (or\r\n`console.log()` as long as the message doesn't look like TAP formatted\r\ndata).\r\n\r\n#### t.fail(message, extra)\r\n\r\nEmit a failing test point. This method, and `pass()`, are the basic\r\nbuilding blocks of all fancier assertions.\r\n\r\n#### t.pass(message)\r\n\r\nEmit a passing test point. This method, and `fail()`, are the basic\r\nbuilding blocks of all fancier assertions.\r\n\r\n#### t.pragma(set)\r\n\r\nSets a `pragma` switch for a set of boolean keys in the argument.\r\n\r\nThe only pragma currently supported by the TAP parser is `strict`,\r\nwhich tells the parser to treat non-TAP output as a failure.\r\n\r\nExample:\r\n\r\n```\r\nvar t = require('tap')\r\nconsole.log('this non-TAP output is ok')\r\nt.pragma({ strict: true })\r\nconsole.log('but this will cause a failure')\r\n```\r\n\r\n### Asserts\r\n\r\nThe `Test` object has a collection of assertion methods, many of which\r\nare given several synonyms for compatibility with other test runners\r\nand the vagaries of human expectations and spelling. When a synonym\r\nis multi-word in `camelCase` the corresponding lower case and\r\n`snake_case` versions are also created as synonyms.\r\n\r\nAll assertion methods take optional `message` and `extra` arguments as\r\nthe last two params. The `message` is the name of the test. The\r\n`extra` argument can contain any arbitrary data about the test, but\r\nthe following fields are \"special\".\r\n\r\n* `todo` Set to boolean `true` or a String to mark this as pending\r\n* `skip` Set to boolean `true` or a String to mark this as skipped\r\n* `at` Generated by the framework. The location where the assertion\r\n was called. Do not set this field.\r\n* `stack` Generated by the framework. The stack trace to the point\r\n where the assertion was called. Do not set this field.\r\n\r\n#### t.ok(obj, message, extra)\r\n\r\nVerifies that the object is truthy.\r\n\r\nSynonyms: `t.true`, `t.assert`\r\n\r\n#### t.notOk(obj, message, extra)\r\n\r\nVerifies that the object is not truthy.\r\n\r\nSynonyms: `t.false`, `t.assertNot`\r\n\r\n#### t.error(obj, message, extra)\r\n\r\nIf the object is an error, then the assertion fails.\r\n\r\nNote: if an error is encountered unexpectedly, it's often better to\r\nsimply throw it. The Test object will handle this as a failure.\r\n\r\nSynonyms: `t.ifErr`, `t.ifError`\r\n\r\n#### t.throws(fn, [expectedError], message, extra)\r\n\r\nExpect the function to throw an error. If an expected error is\r\nprovided, then also verify that the thrown error matches the expected\r\nerror.\r\n\r\nIf the function has a name, and the message is not provided, then the\r\nfunction name will be used as the message.\r\n\r\nIf the function is not provided, then this will be treated as a `todo`\r\ntest.\r\n\r\nCaveat: if you pass a `extra` object to t.throws, then you MUST also\r\npass in an expected error, or else it will read the diag object as the\r\nexpected error, and get upset when your thrown error doesn't match\r\n`{skip:true}` or whatever.\r\n\r\nFor example, this will not work as expected:\r\n\r\n```javascript\r\nt.throws(function() {throw new Error('x')}, { skip: true })\r\n```\r\n\r\nBut this is fine:\r\n\r\n```javascript\r\n// note the empty 'expected error' object.\r\n// since it has no fields, it'll only verify that the thrown thing is\r\n// an object, not the value of any properties\r\nt.throws(function() {throw new Error('x')}, {}, { skip: true })\r\n```\r\n\r\nThe expected error is tested against the throw error using `t.match`,\r\nso regular expressions and the like are fine. If the expected error\r\nis an `Error` object, then the `stack` field is ignored, since that\r\nwill generally never match.\r\n\r\nSynonyms: `t.throw`\r\n\r\n#### t.doesNotThrow(fn, message, extra)\r\n\r\nVerify that the provided function does not throw.\r\n\r\nIf the function has a name, and the message is not provided, then the\r\nfunction name will be used as the message.\r\n\r\nIf the function is not provided, then this will be treated as a `todo`\r\ntest.\r\n\r\nNote: if an error is encountered unexpectedly, it's often better to\r\nsimply throw it. The Test object will handle this as a failure.\r\n\r\nSynonyms: `t.notThrow`\r\n\r\n#### t.equal(found, wanted, message, extra)\r\n\r\nVerify that the object found is exactly the same (that is, `===`) to\r\nthe object that is wanted.\r\n\r\nSynonyms: `t.equals`, `t.isEqual`, `t.is`, `t.strictEqual`,\r\n`t.strictEquals`, `t.strictIs`, `t.isStrict`, `t.isStrictly`\r\n\r\n#### t.notEqual(found, notWanted, message, extra)\r\n\r\nInverse of `t.equal()`.\r\n\r\nVerify that the object found is not exactly the same (that is, `!==`) as\r\nthe object that is wanted.\r\n\r\nSynonyms: `t.inequal`, `t.notEqual`, `t.notEquals`,\r\n`t.notStrictEqual`, `t.notStrictEquals`, `t.isNotEqual`, `t.isNot`,\r\n`t.doesNotEqual`, `t.isInequal`\r\n\r\n#### t.same(found, wanted, message, extra)\r\n\r\nVerify that the found object is deeply equivalent to the wanted\r\nobject. Use non-strict equality for scalars (ie, `==`).\r\n\r\nSynonyms: `t.equivalent`, `t.looseEqual`, `t.looseEquals`,\r\n`t.deepEqual`, `t.deepEquals`, `t.isLoose`, `t.looseIs`\r\n\r\n#### t.notSame(found, notWanted, message, extra)\r\n\r\nInverse of `t.same()`.\r\n\r\nVerify that the found object is not deeply equivalent to the\r\nunwanted object. Uses non-strict inequality (ie, `!=`) for scalars.\r\n\r\nSynonyms: `t.inequivalent`, `t.looseInequal`, `t.notDeep`,\r\n`t.deepInequal`, `t.notLoose`, `t.looseNot`\r\n\r\n#### t.strictSame(found, wanted, message, extra)\r\n\r\nStrict version of `t.same()`.\r\n\r\nVerify that the found object is deeply equivalent to the wanted\r\nobject. Use strict equality for scalars (ie, `===`).\r\n\r\nSynonyms: `t.strictEquivalent`, `t.strictDeepEqual`, `t.sameStrict`,\r\n`t.deepIs`, `t.isDeeply`, `t.isDeep`, `t.strictDeepEquals`\r\n\r\n#### t.strictNotSame(found, notWanted, message, extra)\r\n\r\nInverse of `t.strictSame()`.\r\n\r\nVerify that the found object is not deeply equivalent to the unwanted\r\nobject. Use strict equality for scalars (ie, `===`).\r\n\r\nSynonyms: `t.strictInequivalent`, `t.strictDeepInequal`,\r\n`t.notSameStrict`, `t.deepNot`, `t.notDeeply`, `t.strictDeepInequals`,\r\n`t.notStrictSame`\r\n\r\n#### t.match(found, pattern, message, extra)\r\n\r\nVerify that the found object matches the pattern provided.\r\n\r\nIf pattern is a regular expression, and found is a string, then verify\r\nthat the string matches the pattern.\r\n\r\nIf the pattern is a string, and found is a string, then verify that\r\nthe pattern occurs within the string somewhere.\r\n\r\nIf pattern is an object, then verify that all of the (enumerable)\r\nfields in the pattern match the corresponding fields in the object\r\nusing this same algorithm. For example, the pattern `{x:/a[sdf]{3}/}`\r\nwould successfully match `{x:'asdf',y:'z'}`.\r\n\r\nThis is useful when you want to verify that an object has a certain\r\nset of required fields, but additional fields are ok.\r\n\r\nSynonyms: `t.has`, `t.hasFields`, `t.matches`, `t.similar`, `t.like`,\r\n`t.isLike`, `t.includes`, `t.include`, `t.contains`\r\n\r\n#### t.notMatch(found, pattern, message, extra)\r\n\r\nInterse of `match()`\r\n\r\nVerify that the found object does not match the pattern provided.\r\n\r\nSynonyms: `t.dissimilar`, `t.unsimilar`, `t.notSimilar`, `t.unlike`,\r\n`t.isUnlike`, `t.notLike`, `t.isNotLike`, `t.doesNotHave`,\r\n`t.isNotSimilar`, `t.isDissimilar`\r\n\r\n#### t.type(object, type, message, extra)\r\n\r\nVerify that the object is of the type provided.\r\n\r\nType can be a string that matches the `typeof` value of the object, or\r\nthe string name of any constructor in the object's prototype chain, or\r\na constructor function in the object's prototype chain.\r\n\r\nFor example, all the following will pass:\r\n\r\n```javascript\r\nt.type(new Date(), 'object')\r\nt.type(new Date(), 'Date')\r\nt.type(new Date(), Date)\r\n```\r\n\r\nSynonyms: `t.isa`, `t.isA`\r\n\r\n### Advanced Usage\r\n\r\nThese methods are primarily for internal use, but can be handy in some\r\nunusual situations. If you find yourself using them frequently, you\r\n*may* be Doing It Wrong. However, if you find them useful, you should\r\nfeel perfectly comfortable using them.\r\n\r\nPlease [let us know](https://github.com/isaacs/node-tap/issues) if you\r\nfrequently encounter situations requiring advanced usage, because this\r\nmay indicate a shortcoming in the \"non-advanced\" API.\r\n\r\n#### t.stdin()\r\n\r\nParse standard input as if it was a child test named `/dev/stdin`.\r\n\r\nThis is primarily for use in the test runner, so that you can do\r\n`some-tap-emitting-program | tap other-file.js - -Rnyan`.\r\n\r\n#### t.spawn(command, arguments, [options], [name], [extra])\r\n\r\nSometimes, instead of running a child test directly inline, you might\r\nwant to run a TAP producting test as a child process, and treat its\r\nstandard output as the TAP stream.\r\n\r\nThat's what this method does.\r\n\r\nIt is primarily used by the executable runner, to run all of the\r\nfilename arguments provided on the command line.\r\n\r\nThe `options` object is passed to `child_process.spawn`, and can\r\ncontain stuff like stdio directives and environment vars.\r\n\r\nThe `extra` arg is the same that would be passed to any assertion or\r\nchild test, with the addition of the following fields:\r\n\r\n* `bail`: Set to `true` to bail out on the first failure. This is\r\n done by checking the output and then forcibly killing the process,\r\n but also sets the `TAP_BAIL` environment variable, which node-tap\r\n uses to set this field internally as well.\r\n* `timeout`: The number of ms to allow the child process to continue.\r\n If it goes beyond this time, the child process will be forcibly\r\n killed.\r\n\r\n#### t.addAssert(name, length, fn)\r\n\r\nThis is used for creating assertion methods on the `Test` class.\r\n\r\nIt's a little bit advanced, but it's also super handy sometimes. All\r\nof the assert methods below are created using this function, and it\r\ncan be used to create application-specific assertions in your tests.\r\n\r\nThe name is the method name that will be created. The length is the\r\nnumber of arguments the assertion operates on. (The `message` and\r\n`extra` arguments will alwasy be appended to the end.)\r\n\r\nFor example, you could have a file at `test/setup.js` that does the\r\nfollowing:\r\n\r\n```javascript\r\nvar tap = require('tap')\r\n\r\n// convenience\r\nif (module === require.main) {\r\n tap.pass('ok')\r\n return\r\n}\r\n\r\n// Add an assertion that a string is in Title Case\r\n// It takes one argument (the string to be tested)\r\ntap.Test.prototype.addAssert('titleCase', 1, function (str, message, extra) {\r\n message = message || 'should be in Title Case'\r\n // the string in Title Case\r\n // A fancier implementation would avoid capitalizing little words\r\n // to get `Silence of the Lambs` instead of `Silence Of The Lambs`\r\n // But whatever, it's just an example.\r\n var tc = str.toLowerCase().replace(/\\b./, function (match) {\r\n return match.toUpperCase()\r\n })\r\n\r\n // should always return another assert call, or\r\n // this.pass(message) or this.fail(message, extra)\r\n return this.equal(str, tc, message, extra)\r\n})\r\n```\r\n\r\nThen in your individual tests, you'd do this:\r\n\r\n```javascript\r\nrequire('./setup.js') // adds the assert\r\nvar tap = require('tap')\r\ntap.titleCase('This Passes')\r\ntap.titleCase('however, tHis tOTaLLy faILS')\r\n```\r\n\r\n#### t.endAll()\r\n\r\nCall the `end()` method on all child tests, and then on this one.\r\n\r\n#### t.current()\r\n\r\nReturn the currently active test.\r\n","google":"","note":"Don't delete this file! It's used internally to help with page regeneration."} \ No newline at end of file diff --git a/docs/promises/index.md b/docs/promises/index.md new file mode 100644 index 000000000..47cc9b4d6 --- /dev/null +++ b/docs/promises/index.md @@ -0,0 +1,124 @@ +--- +layout: layout +--- + +# Promises + +The `t.test()`, `t.spawn()` and `t.stdin()` methods all return a +Promise which resolves to the `t` object once the child test, process, +or input stream is done. (Note that the returned Promise does *not* +resolve to the *child* object, because the child is already ended when +control returns to the parent.) + +Additionally, if the function passed to `t.test()` *returns* a +Promise, then the child test will be ended when the Promise resolves, +or failed when it is rejected. + +These two features together mean that you can string together Promise +chains in your tests, if that's a thing you're into. + +If you do use a lot of Promises to chain your tests in a long +declarative list, it's a good idea to put `.catch(t.threw)` at the +end, so that any unhandled rejections will be bubbled up to the top +level handler rather than being ignored or reported in a less helpful +manner. + +Here is an example: + +```javascript +var t = require('tap') +t.test('get thing', function (t) { + return getSomeThing().then(function (result) { + return t.test('check result', function (t) { + t.equal(result.foo, 'bar') + t.end() + }) + }) +}).then(function (t) { + return getTwoThings().then(function (things) { + return t.test('the things', function (t) { + t.equal(things.length, 2) + return makeSomeOtherPromise() + }) + }).then(function (otherPromiseResult) { + return t.test('check other promise thing', function (t) { + t.equal(otherPromiseResult, 7, 'it should be seven') + t.end() + }) + }) +}).catch(t.threw) +``` + +If this sort of style offends you, you are welcome to ignore it. It's +not mandatory. + +If you want to pass Promises to [assertions](/asserts/) and have them +auto-resolve, then check out [tapromise](http://npm.im/tapromise). + +## Rejected promise + +To verify that a promise is rejected, you can use the +[`t.rejects()`](/asserts/#trejectspromise--fn-expectederror-message-extra) +function. + +## `async`/`await` + +Because `async` functions return a Promise, you can use them out of +the box with node-tap. If you pass an `async` function as the +`t.test()` callback, then tap will detect the promise return and move +onto the next test once the async function has completely resolved. + +The above example could be written like this: + +```js +var t = require('tap') +t.test('get thing', async function (t) { + var result = await getSomeThing() + await t.test('check result', function (t) { + t.equal(result.foo, 'bar') + t.end() + }) +}).then(async function (t) { + var things = await getTwoThings() + var otherPromiseResult = await t.test('the things', function (t) { + t.equal(things.length, 2) + return makeSomeOtherPromise() + }) + await t.test('check other promise thing', function (t) { + t.equal(otherPromiseResult, 7, 'it should be seven') + t.end() + }) +}).catch(t.threw) +``` + +Because subtests return promises, you can also `await` them to do +things in between subtests. However, this requires a top-level async +function. + +For example: + +```js +var t = require('tap') + +async function main () { + await t.test('get thing', function (t) { + return getSomeThing().then(function (result) { + return t.test('check result', function (t) { + t.equal(result.foo, 'bar') + t.end() + }) + }) + }) + var things = await getTwoThings() + var otherPromiseResult = await t.test('got two things', function (t) { + t.equal(things.length, 2) + return makeSomeOtherPromise() + }) + await t.test('check other promise thing', function (t) { + t.equal(otherPromiseResult, 7, 'it should be seven') + t.end() + }) + console.log('tests are all done!') +} +main() +``` diff --git a/docs/reporting/index.md b/docs/reporting/index.md new file mode 100644 index 000000000..c57b5f65b --- /dev/null +++ b/docs/reporting/index.md @@ -0,0 +1,87 @@ +--- +layout: layout +--- + +# Reporting + +Tests can be reported in a variety of different ways. + +When you run a test script directly, it'll always output +[TAP](/tap-format/). The tap runner will interpret this +output, and can format it in a variety of different ways. + +These are done programmatically using the +[tap-mocha-reporter](http://npm.im/tap-mocha-reporter) module, which +ports many of the report styles built into +[mocha](http://mochajs.org/#reporters). + +You can specify a reporter using the `--reporter` or `-R` argument. +The following options are available: + +- classic + + The default when stdout is a terminal. Show a summary of + each test file being run, along with reporting each failure and + pending test. + +- tap + + Just the raw TAP. Default when stdout is not a terminal. + +- doc + + Output heirarchical HTML. + +- dot + + Output a dot for each pass and failure. + +- dump + + Mostly for debugging tap-mocha-reporter, dumping out the TAP + output and the way that its being interpreted. + +- json + + Output results in one big JSON object. + +- jsonstream + + Output results as a stream of `\n`-delimited JSON. + +- landing + + A unicode airplane that lands on your terminal. + +- list + + List out each test as it's run. + +- markdown + + Heirarchical markdown output with a table of contents. + +- min + + Just the post-test summary of failures and test count. + +- nyan + + A magical cat who is also a toaster pastry. + +- progress + + A progress bar. + +- silent + + Output absolutely nothing + +- spec + + Output based on rspec, with heirarchical indentation and + unicode red and green checks and X's. + +- xunit + + XML output popular in .NET land. diff --git a/docs/snapshots/index.md b/docs/snapshots/index.md new file mode 100644 index 000000000..5d12dedd2 --- /dev/null +++ b/docs/snapshots/index.md @@ -0,0 +1,151 @@ +--- +layout: layout +--- + +# Testing with Snapshots + +As of version 11, tap supports saving and then comparing against +"snapshot" strings. This is a powerful technique for testing programs +that generate output, but it comes with some caveats. + +## Basics of Output Testing + +Consider a test program like this: + +```javascript +module.exports = function (tag, contents) { + return '<' + tag + '>' + contents + '' +} +``` + +We might have a test like this: + +```javascript +const t = require('tap') +const tagger = require('./index.js') +t.equal(tagger('tagName', 'content'), 'content') +``` + +This is good for a couple of reasons: + +1. It's clear reading our test what the expected output is. +2. We're unlikely to create a test without thinking carefully about + what _exactly_ it's testing. + +However, managing strings like this can become extremely tedious, +especially in cases where the output is long, or there are many cases +to test. If we make an _intentional_ change to the output, then we +need to manually update a large number of large strings, scattered +throughout the test suite. The inevitable result is that we either +make the tests less comprehensive, or even worse, treat some as "known +failures". + +## Testing Output with Snapshots + +We could also write our test file like this: + +```javascript +const t = require('tap') +const tagger = require('./index.js') +t.matchSnapshot(tagger('tagName', 'content'), 'output') +``` + +But wait, where is the expected output? + +To create the snapshot file, we run this command: + +``` +$ TAP_SNAPSHOT=1 tap test.js +test.js ............................................... 1/1 +total ................................................. 1/1 + + 1 passing (207.826ms) + + ok +``` + +By setting `TAP_SNAPSHOT` in the environment, we tell tap to write the +output to a special file, and treat the assertion as automatically +passing. + +The generated file is designed to be human-readable, but you should +not edit it directly. + + $ cat tap-snapshots/test.js-TAP.test.js + /* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ + 'use strict' + exports[`test.js TAP > output 1`] = ` + content + +The filename is derived from the name of the test file. The headings +of each string output are based on the names of your tests and +assertions, and given a numeric index to handle collisions. + +## Snapshotting non-Strings + +If the argument passed to `t.matchSnapshot()` isn't a string, then it +will be converted to a string using Node.js's `util.inspect`. This is +typically pretty easy for humans to understand, but of course if you +prefer to use `JSON.stringify` or something else, you can do so easily +enough. + +## Caveats + +### Track Changes + +**Important: you should check the snapshot file into source control!** + +When there are changes to it, inspect the diff and make sure that +nothing unexpected happened to change your output. + +If you don't check this file into source control, then a significant +part of your test is not checked in. This prevents people from +collaborating on your project. + +If you accept changes to it without care, then you can obscure +unintended changes. (Though, even if this happens, `git bisect` can +track down the source of the change quite quickly, so it's not the end +of the world if there are occasional mistakes.) + +### Strip Variables from Output + +If your output includes data that is known to change from one run to +the next, then these changes should be stripped before matching +against a snapshot. + +This includes process IDs, time stamps, and many other system details. + +Consider this function: + +```javascript +function msgTime (msg) { + return msg + ' time=' + Date.now() +} +``` + +Since the output will obviously be slightly different every time the +function is tested, we need to strip out the time value. + +```javascript +const t = require('tap') + +function clean (output) { + return output.replace(/ time=[0-9]+$/g, ' time={time}') +} + +const output = msgTime('this is a test') +t.matchSnapshot(clean(output), 'add timestamp to message') +``` + +When run with `TAP_SNAPSHOT=1`, it generates this snapshot file: + +```javascript +exports[`test.js TAP > add timestamp to message 1`] = ` +this is a test time={time} +` +``` diff --git a/docs/static/prism.css b/docs/static/prism.css new file mode 100644 index 000000000..b95d350c3 --- /dev/null +++ b/docs/static/prism.css @@ -0,0 +1,196 @@ +/* http://prismjs.com/download.html?themes=prism&languages=clike+javascript&plugins=line-numbers */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ + +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Triplicate T4, Fira Mono OT, Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospacek; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, pre[class*="language-"] ::selection, +code[class*="language-"]::selection, code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.bailout, +.token.fail { + color:#a00; + font-weight:bold; +} + +.token.subtest, +.token.version, +.token.plan { + color:#009; +} + +.token.pass { + color:#080; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.todo, +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #a67f59; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + +pre.line-numbers { + position: relative; + padding-left: 3.8em; + counter-reset: linenumber; +} + +pre.line-numbers > code { + position: relative; +} + +.line-numbers .line-numbers-rows { + position: absolute; + pointer-events: none; + top: 0; + font-size: 100%; + left: -3.8em; + width: 3em; /* works for line-numbers below 1000 lines */ + letter-spacing: -1px; + border-right: 1px solid #999; + + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + +} + + .line-numbers-rows > span { + pointer-events: none; + display: block; + counter-increment: linenumber; + } + + .line-numbers-rows > span:before { + content: counter(linenumber); + color: #999; + display: block; + padding-right: 0.8em; + text-align: right; + } diff --git a/docs/static/prism.js b/docs/static/prism.js new file mode 100644 index 000000000..e20355065 --- /dev/null +++ b/docs/static/prism.js @@ -0,0 +1,728 @@ +/* http://prismjs.com/download.html?themes=prism-dark&languages=clike+javascript+json+yaml&plugins=line-numbers */ +var _self = (typeof window !== 'undefined') + ? window // if in browser + : ( + (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) + ? self // if in worker + : {} // if in node js + ); + +/** + * Prism: Lightweight, robust, elegant syntax highlighting + * MIT license http://www.opensource.org/licenses/mit-license.php/ + * @author Lea Verou http://lea.verou.me + */ + +var Prism = (function(){ + +// Private helper vars +var lang = /\blang(?:uage)?-(\w+)\b/i; +var uniqueId = 0; + +var _ = _self.Prism = { + manual: _self.Prism && _self.Prism.manual, + util: { + encode: function (tokens) { + if (tokens instanceof Token) { + return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias); + } else if (_.util.type(tokens) === 'Array') { + return tokens.map(_.util.encode); + } else { + return tokens.replace(/&/g, '&').replace(/ text.length) { + // Something went terribly wrong, ABORT, ABORT! + break tokenloop; + } + + if (str instanceof Token) { + continue; + } + + pattern.lastIndex = 0; + + var match = pattern.exec(str), + delNum = 1; + + // Greedy patterns can override/remove up to two previously matched tokens + if (!match && greedy && i != strarr.length - 1) { + pattern.lastIndex = pos; + match = pattern.exec(text); + if (!match) { + break; + } + + var from = match.index + (lookbehind ? match[1].length : 0), + to = match.index + match[0].length, + k = i, + p = pos; + + for (var len = strarr.length; k < len && p < to; ++k) { + p += strarr[k].length; + // Move the index i to the element in strarr that is closest to from + if (from >= p) { + ++i; + pos = p; + } + } + + /* + * If strarr[i] is a Token, then the match starts inside another Token, which is invalid + * If strarr[k - 1] is greedy we are in conflict with another greedy pattern + */ + if (strarr[i] instanceof Token || strarr[k - 1].greedy) { + continue; + } + + // Number of tokens to delete and replace with the new match + delNum = k - i; + str = text.slice(pos, p); + match.index -= pos; + } + + if (!match) { + continue; + } + + if(lookbehind) { + lookbehindLength = match[1].length; + } + + var from = match.index + lookbehindLength, + match = match[0].slice(lookbehindLength), + to = from + match.length, + before = str.slice(0, from), + after = str.slice(to); + + var args = [i, delNum]; + + if (before) { + args.push(before); + } + + var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match, greedy); + + args.push(wrapped); + + if (after) { + args.push(after); + } + + Array.prototype.splice.apply(strarr, args); + } + } + } + + return strarr; + }, + + hooks: { + all: {}, + + add: function (name, callback) { + var hooks = _.hooks.all; + + hooks[name] = hooks[name] || []; + + hooks[name].push(callback); + }, + + run: function (name, env) { + var callbacks = _.hooks.all[name]; + + if (!callbacks || !callbacks.length) { + return; + } + + for (var i=0, callback; callback = callbacks[i++];) { + callback(env); + } + } + } +}; + +var Token = _.Token = function(type, content, alias, matchedStr, greedy) { + this.type = type; + this.content = content; + this.alias = alias; + // Copy of the full string this token was created from + this.length = (matchedStr || "").length|0; + this.greedy = !!greedy; +}; + +Token.stringify = function(o, language, parent) { + if (typeof o == 'string') { + return o; + } + + if (_.util.type(o) === 'Array') { + return o.map(function(element) { + return Token.stringify(element, language, o); + }).join(''); + } + + var env = { + type: o.type, + content: Token.stringify(o.content, language, parent), + tag: 'span', + classes: ['token', o.type], + attributes: {}, + language: language, + parent: parent + }; + + if (env.type == 'comment') { + env.attributes['spellcheck'] = 'true'; + } + + if (o.alias) { + var aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias]; + Array.prototype.push.apply(env.classes, aliases); + } + + _.hooks.run('wrap', env); + + var attributes = Object.keys(env.attributes).map(function(name) { + return name + '="' + (env.attributes[name] || '').replace(/"/g, '"') + '"'; + }).join(' '); + + return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + (attributes ? ' ' + attributes : '') + '>' + env.content + ''; + +}; + +if (!_self.document) { + if (!_self.addEventListener) { + // in Node.js + return _self.Prism; + } + // In worker + _self.addEventListener('message', function(evt) { + var message = JSON.parse(evt.data), + lang = message.language, + code = message.code, + immediateClose = message.immediateClose; + + _self.postMessage(_.highlight(code, _.languages[lang], lang)); + if (immediateClose) { + _self.close(); + } + }, false); + + return _self.Prism; +} + +//Get current script and highlight +var script = document.currentScript || [].slice.call(document.getElementsByTagName("script")).pop(); + +if (script) { + _.filename = script.src; + + if (document.addEventListener && !_.manual && !script.hasAttribute('data-manual')) { + if(document.readyState !== "loading") { + if (window.requestAnimationFrame) { + window.requestAnimationFrame(_.highlightAll); + } else { + window.setTimeout(_.highlightAll, 16); + } + } + else { + document.addEventListener('DOMContentLoaded', _.highlightAll); + } + } +} + +return _self.Prism; + +})(); + +if (typeof module !== 'undefined' && module.exports) { + module.exports = Prism; +} + +// hack for components to work correctly in node.js +if (typeof global !== 'undefined') { + global.Prism = Prism; +} +; +Prism.languages.clike = { + 'comment': [ + { + pattern: /(^|[^\\])\/\*[\w\W]*?\*\//, + lookbehind: true + }, + { + pattern: /(^|[^\\:])\/\/.*/, + lookbehind: true + } + ], + 'string': { + pattern: /(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, + greedy: true + }, + 'class-name': { + pattern: /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i, + lookbehind: true, + inside: { + punctuation: /(\.|\\)/ + } + }, + 'keyword': /\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/, + 'boolean': /\b(true|false)\b/, + 'function': /[a-z0-9_]+(?=\()/i, + 'number': /\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i, + 'operator': /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/, + 'punctuation': /[{}[\];(),.:]/ +}; + +Prism.languages.javascript = Prism.languages.extend('clike', { + 'keyword': /\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/, + 'number': /\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/, + // Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444) + 'function': /[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i, + 'operator': /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*\*?|\/|~|\^|%|\.{3}/ +}); + +Prism.languages.insertBefore('javascript', 'keyword', { + 'regex': { + pattern: /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/, + lookbehind: true, + greedy: true + } +}); + +Prism.languages.insertBefore('javascript', 'string', { + 'template-string': { + pattern: /`(?:\\\\|\\?[^\\])*?`/, + greedy: true, + inside: { + 'interpolation': { + pattern: /\$\{[^}]+\}/, + inside: { + 'interpolation-punctuation': { + pattern: /^\$\{|\}$/, + alias: 'punctuation' + }, + rest: Prism.languages.javascript + } + }, + 'string': /[\s\S]+/ + } + } +}); + +if (Prism.languages.markup) { + Prism.languages.insertBefore('markup', 'tag', { + 'script': { + pattern: /()[\w\W]*?(?=<\/script>)/i, + lookbehind: true, + inside: Prism.languages.javascript, + alias: 'language-javascript' + } + }); +} + +Prism.languages.js = Prism.languages.javascript; +Prism.languages.json = { + 'property': /"(?:\\.|[^\\"])*"(?=\s*:)/ig, + 'string': /"(?!:)(?:\\.|[^\\"])*"(?!:)/g, + 'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee][+-]?\d+)?)\b/g, + 'punctuation': /[{}[\]);,]/g, + 'operator': /:/g, + 'boolean': /\b(true|false)\b/gi, + 'null': /\bnull\b/gi +}; + + +Prism.languages.jsonp = Prism.languages.json; + +Prism.languages.yaml = { + 'scalar': { + pattern: /([\-:]\s*(![^\s]+)?[ \t]*[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)[^\r\n]+(?:\3[^\r\n]+)*)/, + lookbehind: true, + alias: 'string' + }, + 'comment': /#.*/, + 'key': { + pattern: /(\s*(?:^|[:\-,[{\r\n?])[ \t]*(![^\s]+)?[ \t]*)[^\r\n{[\]},#\s]+?(?=\s*:\s)/, + lookbehind: true, + alias: 'atrule' + }, + 'directive': { + pattern: /(^[ \t]*)%.+/m, + lookbehind: true, + alias: 'important' + }, + 'datetime': { + pattern: /([:\-,[{]\s*(![^\s]+)?[ \t]*)(\d{4}-\d\d?-\d\d?([tT]|[ \t]+)\d\d?:\d{2}:\d{2}(\.\d*)?[ \t]*(Z|[-+]\d\d?(:\d{2})?)?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(:\d{2}(\.\d*)?)?)(?=[ \t]*($|,|]|}))/m, + lookbehind: true, + alias: 'number' + }, + 'boolean': { + pattern: /([:\-,[{]\s*(![^\s]+)?[ \t]*)(true|false)[ \t]*(?=$|,|]|})/im, + lookbehind: true, + alias: 'important' + }, + 'null': { + pattern: /([:\-,[{]\s*(![^\s]+)?[ \t]*)(null|~)[ \t]*(?=$|,|]|})/im, + lookbehind: true, + alias: 'important' + }, + 'string': { + pattern: /([:\-,[{]\s*(![^\s]+)?[ \t]*)("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')(?=[ \t]*($|,|]|}))/m, + lookbehind: true + }, + 'number': { + pattern: /([:\-,[{]\s*(![^\s]+)?[ \t]*)[+\-]?(0x[\da-f]+|0o[0-7]+|(\d+\.?\d*|\.?\d+)(e[\+\-]?\d+)?|\.inf|\.nan)[ \t]*(?=$|,|]|})/im, + lookbehind: true + }, + 'tag': /![^\s]+/, + 'important': /[&*][\w]+/, + 'punctuation': /---|[:[\]{}\-,|>?]|\.\.\./ +}; + +Prism.languages.tap = { + pass: /(^|\n)( )*ok[^#\{\n]*/, + fail: /(^|\n)( )*not ok[^#\{\n]*/, + pragma: /(^|\n)( )*pragma ([+-])([a-z]+)(\n|$)/, + bailout: /(^|\n)( )*bail out!(.*)(\n|$)/i, + version: /(^|\n)( )*TAP version ([0-9]+)(\n|$)/i, + plan: /(^|\n)( )*([0-9]+)\.\.([0-9]+)( +#[^\n]*)?(\n|$)/m, + subtest: { + pattern: /(^|\n)( )*# Subtest(?:: (.*))?(\n|$)/, + greedy: true + }, + punctuation: /[{}]/, + 'comment': /#.*/, + yamlish: { + pattern: /(^|\n)(( )*( ))---\n(.*?\n)+\2\.\.\.(\n|$)/, + lookbehind: true, + inside: Prism.languages.yaml, + alias: 'language-yaml' + } +}; + +(function() { + +if (typeof self === 'undefined' || !self.Prism || !self.document) { + return; +} + +Prism.hooks.add('complete', function (env) { + if (!env.code) { + return; + } + + // works only for wrapped inside
 (not inline)
+	var pre = env.element.parentNode;
+	var clsReg = /\s*\bline-numbers\b\s*/;
+	if (
+		!pre || !/pre/i.test(pre.nodeName) ||
+			// Abort only if nor the 
 nor the  have the class
+		(!clsReg.test(pre.className) && !clsReg.test(env.element.className))
+	) {
+		return;
+	}
+
+	if (env.element.querySelector(".line-numbers-rows")) {
+		// Abort if line numbers already exists
+		return;
+	}
+
+	if (clsReg.test(env.element.className)) {
+		// Remove the class "line-numbers" from the 
+		env.element.className = env.element.className.replace(clsReg, '');
+	}
+	if (!clsReg.test(pre.className)) {
+		// Add the class "line-numbers" to the 
+		pre.className += ' line-numbers';
+	}
+
+	var match = env.code.match(/\n(?!$)/g);
+	var linesNum = match ? match.length + 1 : 1;
+	var lineNumbersWrapper;
+
+	var lines = new Array(linesNum + 1);
+	lines = lines.join('');
+
+	lineNumbersWrapper = document.createElement('span');
+	lineNumbersWrapper.setAttribute('aria-hidden', 'true');
+	lineNumbersWrapper.className = 'line-numbers-rows';
+	lineNumbersWrapper.innerHTML = lines;
+
+	if (pre.hasAttribute('data-start')) {
+		pre.style.counterReset = 'linenumber ' + (parseInt(pre.getAttribute('data-start'), 10) - 1);
+	}
+
+	env.element.appendChild(lineNumbersWrapper);
+
+});
+
+}());
diff --git a/docs/static/tapjs.png b/docs/static/tapjs.png
new file mode 100644
index 0000000000000000000000000000000000000000..91c2ad0f66f757b9826ccec1f6133fac971f2d5c
GIT binary patch
literal 57234
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 comment,
+followed by the child TAP stream indented by 4 spaces, and finished
+with a test point that indicates the passing status of the stream as a
+whole.
+
+"Buffered" subtest start with a test point indicating the status of
+the group, and the indented child stream is wrapped in `{}` braces.
+It's called "buffered" because the entire child stream has to be
+parsed before the summary test point can be generated.
+
+The summary test point ensures that TAP consumers that ignore indented
+lines will at least report on the passing status based on the summary.
+
+```tap
+1..2
+# Subtest: not buffered
+    ok 1 - each line just printed as it comes
+    ok 2 - no time to wait!
+    1..2
+ok 1 - not buffered
+
+ok 2 - buffered {
+    1..3
+    ok 1 - this test is buffered
+    ok 2 - so all the test points had to be parsed
+    ok 3 - before success could be reported
+}
+```
+
+Directives on buffered subtests can go either before or after the `{`
+character.  When a buffered subtest has diagnostics, the `{` goes on
+the line by itself after the yaml block.
+
+```tap
+1..2
+ok 1 - usually would not even run this # TODO {
+    ok 1 - but here we are anyway
+    ok 2 - todo'ing away
+    1..2
+}
+ok 2 - a very diagnostic subtest # time=33ms
+  ---
+  this: is fine
+  i: am ok with the way things are proceeding
+  ...
+{
+    1..1
+    ok 1 - whatever
+}
+```
+
+The most common way to run subtests is via `t.test(...)`.  See
+[Subtests](/subtests/) for more info.
+
+## Pragma
+
+Pragmas are a way to set arbitrary switches on the parser.
+
+The only switch that is treated specially is `strict`.  When in strict
+mode, any non-TAP data is treated as an error.
+
+```tap
+TAP version 13
+pragma +strict
+ok 1 - this is very strict
+so this line here is an error
+not ok 2 - that line failed
+pragma -strict
+but this line here is fine
+ok 3 - because garbage data is allowed in non-strict mode
+1..3
+```
+
+Set pragms in `tap` by doing `t.pragma({ keys: values, ... })`.
+The object can contain any number of keys, but only `strict` has any
+meaning to `tap` itself.
+
+## Bail out!
+
+Sometimes a set of tests hits a state where there's no point
+continuing.  Or, perhaps you just wish to stop on the first failure to
+work on errors one at a time with a faster turnover.
+
+In this case, TAP allows a "bail out".  A bail out is much more
+extreme than a test point failure.  It means that everything should
+come to a halt, all the way up to the highest level test harness.
+Nothing should come after a bailout.  Any plan is dropped, test points
+ignored, and so on.
+
+```tap
+TAP version 13
+# Subtest: child
+    # Subtest: grandchild
+        1..2999
+        ok 1 - here we go
+        Bail out! Nope.
+Bail out! Nope.
+```
+
+Bail outs in buffered tests should still print the closing `}` braces,
+but no other output.
+
+```tap
+TAP version 13
+not ok 1 - child {
+    not ok 2 - grandchild {
+        1..2999
+        ok 1 - here we go
+        Bail out! Nope.
+    }
+}
+Bail out! Nope.
+```
+
+You can generate a bailout explicitly by doing `t.bailout(reason)`.
+You can also have `tap` bail out on any test failure by setting
+`TAP_BAIL=1` in the environment, or by setting `{ bail: true }` in a
+child test options, or by running with the `tap` [command-line
+interface](/cli/) and passing the `--bail` flag.
+
+## Comments and Other Stuff
+
+Anything that starts with a `#` and is not a directive or subtest
+prefix is treated as a comment, and ignored.
+
+Anything that isn't parseable as one of the above types of lines is
+considered "extra" non-TAP data.  In strict mode, extra output is an
+error.  In non-strict mode, it's ignored.
+
+The `tap` runner ignores comments.  Non-TAP data is passed through the
+reporting engine and printed to the top-level process standard output.
+This means that `console.log('foo')` will make its way to the top
+level, instead of being swallowed by a reporter.
+
+You can output comments by doing `t.comment('foo')`.  This function
+takes any arguments that can be passed to `console.log()`.  For
+example, `t.comment('number %d and\nobj =', 1, { foo: 'bar' })` would
+output:
+
+```tap
+# number 1 and
+# obj = { foo: 'bar' }
+```