diff --git a/README.md b/README.md index 98f5961..c8105b0 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,42 @@ test('skip() method with message', t => { }) ``` +## `describe`/`it` syntax + +Running tests can also be done using `describe` to declare a suite +and `it` to declare a test. +A suite is used to organize and group related tests together. +`it` is an alias for `test`, except there is no test context passed, +since nesting is done using suites, as demonstrated in this example + +```js +describe('A thing', () => { + it('should work', () => { + assert.strictEqual(1, 1); + }); + + it('should be ok', () => { + assert.strictEqual(2, 2); + }); + + describe('a nested thing', () => { + it('should work', () => { + assert.strictEqual(3, 3); + }); + }); +}); +``` + +`describe` and `it` are imported from the `test` module + +```mjs +import { describe, it } from 'test'; +``` + +```cjs +const { describe, it } = require('test'); +``` + ### `only` tests If `node--test` is started with the `--test-only` command-line option, it is @@ -303,7 +339,7 @@ internally. - `todo` {boolean|string} If truthy, the test marked as `TODO`. If a string is provided, that string is displayed in the test results as the reason why the test is `TODO`. **Default:** `false`. -- `fn` {Function|AsyncFunction} The function under test. This first argument +- `fn` {Function|AsyncFunction} The function under test. The first argument to this function is a [`TestContext`][] object. If the test uses callbacks, the callback function is passed as the second argument. **Default:** A no-op function. @@ -335,6 +371,59 @@ test('top level test', async t => { }) ``` +## `describe([name][, options][, fn])` + +* `name` {string} The name of the suite, which is displayed when reporting test + results. **Default:** The `name` property of `fn`, or `''` if `fn` + does not have a name. +* `options` {Object} Configuration options for the suite. + supports the same options as `test([name][, options][, fn])` +* `fn` {Function} The function under suite. + a synchronous function declaring all subtests and subsuites. + **Default:** A no-op function. +* Returns: `undefined`. + +The `describe()` function imported from the `test` module. Each +invocation of this function results in the creation of a Subtest +and a test point in the TAP output. +After invocation of top level `describe` functions, +all top level tests and suites will execute + +## `describe.skip([name][, options][, fn])` + +Shorthand for skipping a suite, same as [`describe([name], { skip: true }[, fn])`][describe options]. + +## `describe.todo([name][, options][, fn])` + +Shorthand for marking a suite as `TODO`, same as +[`describe([name], { todo: true }[, fn])`][describe options]. + +## `it([name][, options][, fn])` + +* `name` {string} The name of the test, which is displayed when reporting test + results. **Default:** The `name` property of `fn`, or `''` if `fn` + does not have a name. +* `options` {Object} Configuration options for the suite. + supports the same options as `test([name][, options][, fn])`. +* `fn` {Function|AsyncFunction} The function under test. + If the test uses callbacks, the callback function is passed as an argument. + **Default:** A no-op function. +* Returns: `undefined`. + +The `it()` function is the value imported from the `test` module. +Each invocation of this function results in the creation of a test point in the +TAP output. + +## `it.skip([name][, options][, fn])` + +Shorthand for skipping a test, +same as [`it([name], { skip: true }[, fn])`][it options]. + +## `it.todo([name][, options][, fn])` + +Shorthand for marking a test as `TODO`, +same as [`it([name], { todo: true }[, fn])`][it options]. + ## Class: `TestContext` An instance of `TestContext` is passed to each test function in order to @@ -394,7 +483,7 @@ execution of the test function. This function does not return a value. - `todo` {boolean|string} If truthy, the test marked as `TODO`. If a string is provided, that string is displayed in the test results as the reason why the test is `TODO`. **Default:** `false`. -- `fn` {Function|AsyncFunction} The function under test. This first argument +- `fn` {Function|AsyncFunction} The function under test. The first argument to this function is a [`TestContext`][] object. If the test uses callbacks, the callback function is passed as the second argument. **Default:** A no-op function. @@ -406,6 +495,8 @@ behaves in the same fashion as the top level [`test()`][] function. [tap]: https://testanything.org/ [`testcontext`]: #class-testcontext [`test()`]: #testname-options-fn +[describe options]: #describename-options-fn +[it options]: #testname-options-fn [test runner execution model]: #test-runner-execution-model ## License diff --git a/lib/internal/main/test_runner.js b/lib/internal/main/test_runner.js index 247b9e0..cc6fb5b 100644 --- a/lib/internal/main/test_runner.js +++ b/lib/internal/main/test_runner.js @@ -1,4 +1,4 @@ -// https://github.com/nodejs/node/blob/1aab13cad9c800f4121c1d35b554b78c1b17bdbd/lib/internal/main/test_runner.js +// https://github.com/nodejs/node/blob/e2225ba8e1c00995c0f8bd56e607ea7c5b463ab9/lib/internal/main/test_runner.js 'use strict' const { ArrayFrom, @@ -21,7 +21,7 @@ const { ERR_TEST_FAILURE } } = require('#internal/errors') -const test = require('#internal/test_runner/harness') +const { test } = require('#internal/test_runner/harness') const { kSubtestsFailed } = require('#internal/test_runner/test') const { isSupportedFileType, diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index 69aa660..30ec71f 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -8,6 +8,7 @@ exports.ArrayPrototypeForEach = (arr, fn, thisArg) => arr.forEach(fn, thisArg) exports.ArrayPrototypeIncludes = (arr, el, fromIndex) => arr.includes(el, fromIndex) exports.ArrayPrototypeJoin = (arr, str) => arr.join(str) exports.ArrayPrototypePush = (arr, ...el) => arr.push(...el) +exports.ArrayPrototypeReduce = (arr, fn, originalVal) => arr.reduce(fn, originalVal) exports.ArrayPrototypeShift = arr => arr.shift() exports.ArrayPrototypeSlice = (arr, offset) => arr.slice(offset) exports.ArrayPrototypeSort = (arr, fn) => arr.sort(fn) @@ -27,6 +28,7 @@ exports.ObjectIsExtensible = obj => Object.isExtensible(obj) exports.ObjectPrototypeHasOwnProperty = (obj, property) => Object.prototype.hasOwnProperty.call(obj, property) exports.ReflectApply = (target, self, args) => Reflect.apply(target, self, args) exports.Promise = Promise +exports.PromiseResolve = val => Promise.resolve(val) exports.SafeMap = Map exports.SafeSet = Set exports.SafeWeakMap = WeakMap diff --git a/lib/internal/test_runner/harness.js b/lib/internal/test_runner/harness.js index a212dda..972e4ca 100644 --- a/lib/internal/test_runner/harness.js +++ b/lib/internal/test_runner/harness.js @@ -1,8 +1,10 @@ -// https://github.com/nodejs/node/blob/1aab13cad9c800f4121c1d35b554b78c1b17bdbd/lib/internal/test_runner/harness.js - +// https://github.com/nodejs/node/blob/e2225ba8e1c00995c0f8bd56e607ea7c5b463ab9/lib/internal/test_runner/harness.js 'use strict' - -const { FunctionPrototypeBind, SafeMap } = require('#internal/per_context/primordials') +const { + ArrayPrototypeForEach, + FunctionPrototypeBind, + SafeMap +} = require('#internal/per_context/primordials') const { createHook, executionAsyncId @@ -12,34 +14,44 @@ const { ERR_TEST_FAILURE } } = require('#internal/errors') -const { Test } = require('#internal/test_runner/test') +const { Test, ItTest, Suite } = require('#internal/test_runner/test') -function createProcessEventHandler (eventName, rootTest, testResources) { +const testResources = new SafeMap() +const root = new Test({ __proto__: null, name: '' }) +let wasRootSetup = false + +function createProcessEventHandler (eventName, rootTest) { return (err) => { // Check if this error is coming from a test. If it is, fail the test. const test = testResources.get(executionAsyncId()) - if (test !== undefined) { - if (test.finished) { - // If the test is already finished, report this as a top level - // diagnostic since this is a malformed test. - const msg = `Warning: Test "${test.name}" generated asynchronous ` + - 'activity after the test ended. This activity created the error ' + - `"${err}" and would have caused the test to fail, but instead ` + - `triggered an ${eventName} event.` + if (!test) { + // Node.js 14.x crashes if the error is throw here. + if (process.version.startsWith('v14.')) return + throw err + } - rootTest.diagnostic(msg) - return - } + if (test.finished) { + // If the test is already finished, report this as a top level + // diagnostic since this is a malformed test. + const msg = `Warning: Test "${test.name}" generated asynchronous ` + + 'activity after the test ended. This activity created the error ' + + `"${err}" and would have caused the test to fail, but instead ` + + `triggered an ${eventName} event.` - test.fail(new ERR_TEST_FAILURE(err, eventName)) - test.postRun() + rootTest.diagnostic(msg) + return } + + test.fail(new ERR_TEST_FAILURE(err, eventName)) + test.postRun() } } function setup (root) { - const testResources = new SafeMap() + if (wasRootSetup) { + return root + } const hook = createHook({ init (asyncId, type, triggerAsyncId, resource) { if (resource instanceof Test) { @@ -61,9 +73,9 @@ function setup (root) { hook.enable() const exceptionHandler = - createProcessEventHandler('uncaughtException', root, testResources) + createProcessEventHandler('uncaughtException', root) const rejectionHandler = - createProcessEventHandler('unhandledRejection', root, testResources) + createProcessEventHandler('unhandledRejection', root) process.on('uncaughtException', exceptionHandler) process.on('unhandledRejection', rejectionHandler) @@ -116,19 +128,39 @@ function setup (root) { root.reporter.pipe(process.stdout) root.reporter.version() + + wasRootSetup = true + return root } function test (name, options, fn) { - // If this is the first test encountered, bootstrap the test harness. - if (this.subtests.length === 0) { - setup(this) + const subtest = setup(root).createSubtest(Test, name, options, fn) + return subtest.start() +} + +function runInParentContext (Factory) { + function run (name, options, fn, overrides) { + const parent = testResources.get(executionAsyncId()) || setup(root) + const subtest = parent.createSubtest(Factory, name, options, fn, overrides) + if (parent === root) { + subtest.start() + } } - const subtest = this.createSubtest(name, options, fn) + const cb = (name, options, fn) => { + run(name, options, fn) + } - return subtest.start() + ArrayPrototypeForEach(['skip', 'todo'], (keyword) => { + cb[keyword] = (name, options, fn) => { + run(name, options, fn, { [keyword]: true }) + } + }) + return cb } -const root = new Test({ name: '' }) - -module.exports = FunctionPrototypeBind(test, root) +module.exports = { + test: FunctionPrototypeBind(test, root), + describe: runInParentContext(Suite), + it: runInParentContext(ItTest) +} diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index 9ac9a77..5964f67 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -1,12 +1,16 @@ -// https://github.com/nodejs/node/blob/5fadc389b8a9a32809adda8245ad32928623409b/lib/internal/test_runner/test.js +// https://github.com/nodejs/node/blob/e2225ba8e1c00995c0f8bd56e607ea7c5b463ab9/lib/internal/test_runner/test.js 'use strict' const { ArrayPrototypePush, + ArrayPrototypeReduce, ArrayPrototypeShift, + ArrayPrototypeUnshift, FunctionPrototype, Number, + PromiseResolve, + ReflectApply, SafeMap } = require('#internal/per_context/primordials') const { AsyncResource } = require('async_hooks') @@ -18,6 +22,7 @@ const { } = require('#internal/errors') const { getOptionValue } = require('#internal/options') const { TapStream } = require('#internal/test_runner/tap_stream') +const { createDeferredCallback } = require('#internal/test_runner/utils') const { createDeferredPromise, kEmptyObject @@ -28,7 +33,6 @@ const { cpus } = require('os') const { bigint: hrtime } = process.hrtime const kCallbackAndPromisePresent = 'callbackAndPromisePresent' const kCancelledByParent = 'cancelledByParent' -const kMultipleCallbackInvocations = 'multipleCallbackInvocations' const kParentAlreadyFinished = 'parentAlreadyFinished' const kSubtestsFailed = 'subtestsFailed' const kTestCodeFailure = 'testCodeFailure' @@ -63,7 +67,8 @@ class TestContext { } test (name, options, fn) { - const subtest = this.#test.createSubtest(name, options, fn) + // eslint-disable-next-line no-use-before-define + const subtest = this.#test.createSubtest(Test, name, options, fn) return subtest.start() } @@ -198,7 +203,7 @@ class Test extends AsyncResource { } } - createSubtest (name, options, fn) { + createSubtest (Factory, name, options, fn, overrides) { if (typeof name === 'function') { fn = name } else if (name !== null && typeof name === 'object') { @@ -222,13 +227,14 @@ class Test extends AsyncResource { } } - const test = new Test({ fn, name, parent, ...options }) + const test = new Factory({ fn, name, parent, ...options, ...overrides }) if (parent.waitingOn === 0) { parent.waitingOn = test.testNumber } if (this.finished) { + test.startTime = test.startTime || hrtime() test.fail( new ERR_TEST_FAILURE( 'test could not be started because its parent finished', @@ -303,39 +309,25 @@ class Test extends AsyncResource { return this.run() } + getRunArgs () { + const ctx = new TestContext(this) + return { ctx, args: [ctx] } + } + async run () { this.parent.activeSubtests++ this.startTime = hrtime() try { - const ctx = new TestContext(this) + const { args, ctx } = this.getRunArgs() + ArrayPrototypeUnshift(args, this.fn, ctx) // Note that if it's not OK to mutate args, we need to first clone it. - if (this.fn.length === 2) { + if (this.fn.length === args.length - 1) { // This test is using legacy Node.js error first callbacks. - const { promise, resolve, reject } = createDeferredPromise() - let calledCount = 0 - const ret = this.runInAsyncScope(this.fn, ctx, ctx, (err) => { - calledCount++ - - // If the callback is called a second time, let the user know, but - // don't let them know more than once. - if (calledCount > 1) { - if (calledCount === 2) { - throw new ERR_TEST_FAILURE( - 'callback invoked multiple times', - kMultipleCallbackInvocations - ) - } - - return - } - - if (err) { - return reject(err) - } - - resolve() - }) + const { promise, cb } = createDeferredCallback() + + ArrayPrototypePush(args, cb) + const ret = ReflectApply(this.runInAsyncScope, this, args) if (isPromise(ret)) { this.fail(new ERR_TEST_FAILURE( @@ -348,7 +340,7 @@ class Test extends AsyncResource { } } else { // This test is synchronous or using Promises. - await this.runInAsyncScope(this.fn, ctx, ctx) + await ReflectApply(this.runInAsyncScope, this, args) } this.pass() @@ -460,4 +452,36 @@ class Test extends AsyncResource { } } -module.exports = { kDefaultIndent, kSubtestsFailed, kTestCodeFailure, Test } +class ItTest extends Test { + constructor (opt) { super(opt) } // eslint-disable-line no-useless-constructor + getRunArgs () { + return { ctx: {}, args: [] } + } +} +class Suite extends Test { + constructor (options) { + super(options) + + this.runInAsyncScope(this.fn) + this.fn = () => {} + this.finished = true // Forbid adding subtests to this suite + } + + start () { + return this.run() + } + + async run () { + this.parent.activeSubtests++ + this.startTime = hrtime() + const subtests = this.skipped ? [] : this.subtests + await ArrayPrototypeReduce(subtests, async (prev, subtest) => { + await prev + await subtest.run() + }, PromiseResolve()) + this.pass() + this.postRun() + } +} + +module.exports = { kDefaultIndent, kSubtestsFailed, kTestCodeFailure, Test, Suite, ItTest } diff --git a/lib/internal/test_runner/utils.js b/lib/internal/test_runner/utils.js index a7f880a..647ec75 100644 --- a/lib/internal/test_runner/utils.js +++ b/lib/internal/test_runner/utils.js @@ -1,7 +1,15 @@ -// https://github.com/nodejs/node/blob/1aab13cad9c800f4121c1d35b554b78c1b17bdbd/lib/internal/test_runner/utils.js +// https://github.com/nodejs/node/blob/e2225ba8e1c00995c0f8bd56e607ea7c5b463ab9/lib/internal/test_runner/utils.js 'use strict' const { RegExpPrototypeExec } = require('#internal/per_context/primordials') const { basename } = require('path') +const { createDeferredPromise } = require('#internal/util') +const { + codes: { + ERR_TEST_FAILURE + } +} = require('#internal/errors') + +const kMultipleCallbackInvocations = 'multipleCallbackInvocations' const kSupportedFileExtensions = /\.[cm]?js$/ const kTestFilePattern = /((^test(-.+)?)|(.+[.\-_]test))\.[cm]?js$/ @@ -13,4 +21,37 @@ function isSupportedFileType (p) { return RegExpPrototypeExec(kSupportedFileExtensions, p) !== null } -module.exports = { isSupportedFileType, doesPathMatchFilter } +function createDeferredCallback () { + let calledCount = 0 + const { promise, resolve, reject } = createDeferredPromise() + const cb = (err) => { + calledCount++ + + // If the callback is called a second time, let the user know, but + // don't let them know more than once. + if (calledCount > 1) { + if (calledCount === 2) { + throw new ERR_TEST_FAILURE( + 'callback invoked multiple times', + kMultipleCallbackInvocations + ) + } + + return + } + + if (err) { + return reject(err) + } + + resolve() + } + + return { promise, cb } +} + +module.exports = { + createDeferredCallback, + doesPathMatchFilter, + isSupportedFileType +} diff --git a/lib/test.js b/lib/test.js index 1d8a781..964c97d 100644 --- a/lib/test.js +++ b/lib/test.js @@ -1,8 +1,10 @@ -// https://github.com/nodejs/node/blob/1aab13cad9c800f4121c1d35b554b78c1b17bdbd/lib/test.js +// https://github.com/nodejs/node/blob/e2225ba8e1c00995c0f8bd56e607ea7c5b463ab9/lib/test.js 'use strict' -const test = require('#internal/test_runner/harness') +const { test, describe, it } = require('#internal/test_runner/harness') module.exports = test module.exports.test = test +module.exports.describe = describe +module.exports.it = it diff --git a/test/message/test_runner_desctibe_it.js b/test/message/test_runner_desctibe_it.js new file mode 100644 index 0000000..c857668 --- /dev/null +++ b/test/message/test_runner_desctibe_it.js @@ -0,0 +1,303 @@ +// https://github.com/nodejs/node/blob/e2225ba8e1c00995c0f8bd56e607ea7c5b463ab9/test/message/test_runner_desctibe_it.js +// Flags: --no-warnings +'use strict' +require('../common') +const assert = require('node:assert') +const { describe, it } = require('#node:test') +const util = require('util') + +it.todo('sync pass todo', () => { + +}) + +it('sync pass todo with message', { todo: 'this is a passing todo' }, () => { +}) + +it.todo('sync fail todo', () => { + throw new Error('thrown from sync fail todo') +}) + +it('sync fail todo with message', { todo: 'this is a failing todo' }, () => { + throw new Error('thrown from sync fail todo with message') +}) + +it.skip('sync skip pass', () => { +}) + +it('sync skip pass with message', { skip: 'this is skipped' }, () => { +}) + +it('sync pass', () => { +}) + +it('sync throw fail', () => { + throw new Error('thrown from sync throw fail') +}) + +it.skip('async skip pass', async () => { +}) + +it('async pass', async () => { + +}) + +it('async throw fail', async () => { + throw new Error('thrown from async throw fail') +}) + +it('async assertion fail', async () => { + // Make sure the assert module is handled. + assert.strictEqual(true, false) +}) + +it('resolve pass', () => { + return Promise.resolve() +}) + +it('reject fail', () => { + return Promise.reject(new Error('rejected from reject fail')) +}) + +it('unhandled rejection - passes but warns', () => { + Promise.reject(new Error('rejected from unhandled rejection fail')) +}) + +it('async unhandled rejection - passes but warns', async () => { + Promise.reject(new Error('rejected from async unhandled rejection fail')) +}) + +it('immediate throw - passes but warns', () => { + setImmediate(() => { + throw new Error('thrown from immediate throw fail') + }) +}) + +it('immediate reject - passes but warns', () => { + setImmediate(() => { + Promise.reject(new Error('rejected from immediate reject fail')) + }) +}) + +it('immediate resolve pass', () => { + return new Promise((resolve) => { + setImmediate(() => { + resolve() + }) + }) +}) + +describe('subtest sync throw fail', () => { + it('+sync throw fail', () => { + throw new Error('thrown from subtest sync throw fail') + }) +}) + +it('sync throw non-error fail', async () => { + throw Symbol('thrown symbol from sync throw non-error fail') +}) + +describe('level 0a', { concurrency: 4 }, () => { + it('level 1a', async () => { + const p1a = new Promise((resolve) => { + setTimeout(() => { + resolve() + }, 1000) + }) + + return p1a + }) + + it('level 1b', async () => { + const p1b = new Promise((resolve) => { + resolve() + }) + + return p1b + }) + + it('level 1c', async () => { + const p1c = new Promise((resolve) => { + setTimeout(() => { + resolve() + }, 2000) + }) + + return p1c + }) + + it('level 1d', async () => { + const p1c = new Promise((resolve) => { + setTimeout(() => { + resolve() + }, 1500) + }) + + return p1c + }) + + const p0a = new Promise((resolve) => { + setTimeout(() => { + resolve() + }, 3000) + }) + + return p0a +}) + +describe('top level', { concurrency: 2 }, () => { + it('+long running', async () => { + return new Promise((resolve, reject) => { + setTimeout(resolve, 3000).unref() + }) + }) + + describe('+short running', async () => { + it('++short running', async () => {}) + }) +}) + +describe('invalid subtest - pass but subtest fails', () => { + setImmediate(() => { + it('invalid subtest fail', () => { + throw new Error('this should not be thrown') + }) + }) +}) + +it.skip('sync skip option', () => { + throw new Error('this should not be executed') +}) + +it('sync skip option with message', { skip: 'this is skipped' }, () => { + throw new Error('this should not be executed') +}) + +it('sync skip option is false fail', { skip: false }, () => { + throw new Error('this should be executed') +}) + +// A test with no arguments provided. +it() + +// A test with only a named function provided. +it(function functionOnly () {}) + +// A test with only an anonymous function provided. +it(() => {}) + +// A test with only a name provided. +it('test with only a name provided') + +// A test with an empty string name. +it('') + +// A test with only options provided. +it({ skip: true }) + +// A test with only a name and options provided. +it('test with a name and options provided', { skip: true }) + +// A test with only options and a function provided. +it({ skip: true }, function functionAndOptions () {}) + +// A test whose description needs to be escaped. +it('escaped description \\ # \\#\\') + +// A test whose skip message needs to be escaped. +it('escaped skip message', { skip: '#skip' }) + +// A test whose todo message needs to be escaped. +it('escaped todo message', { todo: '#todo' }) + +it('callback pass', (done) => { + setImmediate(done) +}) + +it('callback fail', (done) => { + setImmediate(() => { + done(new Error('callback failure')) + }) +}) + +it('sync t is this in test', function () { + assert.deepStrictEqual(this, {}) +}) + +it('async t is this in test', async function () { + assert.deepStrictEqual(this, {}) +}) + +it('callback t is this in test', function (done) { + assert.deepStrictEqual(this, {}) + done() +}) + +it('callback also returns a Promise', async (done) => { + throw new Error('thrown from callback also returns a Promise') +}) + +it('callback throw', (done) => { + throw new Error('thrown from callback throw') +}) + +it('callback called twice', (done) => { + done() + done() +}) + +it('callback called twice in different ticks', (done) => { + setImmediate(done) + done() +}) + +it('callback called twice in future tick', (done) => { + setImmediate(() => { + done() + done() + }) +}) + +it('callback async throw', (done) => { + setImmediate(() => { + throw new Error('thrown from callback async throw') + }) +}) + +it('callback async throw after done', (done) => { + setImmediate(() => { + throw new Error('thrown from callback async throw after done') + }) + + done() +}) + +it('custom inspect symbol fail', () => { + const obj = { + [util.inspect.custom] () { + return 'customized' + }, + foo: 1 + } + + throw obj +}) + +it('custom inspect symbol that throws fail', () => { + const obj = { + [util.inspect.custom] () { + throw new Error('bad-inspect') + }, + foo: 1 + } + + throw obj +}) + +describe('subtest sync throw fails', () => { + it('sync throw fails at first', () => { + throw new Error('thrown from subtest sync throw fails at first') + }) + it('sync throw fails at second', () => { + throw new Error('thrown from subtest sync throw fails at second') + }) +}) diff --git a/test/message/test_runner_desctibe_it.out b/test/message/test_runner_desctibe_it.out new file mode 100644 index 0000000..4b7fa4a --- /dev/null +++ b/test/message/test_runner_desctibe_it.out @@ -0,0 +1,520 @@ +TAP version 13 +# Subtest: sync pass todo +ok 1 - sync pass todo # TODO + --- + duration_ms: * + ... +# Subtest: sync pass todo with message +ok 2 - sync pass todo with message # TODO this is a passing todo + --- + duration_ms: * + ... +# Subtest: sync fail todo +not ok 3 - sync fail todo # TODO + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from sync fail todo' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + ... +# Subtest: sync fail todo with message +not ok 4 - sync fail todo with message # TODO this is a failing todo + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from sync fail todo with message' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + * + * + * + ... +# Subtest: sync skip pass +ok 5 - sync skip pass # SKIP + --- + duration_ms: * + ... +# Subtest: sync skip pass with message +ok 6 - sync skip pass with message # SKIP this is skipped + --- + duration_ms: * + ... +# Subtest: sync pass +ok 7 - sync pass + --- + duration_ms: * + ... +# Subtest: sync throw fail +not ok 8 - sync throw fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from sync throw fail' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + ... +# Subtest: async skip pass +ok 9 - async skip pass # SKIP + --- + duration_ms: * + ... +# Subtest: async pass +ok 10 - async pass + --- + duration_ms: * + ... +# Subtest: async throw fail +not ok 11 - async throw fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from async throw fail' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + ... +# Subtest: async assertion fail +not ok 12 - async assertion fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: |- + Expected values to be strictly equal: + + true !== false + + code: 'ERR_ASSERTION' + stack: |- + * + * + * + * + * + * + * + ... +# Subtest: resolve pass +ok 13 - resolve pass + --- + duration_ms: * + ... +# Subtest: reject fail +not ok 14 - reject fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'rejected from reject fail' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + ... +# Subtest: unhandled rejection - passes but warns +ok 15 - unhandled rejection - passes but warns + --- + duration_ms: * + ... +# Subtest: async unhandled rejection - passes but warns +ok 16 - async unhandled rejection - passes but warns + --- + duration_ms: * + ... +# Subtest: immediate throw - passes but warns +ok 17 - immediate throw - passes but warns + --- + duration_ms: * + ... +# Subtest: immediate reject - passes but warns +ok 18 - immediate reject - passes but warns + --- + duration_ms: * + ... +# Subtest: immediate resolve pass +ok 19 - immediate resolve pass + --- + duration_ms: * + ... +# Subtest: subtest sync throw fail + # Subtest: +sync throw fail + not ok 1 - +sync throw fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from subtest sync throw fail' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + ... + 1..1 +not ok 20 - subtest sync throw fail + --- + duration_ms: * + failureType: 'subtestsFailed' + error: '1 subtest failed' + code: 'ERR_TEST_FAILURE' + stack: |- + ... +# Subtest: sync throw non-error fail +not ok 21 - sync throw non-error fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'Symbol(thrown symbol from sync throw non-error fail)' + code: 'ERR_TEST_FAILURE' + stack: |- + ... +# Subtest: level 0a + # Subtest: level 1a + ok 1 - level 1a + --- + duration_ms: * + ... + # Subtest: level 1b + ok 2 - level 1b + --- + duration_ms: * + ... + # Subtest: level 1c + ok 3 - level 1c + --- + duration_ms: * + ... + # Subtest: level 1d + ok 4 - level 1d + --- + duration_ms: * + ... + 1..4 +ok 22 - level 0a + --- + duration_ms: * + ... +# Subtest: top level + # Subtest: +long running + ok 1 - +long running + --- + duration_ms: * + ... + # Subtest: +short running + # Subtest: ++short running + ok 1 - ++short running + --- + duration_ms: * + ... + 1..1 + ok 2 - +short running + --- + duration_ms: * + ... + 1..2 +ok 23 - top level + --- + duration_ms: * + ... +# Subtest: invalid subtest - pass but subtest fails +ok 24 - invalid subtest - pass but subtest fails + --- + duration_ms: * + ... +# Subtest: sync skip option +ok 25 - sync skip option # SKIP + --- + duration_ms: * + ... +# Subtest: sync skip option with message +ok 26 - sync skip option with message # SKIP this is skipped + --- + duration_ms: * + ... +# Subtest: sync skip option is false fail +not ok 27 - sync skip option is false fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'this should be executed' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + ... +# Subtest: +ok 28 - + --- + duration_ms: * + ... +# Subtest: functionOnly +ok 29 - functionOnly + --- + duration_ms: * + ... +# Subtest: +ok 30 - + --- + duration_ms: * + ... +# Subtest: test with only a name provided +ok 31 - test with only a name provided + --- + duration_ms: * + ... +# Subtest: +ok 32 - + --- + duration_ms: * + ... +# Subtest: +ok 33 - # SKIP + --- + duration_ms: * + ... +# Subtest: test with a name and options provided +ok 34 - test with a name and options provided # SKIP + --- + duration_ms: * + ... +# Subtest: functionAndOptions +ok 35 - functionAndOptions # SKIP + --- + duration_ms: * + ... +# Subtest: escaped description \\ \# \\\#\\ +ok 36 - escaped description \\ \# \\\#\\ + --- + duration_ms: * + ... +# Subtest: escaped skip message +ok 37 - escaped skip message # SKIP \#skip + --- + duration_ms: * + ... +# Subtest: escaped todo message +ok 38 - escaped todo message # TODO \#todo + --- + duration_ms: * + ... +# Subtest: callback pass +ok 39 - callback pass + --- + duration_ms: * + ... +# Subtest: callback fail +not ok 40 - callback fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'callback failure' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + ... +# Subtest: sync t is this in test +ok 41 - sync t is this in test + --- + duration_ms: * + ... +# Subtest: async t is this in test +ok 42 - async t is this in test + --- + duration_ms: * + ... +# Subtest: callback t is this in test +ok 43 - callback t is this in test + --- + duration_ms: * + ... +# Subtest: callback also returns a Promise +not ok 44 - callback also returns a Promise + --- + duration_ms: * + failureType: 'callbackAndPromisePresent' + error: 'passed a callback but also returned a Promise' + code: 'ERR_TEST_FAILURE' + stack: |- + ... +# Subtest: callback throw +not ok 45 - callback throw + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from callback throw' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + ... +# Subtest: callback called twice +not ok 46 - callback called twice + --- + duration_ms: * + failureType: 'multipleCallbackInvocations' + error: 'callback invoked multiple times' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + ... +# Subtest: callback called twice in different ticks +ok 47 - callback called twice in different ticks + --- + duration_ms: * + ... +# Subtest: callback called twice in future tick +not ok 48 - callback called twice in future tick + --- + duration_ms: * + failureType: 'uncaughtException' + error: 'callback invoked multiple times' + code: 'ERR_TEST_FAILURE' + stack: |- + * + ... +# Subtest: callback async throw +not ok 49 - callback async throw + --- + duration_ms: * + failureType: 'uncaughtException' + error: 'thrown from callback async throw' + code: 'ERR_TEST_FAILURE' + stack: |- + * + ... +# Subtest: callback async throw after done +ok 50 - callback async throw after done + --- + duration_ms: * + ... +# Subtest: custom inspect symbol fail +not ok 51 - custom inspect symbol fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'customized' + code: 'ERR_TEST_FAILURE' + stack: |- + ... +# Subtest: custom inspect symbol that throws fail +not ok 52 - custom inspect symbol that throws fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: |- + { + foo: 1, + [Symbol(nodejs.util.inspect.custom)]: [Function: [nodejs.util.inspect.custom]] + } + code: 'ERR_TEST_FAILURE' + stack: |- + ... +# Subtest: subtest sync throw fails + # Subtest: sync throw fails at first + not ok 1 - sync throw fails at first + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from subtest sync throw fails at first' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + ... + # Subtest: sync throw fails at second + not ok 2 - sync throw fails at second + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from subtest sync throw fails at second' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + ... + 1..2 +not ok 53 - subtest sync throw fails + --- + duration_ms: * + failureType: 'subtestsFailed' + error: '2 subtests failed' + code: 'ERR_TEST_FAILURE' + stack: |- + ... +# Subtest: invalid subtest fail +not ok 54 - invalid subtest fail + --- + duration_ms: * + failureType: 'parentAlreadyFinished' + error: 'test could not be started because its parent finished' + code: 'ERR_TEST_FAILURE' + stack: |- + * + ... +1..54 +# Warning: Test "unhandled rejection - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. +# Warning: Test "async unhandled rejection - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from async unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. +# Warning: Test "immediate throw - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: thrown from immediate throw fail" and would have caused the test to fail, but instead triggered an uncaughtException event. +# Warning: Test "immediate reject - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from immediate reject fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. +# Warning: Test "callback called twice in different ticks" generated asynchronous activity after the test ended. This activity created the error "Error [ERR_TEST_FAILURE]: callback invoked multiple times" and would have caused the test to fail, but instead triggered an uncaughtException event. +# Warning: Test "callback async throw after done" generated asynchronous activity after the test ended. This activity created the error "Error: thrown from callback async throw after done" and would have caused the test to fail, but instead triggered an uncaughtException event. +# tests 54 +# pass 23 +# fail 17 +# skipped 9 +# todo 5 +# duration_ms *