From 526fc7279451f66c620f0cf54c48b8640d847dd4 Mon Sep 17 00:00:00 2001 From: Timo Tijhof Date: Fri, 29 Mar 2024 11:51:12 -0700 Subject: [PATCH] All: Step 1 of site unification, import api content * Import content from https://github.com/qunitjs/qunit/tree/8a6723f7e2b4fad41779734301d16135c9e397ad/docs into api/ directory. * Modify the imported pages to add a `redirect_from` entry reflecting their previous URL on the api.qunitjs.com domain. For example, api.qunitjs.com/QUnit/test/ is imported here as qunitjs.com/api/QUnit/test/ with a redirect from qunitjs.com/QUnit/test/ This way, once we redirect api.qunitjs.com to qunitjs.com, the old path will be recognised and redirected accordingly from api.qunitjs.com/QUnit/test/ to qunitjs.com/api/QUnit/test/. * Modify pre-existing pages to refer to the in-repo Markdown files instead of raw URLs. This aids local testing, as well as makes sure that jekyll builds can automatically validate that all linked pages exist, and will output links with the canonical URL. Ref https://github.com/qunitjs/qunit/issues/1754 --- _config.yml | 2 - _data/projects.yml | 2 +- _data/sidebar_api.yml | 23 ++ _data/sitenav.yml | 3 +- api/QUnit/hooks.md | 39 ++ api/QUnit/index.md | 7 + api/QUnit/load.md | 50 +++ api/QUnit/module.md | 438 +++++++++++++++++++++ api/QUnit/start.md | 30 ++ api/QUnit/test.each.md | 115 ++++++ api/QUnit/test.md | 102 +++++ api/QUnit/test.only.md | 68 ++++ api/QUnit/test.skip.md | 55 +++ api/QUnit/test.todo.md | 58 +++ api/assert/async.md | 100 +++++ api/assert/deepEqual.md | 58 +++ api/assert/equal.md | 58 +++ api/assert/expect.md | 83 ++++ api/assert/false.md | 43 ++ api/assert/index.md | 15 + api/assert/notDeepEqual.md | 40 ++ api/assert/notEqual.md | 48 +++ api/assert/notOk.md | 43 ++ api/assert/notPropContains.md | 78 ++++ api/assert/notPropEqual.md | 57 +++ api/assert/notStrictEqual.md | 38 ++ api/assert/ok.md | 45 +++ api/assert/propContains.md | 77 ++++ api/assert/propEqual.md | 81 ++++ api/assert/pushResult.md | 87 ++++ api/assert/rejects.md | 120 ++++++ api/assert/step.md | 45 +++ api/assert/strictEqual.md | 45 +++ api/assert/throws.md | 110 ++++++ api/assert/timeout.md | 64 +++ api/assert/true.md | 44 +++ api/assert/verifySteps.md | 148 +++++++ api/async.md | 7 + api/callbacks/QUnit.begin.md | 77 ++++ api/callbacks/QUnit.done.md | 59 +++ api/callbacks/QUnit.log.md | 73 ++++ api/callbacks/QUnit.moduleDone.md | 41 ++ api/callbacks/QUnit.moduleStart.md | 37 ++ api/callbacks/QUnit.on.md | 144 +++++++ api/callbacks/QUnit.testDone.md | 57 +++ api/callbacks/QUnit.testStart.md | 40 ++ api/callbacks/index.md | 8 + api/config/altertitle.md | 27 ++ api/config/autostart.md | 72 ++++ api/config/collapse.md | 27 ++ api/config/current.md | 40 ++ api/config/failOnZeroTests.md | 27 ++ api/config/filter.md | 80 ++++ api/config/fixture.md | 27 ++ api/config/hidepassed.md | 27 ++ api/config/index.md | 40 ++ api/config/maxDepth.md | 27 ++ api/config/module.md | 38 ++ api/config/moduleId.md | 31 ++ api/config/modules.md | 41 ++ api/config/noglobals.md | 27 ++ api/config/notrycatch.md | 29 ++ api/config/reorder.md | 29 ++ api/config/requireExpects.md | 25 ++ api/config/scrolltop.md | 27 ++ api/config/seed.md | 33 ++ api/config/storage.md | 27 ++ api/config/testId.md | 31 ++ api/config/testTimeout.md | 25 ++ api/config/urlConfig.md | 89 +++++ api/deprecated.md | 7 + api/extension/QUnit.assert.md | 17 + api/extension/QUnit.dump.parse.md | 84 ++++ api/extension/QUnit.extend.md | 50 +++ api/extension/QUnit.onUncaughtException.md | 36 ++ api/extension/QUnit.push.md | 30 ++ api/extension/QUnit.stack.md | 45 +++ api/extension/index.md | 7 + api/index.md | 21 + api/removed.md | 7 + brand.md | 4 + cli.md | 16 +- docsearch.config.json | 3 +- img/QUnit-Logo-Large.png | Bin 0 -> 12522 bytes img/logo-qunit@2x.png | Bin 0 -> 9121 bytes img/logo-with-colored-dark-text.svg | 1 + index.md | 7 +- intro.md | 14 +- upgrade-guide-2.x.md | 46 ++- 89 files changed, 4262 insertions(+), 41 deletions(-) create mode 100644 _data/sidebar_api.yml create mode 100644 api/QUnit/hooks.md create mode 100644 api/QUnit/index.md create mode 100644 api/QUnit/load.md create mode 100644 api/QUnit/module.md create mode 100644 api/QUnit/start.md create mode 100644 api/QUnit/test.each.md create mode 100644 api/QUnit/test.md create mode 100644 api/QUnit/test.only.md create mode 100644 api/QUnit/test.skip.md create mode 100644 api/QUnit/test.todo.md create mode 100644 api/assert/async.md create mode 100644 api/assert/deepEqual.md create mode 100644 api/assert/equal.md create mode 100644 api/assert/expect.md create mode 100644 api/assert/false.md create mode 100644 api/assert/index.md create mode 100644 api/assert/notDeepEqual.md create mode 100644 api/assert/notEqual.md create mode 100644 api/assert/notOk.md create mode 100644 api/assert/notPropContains.md create mode 100644 api/assert/notPropEqual.md create mode 100644 api/assert/notStrictEqual.md create mode 100644 api/assert/ok.md create mode 100644 api/assert/propContains.md create mode 100644 api/assert/propEqual.md create mode 100644 api/assert/pushResult.md create mode 100644 api/assert/rejects.md create mode 100644 api/assert/step.md create mode 100644 api/assert/strictEqual.md create mode 100644 api/assert/throws.md create mode 100644 api/assert/timeout.md create mode 100644 api/assert/true.md create mode 100644 api/assert/verifySteps.md create mode 100644 api/async.md create mode 100644 api/callbacks/QUnit.begin.md create mode 100644 api/callbacks/QUnit.done.md create mode 100644 api/callbacks/QUnit.log.md create mode 100644 api/callbacks/QUnit.moduleDone.md create mode 100644 api/callbacks/QUnit.moduleStart.md create mode 100644 api/callbacks/QUnit.on.md create mode 100644 api/callbacks/QUnit.testDone.md create mode 100644 api/callbacks/QUnit.testStart.md create mode 100644 api/callbacks/index.md create mode 100644 api/config/altertitle.md create mode 100644 api/config/autostart.md create mode 100644 api/config/collapse.md create mode 100644 api/config/current.md create mode 100644 api/config/failOnZeroTests.md create mode 100644 api/config/filter.md create mode 100644 api/config/fixture.md create mode 100644 api/config/hidepassed.md create mode 100644 api/config/index.md create mode 100644 api/config/maxDepth.md create mode 100644 api/config/module.md create mode 100644 api/config/moduleId.md create mode 100644 api/config/modules.md create mode 100644 api/config/noglobals.md create mode 100644 api/config/notrycatch.md create mode 100644 api/config/reorder.md create mode 100644 api/config/requireExpects.md create mode 100644 api/config/scrolltop.md create mode 100644 api/config/seed.md create mode 100644 api/config/storage.md create mode 100644 api/config/testId.md create mode 100644 api/config/testTimeout.md create mode 100644 api/config/urlConfig.md create mode 100644 api/deprecated.md create mode 100644 api/extension/QUnit.assert.md create mode 100644 api/extension/QUnit.dump.parse.md create mode 100644 api/extension/QUnit.extend.md create mode 100644 api/extension/QUnit.onUncaughtException.md create mode 100644 api/extension/QUnit.push.md create mode 100644 api/extension/QUnit.stack.md create mode 100644 api/extension/index.md create mode 100644 api/index.md create mode 100644 api/removed.md create mode 100644 img/QUnit-Logo-Large.png create mode 100644 img/logo-qunit@2x.png create mode 100644 img/logo-with-colored-dark-text.svg diff --git a/_config.yml b/_config.yml index 826db84..7f87289 100644 --- a/_config.yml +++ b/_config.yml @@ -26,7 +26,6 @@ exclude: - vendor - build - # Theme settings # # Amethyst theme options are documented at: @@ -54,7 +53,6 @@ amethyst: collection: qunitjs_com search_only_api_key: Zh8mMgohXECel9wjPwqT7lekLSG3OCgz - # Blog archives # # Docs: https://github.com/jekyll/jekyll-archives/ diff --git a/_data/projects.yml b/_data/projects.yml index b050bed..f1c94a3 100644 --- a/_data/projects.yml +++ b/_data/projects.yml @@ -224,7 +224,7 @@ projects: href: https://github.com/videojs/http-streaming - name: mux.js href: https://github.com/videojs/mux.js - + - name: Wikibase href: https://wikiba.se/ sub: diff --git a/_data/sidebar_api.yml b/_data/sidebar_api.yml new file mode 100644 index 0000000..13ebd35 --- /dev/null +++ b/_data/sidebar_api.yml @@ -0,0 +1,23 @@ +- group: main + expand: initial + +- group: assert + +- group: callbacks + +- group: async + # These pages are all in at least one other group. + # It exists for discoverability, but we don't need + # to highlight its pages in two places. + expand: false + +- title: Configuration + group: config + +- group: extension + +- group: deprecated + expand: false + +# - group: removed +# expand: false diff --git a/_data/sitenav.yml b/_data/sitenav.yml index aa115d8..7a685f0 100644 --- a/_data/sitenav.yml +++ b/_data/sitenav.yml @@ -1,4 +1,5 @@ - name: Guides + href: /guides/ sub: - name: Getting Started href: /intro/ @@ -9,7 +10,7 @@ - name: 2.x Upgrade Guide href: /upgrade-guide-2.x/ - name: Documentation - href: https://api.qunitjs.com/ + href: /api/ - name: About href: /about/ sub: diff --git a/api/QUnit/hooks.md b/api/QUnit/hooks.md new file mode 100644 index 0000000..3cf11c2 --- /dev/null +++ b/api/QUnit/hooks.md @@ -0,0 +1,39 @@ +--- +layout: page-api +title: QUnit.hooks +excerpt: Add global callbacks to run before or after each test. +groups: + - extension +redirect_from: + - "/QUnit/hooks/" +version_added: "2.18.0" +--- + +`QUnit.hooks.beforeEach( callback )`
+`QUnit.hooks.afterEach( callback )` + +Register a global callback to run before or after each test. + +| parameter | description | +|-----------|-------------| +| callback (function) | Callback to execute. Called with an [assert](../assert/index.md) argument. | + +This is the equivalent of applying a `QUnit.module()` hook to all modules and all tests, including global tests that are not associated with any module. + +Similar to module hooks, global hooks support async functions or returning a Promise, which will be waited for before QUnit continues executing tests. Each global hook also has access to the same `assert` object and test context as the [QUnit.test](./test.md) that the hook is running for. + +For more details about hooks, refer to [QUnit.module § Hooks](./module.md#hooks). + +## Examples + +```js +QUnit.hooks.beforeEach(function () { + this.app = new MyApp(); +}); + +QUnit.hooks.afterEach(async function (assert) { + assert.deepEqual([], await this.app.getErrors(), 'MyApp errors'); + + MyApp.reset(); +}); +``` diff --git a/api/QUnit/index.md b/api/QUnit/index.md new file mode 100644 index 0000000..b351055 --- /dev/null +++ b/api/QUnit/index.md @@ -0,0 +1,7 @@ +--- +layout: group +group: main +title: Main methods +redirect_from: + - "/QUnit/" +--- diff --git a/api/QUnit/load.md b/api/QUnit/load.md new file mode 100644 index 0000000..07f1fa6 --- /dev/null +++ b/api/QUnit/load.md @@ -0,0 +1,50 @@ +--- +layout: page-api +title: QUnit.load() +excerpt: Inform the test runner that code has finished loading. +groups: + - deprecated +redirect_from: + - "/QUnit/load/" +version_added: "1.0.0" +version_deprecated: "unreleased" +--- + +`QUnit.load()` + +Inform the test runner that your source code and tests have finished loading. + +This method was used in conjunction with the [`QUnit.config.autostart`](../config/autostart.md) option in a web browser, to indicate when your custom way of loading scripts is complete. + +As of [QUnit 2.1.1](https://github.com/qunitjs/qunit/releases/tag/2.1.1), calls to `QUnit.load()` are no longer needed. Existing calls are usually ignored and safe to remove. + +

This method is __deprecated__. Remove call, or replace by a single call to [`QUnit.start()`](./start.md).

+ +## Changelog + +| UNRELEASED | Deprecated. Use [`QUnit.start()`](./start.md) instead. +| [QUnit 2.1.1](https://github.com/qunitjs/qunit/releases/tag/2.1.1) | `QUnit.start()` no longer requires calling `QUnit.load()` first. + +## Migration guide + +If you still call `QUnit.load()` with QUnit 2.2 or later, the call is usually redundant and safe to remove. + +### If you call both `QUnit.load()` and `QUnit.start()` + +If your project started with QUnit 1.x, and you use `QUnit.config.autostart = false`, then you might be calling both of these methods. In the QUnit 1.x era, [`QUnit.start()`](./start.md) required that you also call `QUnit.load()` first. + +This is no longer needed since [QUnit 2.1.1](https://github.com/qunitjs/qunit/releases/tag/2.1.1), and the call to `QUnit.load()` is safe to remove. + +### If you call `QUnit.load()` + +Prior to QUnit 2.21, the built-in HTML Reporter called `QUnit.load()` from the window.onload event, which in turn gracefully calls `QUnit.start()` if it has not been called already. + +If your test runner works in a similar way, call [`QUnit.start()`](./start.md) instead of `QUnit.load()`. This will solve the deprecation warning and prepares you for QUnit 3. + +### Karma runner + +``` +QUnit.load is deprecated and will be removed in QUnit 3.0. +``` + +If you encounter this warning in Karma output, upgrade to [karma-qunit](https://github.com/karma-runner/karma-qunit) 4.2.0 or later, which [fixes](https://github.com/karma-runner/karma-qunit/pull/184) this warning. diff --git a/api/QUnit/module.md b/api/QUnit/module.md new file mode 100644 index 0000000..66e5e1a --- /dev/null +++ b/api/QUnit/module.md @@ -0,0 +1,438 @@ +--- +layout: page-api +title: QUnit.module() +excerpt: Group related tests under a common label. +groups: + - main +redirect_from: + - "/QUnit.module/" + - "/QUnit/module/" + - "/module/" +version_added: "1.0.0" +--- + +`QUnit.module( name )`
+`QUnit.module( name, nested )`
+`QUnit.module( name, options )`
+`QUnit.module( name, options, nested )` + +Group related tests under a common label. + +| parameter | description | +|-----------|-------------| +| `name` (string) | Label for this group of tests. | +| [`options`](#options-object) (object) | Set hook callbacks. | +| [`nested`](#nested-scope) (function) | A scope to create nested modules and/or add hooks functionally. | + +All tests inside a module will be grouped under that module. Tests can be added to a module using the [QUnit.test](./test.md) method. Modules help organize, select, and filter tests to run. See [§ Organizing your tests](#organizing-your-tests). + +Modules can be nested inside other modules. In the output, tests are generally prefixed by the names of all parent modules. E.g. "Grandparent > Parent > Child > my test". See [§ Nested module scope](#nested-module-scope). + +The `QUnit.module.only()`, `QUnit.module.skip()`, and `QUnit.module.todo()` methods are aliases for `QUnit.module()` that apply the behaviour of [`QUnit.test.only()`](./test.only.md), [`QUnit.test.skip()`](./test.skip.md) or [`QUnit.test.todo()`](./test.todo.md) to all a module's tests at once. + +### Hooks + +You can use hooks to prepare fixtures, or run other setup and teardown logic. Hooks can run around individual tests, or around a whole module. + +* `before`: Run a callback before the first test. +* `beforeEach`: Run a callback before each test. +* `afterEach`: Run a callback after each test. +* `after`: Run a callback after the last test. + +You can add hooks via the `hooks` parameter of a [scoped module](#nested-scope), or in the module [`options`](#options-object) object, or globally for all tests via [QUnit.hooks](./hooks.md). + +Hooks that are added to a module, will also apply to tests in any nested modules. + +Hooks that run _before_ a test, are ordered from outer-most to inner-most, in the order that they are added. This means that a test will first run any global beforeEach hooks, then the hooks of parent modules, and finally the hooks added to the immediate module the test is a part of. Hooks that run _after_ a test, are ordered from inner-most to outer-most, in the reverse order. In other words, `before` and `beforeEach` callbacks form a [queue][], while `afterEach` and `after` form a [stack][]. + +[queue]: https://en.wikipedia.org/wiki/Queue_%28abstract_data_type%29 +[stack]: https://en.wikipedia.org/wiki/Stack_%28abstract_data_type%29 + +#### Hook callback + +A hook callback may be an async function, and may return a Promise or any other then-able. QUnit will automatically wait for your hook's asynchronous work to finish before continuing to execute the tests. + +Each hook has access to the same `assert` object, and test context via `this`, as the [QUnit.test](./test.md) that the hook is running for. Example: [§ Using the test context](#using-the-test-context). + +| parameter | description | +|-----------|-------------| +| `assert` (object) | An [Assert](../assert/index.md) object. | + +

It is discouraged to dynamically create a new [QUnit.test](./test.md) from within a hook. In order to satisfy the requirement for the `after` hook to only run once and to be the last hook in a module, QUnit may associate dynamically defined tests with the parent module instead, or as global test. It is recommended to define any dynamic tests via [`QUnit.begin()`](../callbacks/QUnit.begin.md).

+ +### Options object + +You can use the options object to add [hooks](#hooks). + +| name | description | +|-----------|-------------| +| `before` (function) | Runs before the first test. | +| `beforeEach` (function) | Runs before each test. | +| `afterEach` (function) | Runs after each test. | +| `after` (function) | Runs after the last test. | + +Properties on the module options object are copied over to the test context object at the start of each test. Such properties can also be changed from the hook callbacks. See [§ Using the test context](#using-the-test-context). + +Example: [§ Declaring module options](#declaring-module-options). + +### Nested scope + +Modules can be nested to group tests under a common label within a parent module. + +The module scope is given a `hooks` object which can be used to procedurally add [hooks](#hooks). + +| parameter | description | +|-----------|-------------| +| `hooks` (object) | An object for adding hooks. | + +Example: [§ Nested module scope](#nested-module-scope). + +## Changelog + +| [QUnit 2.4](https://github.com/qunitjs/qunit/releases/tag/2.4.0) | The `QUnit.module.only()`, `QUnit.module.skip()`, and `QUnit.module.todo()` aliases were introduced. +| [QUnit 2.0](https://github.com/qunitjs/qunit/releases/tag/2.0.0) | The `before` and `after` options were introduced. +| [QUnit 1.20](https://github.com/qunitjs/qunit/releases/tag/1.20.0) | The `nested` scope feature was introduced. +| [QUnit 1.16](https://github.com/qunitjs/qunit/releases/tag/1.16.0) | The `beforeEach` and `afterEach` options were introduced.
The `setup` and `teardown` options were deprecated in QUnit 1.16 and removed in QUnit 2.0. + +## Examples + +### Organizing your tests + +If `QUnit.module` is called without a `nested` callback argument, all subsequently defined tests will be grouped into that module until another module is defined. + +```js +QUnit.module('Group A'); + +QUnit.test('basic test example 1', function (assert) { + assert.true(true, 'this is fine'); +}); +QUnit.test('basic test example 2', function (assert) { + assert.true(true, 'this is also fine'); +}); + +QUnit.module('Group B'); + +QUnit.test('basic test example 3', function (assert) { + assert.true(true, 'this is fine'); +}); +QUnit.test('basic test example 4', function (assert) { + assert.true(true, 'this is also fine'); +}); +``` + +Using modern syntax: + +```js +const { test } = QUnit; + +QUnit.module('Group A'); + +test('basic test example', assert => { + assert.true(true, 'this is fine'); +}); +test('basic test example 2', assert => { + assert.true(true, 'this is also fine'); +}); + +QUnit.module('Group B'); + +test('basic test example 3', assert => { + assert.true(true, 'this is fine'); +}); +test('basic test example 4', assert => { + assert.true(true, 'this is also fine'); +}); +``` + +### Declaring module options + +```js +QUnit.module('module A', { + before: function () { + // prepare something once for all tests + }, + beforeEach: function () { + // prepare something before each test + }, + afterEach: function () { + // clean up after each test + }, + after: function () { + // clean up once after all tests are done + } +}); +``` + +### Nested module scope + +```js +const { test } = QUnit; + +QUnit.module('Group A', hooks => { + test('basic test example', assert => { + assert.true(true, 'this is fine'); + }); + + test('basic test example 2', assert => { + assert.true(true, 'this is also fine'); + }); +}); + +QUnit.module('Group B', hooks => { + test('basic test example 3', assert => { + assert.true(true, 'this is fine'); + }); + + test('basic test example 4', assert => { + assert.true(true, 'this is also fine'); + }); +}); +``` + +### Hooks on nested modules + +Use `before`/`beforeEach` hooks are queued for nested modules. `after`/`afterEach` hooks are stacked on nested modules. + +```js +const { test } = QUnit; + +QUnit.module('My Group', hooks => { + // It is valid to call the same hook methods more than once. + hooks.beforeEach(assert => { + assert.ok(true, 'beforeEach called'); + }); + + hooks.afterEach(assert => { + assert.ok(true, 'afterEach called'); + }); + + test('with hooks', assert => { + // 1 x beforeEach + // 1 x afterEach + assert.expect(2); + }); + + QUnit.module('Nested Group', hooks => { + // This will run after the parent module's beforeEach hook + hooks.beforeEach(assert => { + assert.ok(true, 'nested beforeEach called'); + }); + + // This will run before the parent module's afterEach + hooks.afterEach(assert => { + assert.ok(true, 'nested afterEach called'); + }); + + test('with nested hooks', assert => { + // 2 x beforeEach (parent, current) + // 2 x afterEach (current, parent) + assert.expect(4); + }); + }); +}); +``` + +### Using the test context + +The test context object is exposed to hook callbacks. + +```js +QUnit.module('Machine Maker', { + beforeEach: function () { + this.maker = new Maker(); + this.parts = ['wheels', 'motor', 'chassis']; + } +}); + +QUnit.test('makes a robot', function (assert) { + this.parts.push('arduino'); + assert.equal(this.maker.build(this.parts), 'robot'); + assert.deepEqual(this.maker.log, ['robot']); +}); + +QUnit.test('makes a car', function (assert) { + assert.equal(this.maker.build(this.parts), 'car'); + this.maker.duplicate(); + assert.deepEqual(this.maker.log, ['car', 'car']); +}); +``` + +The test context is also available when using the nested scope. Beware that use of the `this` binding is not available +in arrow functions. + +```js +const { test } = QUnit; + +QUnit.module('Machine Maker', hooks => { + hooks.beforeEach(function () { + this.maker = new Maker(); + this.parts = ['wheels', 'motor', 'chassis']; + }); + + test('makes a robot', function (assert) { + this.parts.push('arduino'); + assert.equal(this.maker.build(this.parts), 'robot'); + assert.deepEqual(this.maker.log, ['robot']); + }); + + test('makes a car', function (assert) { + assert.equal(this.maker.build(this.parts), 'car'); + this.maker.duplicate(); + assert.deepEqual(this.maker.log, ['car', 'car']); + }); +}); +``` + +It might be more convenient to use JavaScript's own lexical scope instead: + +```js +const { test } = QUnit; + +QUnit.module('Machine Maker', hooks => { + let maker; + let parts; + hooks.beforeEach(() => { + maker = new Maker(); + parts = ['wheels', 'motor', 'chassis']; + }); + + test('makes a robot', assert => { + parts.push('arduino'); + assert.equal(maker.build(parts), 'robot'); + assert.deepEqual(maker.log, ['robot']); + }); + + test('makes a car', assert => { + assert.equal(maker.build(parts), 'car'); + maker.duplicate(); + assert.deepEqual(maker.log, ['car', 'car']); + }); +}); +``` + +### Module hook with Promise + +An example of handling an asynchronous `then`able Promise result in hooks. This example uses an [ES6 Promise][] interface that is fulfilled after connecting to or disconnecting from database. + +[ES6 Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise + +```js +QUnit.module('Database connection', { + before: function () { + return new Promise(function (resolve, reject) { + DB.connect(function (err) { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + }, + after: function () { + return new Promise(function (resolve, reject) { + DB.disconnect(function (err) { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + } +}); +``` + +### Only run a subset of tests + +Use `QUnit.module.only()` to treat an entire module's tests as if they used [`QUnit.test.only`](./test.only.md) instead of [`QUnit.test`](./test.md). + +```js +QUnit.module('Robot', hooks => { + // ... +}); + +// Only execute this module when developing the feature, +// skipping tests from other modules. +QUnit.module.only('Android', hooks => { + let android; + hooks.beforeEach(() => { + android = new Android(); + }); + + QUnit.test('Say hello', assert => { + assert.strictEqual(android.hello(), 'Hello, my name is AN-2178!'); + }); + + QUnit.test('Basic conversation', assert => { + android.loadConversationData({ + Hi: 'Hello', + "What's your name?": 'My name is AN-2178.', + 'Nice to meet you!': 'Nice to meet you too!', + '...': '...' + }); + + assert.strictEqual( + android.answer("What's your name?"), + 'My name is AN-2178.' + ); + }); + + // ... +}); +``` + +Use `QUnit.module.skip()` to treat an entire module's tests as if they used [`QUnit.test.skip`](./test.skip.md) instead of [`QUnit.test`](./test.md). + +```js +QUnit.module('Robot', hooks => { + // ... +}); + +// Skip this module's tests. +// For example if the android tests are failing due to unsolved problems. +QUnit.module.skip('Android', hooks => { + let android; + hooks.beforeEach(() => { + android = new Android(); + }); + + QUnit.test('Say hello', assert => { + assert.strictEqual(android.hello(), 'Hello, my name is AN-2178!'); + }); + + QUnit.test('Basic conversation', assert => { + // ... + assert.strictEqual( + android.answer('Nice to meet you!'), + 'Nice to meet you too!' + ); + }); + + // ... +}); +``` + +Use `QUnit.module.todo()` to denote a feature that is still under development, and is known to not yet be passing all its tests. This treats an entire module's tests as if they used [`QUnit.test.todo`](./test.todo.md) instead of [`QUnit.test`](./test.md). + +```js +QUnit.module.todo('Robot', hooks => { + let robot; + hooks.beforeEach(() => { + robot = new Robot(); + }); + + QUnit.test('Say', assert => { + // Currently, it returns undefined + assert.strictEqual(robot.say(), "I'm Robot FN-2187"); + }); + + QUnit.test('Move arm', assert => { + // Move the arm to point (75, 80). Currently, each throws a NotImplementedError + robot.moveArmTo(75, 80); + assert.deepEqual(robot.getPosition(), { x: 75, y: 80 }); + }); + + // ... +}); +``` diff --git a/api/QUnit/start.md b/api/QUnit/start.md new file mode 100644 index 0000000..aa12966 --- /dev/null +++ b/api/QUnit/start.md @@ -0,0 +1,30 @@ +--- +layout: page-api +title: QUnit.start() +excerpt: Start the test runner. +groups: + - main + - async +redirect_from: + - "/QUnit/start/" + - "/start/" +version_added: "1.0.0" +--- + +`QUnit.start()` + +Call this method to start the test runner. This indicates that all relevant source code has been loaded and all tests have been defined. + +In most environments this is **automatically called** and you do not need to call it. This includes testing via the HTML Runner and the QUnit CLI. + +If you build a custom test runner (such in SpiderMonkey or Node.js), or if you disable `QUnit.config.autostart` and load test files asynchronously (with AMD, RequireJS, or ESM dynamic imports), then you need to call this once after your test files have been loaded. + +See [`QUnit.config.autostart`](../config/autostart.md) for detailed examples of how to use `QUnit.start()`. + +

Prior to QUnit 1.16, this method was used for resuming an async `QUnit.test()` function, as complement to `QUnit.stop()`. To resume asynchronous tests, use [`assert.async()`](../assert/async.md) instead.

+ +## Changelog + +| [QUnit 2.1.1](https://github.com/qunitjs/qunit/releases/tag/2.1.1) | `QUnit.start()` no longer requires calling [`QUnit.load()`](./load.md) first. +| [QUnit 2.0](https://github.com/qunitjs/qunit/releases/tag/2.0.0) | Support for calling `start()` to resume an async test was removed. ([Migration guide](../../upgrade-guide-2.x.md#introducing-assertasync)) +| [QUnit 1.16](https://github.com/qunitjs/qunit/releases/tag/1.16.0) | Use of `start()` to resume an async test was deprecated in favour of [`assert.async()`](../assert/async.md). diff --git a/api/QUnit/test.each.md b/api/QUnit/test.each.md new file mode 100644 index 0000000..0782192 --- /dev/null +++ b/api/QUnit/test.each.md @@ -0,0 +1,115 @@ +--- +layout: page-api +title: QUnit.test.each() +excerpt: Add tests using a data provider. +groups: + - main +redirect_from: + - "/QUnit/test.each/" +version_added: "2.16.0" +--- + +`QUnit.test.each( name, dataset, callback )`
+`QUnit.test.only.each( name, dataset, callback )`
+`QUnit.test.skip.each( name, dataset, callback )`
+`QUnit.test.todo.each( name, dataset, callback )` + +Add tests using a data provider. + +| parameter | description | +|-----------|-------------| +| `name` (string) | Title of unit being tested | +| `dataset` (array or object) | Array or object of data values passed to each test case | +| `callback` (function) | Function that performs the test | + +### Callback parameters + +| parameter | description | +|-----------|-------------| +| `assert` (object) | A new instance object with the [assertion methods](../assert/index.md) | +| `data` (any) | Data value | + +Use this method to add multiple tests that are similar, but with different data passed in. + +`QUnit.test.each()` generates multiple calls to [`QUnit.test()`](./test.md) internally, and has all the same capabilities such support for async functions, returning a Promise, and the `assert` argument. + +Each test case is passed one value of your dataset. + +The [`only`](./test.only.md), [`skip`](./test.skip.md), and [`todo`](./test.todo.md) variants are also available, as `QUnit.test.only.each`, `QUnit.test.skip.each`, and `QUnit.test.todo.each` respectively. + +## Examples + +### Basic data provider + +```js +function isEven (x) { + return x % 2 === 0; +} + +QUnit.test.each('isEven()', [2, 4, 6], (assert, data) => { + assert.true(isEven(data), `${data} is even`); +}); +``` + +### Array data provider + +The original array is passed to your callback. [Array destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) can be used to unpack the data array, directly from the callback signature. + + +```js +function square (x) { + return x * x; +} + +QUnit.test.each('square()', [ + [2, 4], + [3, 9] +], (assert, [value, expected]) => { + assert.equal(square(value), expected, `${value} squared`); +}); +``` + +### Object data provider + +```js +QUnit.test.each('isEven()', { + caseEven: [2, true], + caseNotEven: [3, false] +}, (assert, [value, expected]) => { + assert.strictEqual(isEven(value), expected); +}); +``` + +### Async functions with `each()` + +```js +function isEven (x) { + return x % 2 === 0; +} + +async function isAsyncEven (x) { + return isEven(x); +} + +QUnit.test.each('isAsyncEven()', [2, 4], async (assert, data) => { + assert.true(await isAsyncEven(data), `${data} is even`); +}); +``` + +Or in classic ES5 syntax, by returning a Promise from each callback: + +```js +function isEven (x) { + return x % 2 === 0; +} + +function isAsyncEven (x) { + return Promise.resolve(isEven(x)); +} + +QUnit.test.each('isAsyncEven()', [2, 4], function (assert, data) { + return isAsyncEven(data).then(function (result) { + assert.true(result, data + ' is even'); + }); +}); +``` diff --git a/api/QUnit/test.md b/api/QUnit/test.md new file mode 100644 index 0000000..1275b75 --- /dev/null +++ b/api/QUnit/test.md @@ -0,0 +1,102 @@ +--- +layout: page-api +title: QUnit.test() +excerpt: Define a test. +groups: + - main + - async +redirect_from: + - "/QUnit.asyncTest/" + - "/QUnit.test/" + - "/QUnit/test/" + - "/asyncTest/" + - "/test/" +version_added: "1.0.0" +--- + +`QUnit.test( name, callback )` + +Define a test using `QUnit.test()`. + +| parameter | description | +|-----------|-------------| +| `name` (string) | Title of unit being tested | +| `callback` (function) | Function that performs the test | + +### Callback parameters + +| parameter | description | +|-----------|-------------| +| `assert` (object) | An [Assert](../assert/index.md) object | + +The `assert` argument to the callback contains all of QUnit's [assertion methods](../assert/index.md). Use this to make your test assertions. + +`QUnit.test()` can automatically handle the asynchronous resolution of a Promise on your behalf if you return a "then-able" Promise as the result of your callback function. + +See also: +* [`QUnit.test.only()`](./test.only.md) +* [`QUnit.test.skip()`](./test.skip.md) +* [`QUnit.test.todo()`](./test.todo.md) + +## Changelog + +| [QUnit 1.16](https://github.com/qunitjs/qunit/releases/tag/1.16.0) | Added support for async functions, and returning of a Promise. + +## Examples + +### Example: Standard test + +A practical example, using the assert argument. + +```js +function square (x) { + return x * x; +} + +QUnit.test('square()', assert => { + assert.equal(square(2), 4); + assert.equal(square(3), 9); +}); +``` + +### Example: Async test + +Following the example above, `QUnit.test` also supports JS [async functions][] syntax out of the box. + +[async functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function + +```js +QUnit.test('Test with async-await', async assert => { + const a = await fetchSquare(2); + const b = await fetchSquare(3); + + assert.equal(a, 4); + assert.equal(b, 9); + assert.equal(await fetchSquare(5), 25); +}); +``` + + +### Example: Test with Promise + +In ES5 and older environments, you can also return a [Promise] from your standard test function. This also supports other then-able, values such as `jQuery.Deferred`, and Bluebird Promise. + +This example returns a Promise that is resolved after waiting for 1 second. + +[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise + +```js +function fetchSquare (x) { + return new Promise(function (resolve) { + setTimeout(function () { + resolve(x * x); + }, 1000); + }); +} + +QUnit.test('Test with Promise', function (assert) { + return fetchSquare(3).then(function (result) { + assert.equal(result, 9); + }); +}); +``` diff --git a/api/QUnit/test.only.md b/api/QUnit/test.only.md new file mode 100644 index 0000000..eb6cba7 --- /dev/null +++ b/api/QUnit/test.only.md @@ -0,0 +1,68 @@ +--- +layout: page-api +title: QUnit.test.only() +excerpt: Add a test that is exclusively run. +groups: + - main +redirect_from: + - "/QUnit.only/" + - "/QUnit/only/" + - "/QUnit/test.only/" +version_added: "1.20.0" +--- + +`QUnit.test.only( name, callback )`
+`QUnit.only( name, callback )` + +Add a test that is exclusively run, preventing other tests from running unless they are also defined in this way. + +| parameter | description | +|-----------|-------------| +| `name` (string) | Title of unit being tested | +| `callback` (function) | Function that performs the test | + +### Callback parameters + +| parameter | description | +|-----------|-------------| +| `assert` (object) | A new instance object with the [assertion methods](../assert/index.md) | + +Use this method to focus your test suite on specific tests. `QUnit.test.only` will cause any other tests in your suite to be ignored. + +This method is an alternative to re-running individual tests from the HTML reporter interface, and can be especially useful to set your filter upfront without first running the test suite in a browser, e.g. in a codebase with many long-running tests. + +It can also be used as alternative to the `--filter` CLI option. If you have a specific test in front of you in your text editor, you set the "only" flag on this test directly by using `QUnit.test.only`, without needing to copy or otherwise match the test name via the `--filter` option. Setting the "only" flag in this way, is similar to how you might use the `debugger` keyword to interact with browser devtools. + +When debugging a larger area of code, you may want to expand your filter to run all tests under a given module. You can use[`QUnit.module.only()`](./module.md) to automatically mark all tests inside that module as "only" tests. + +## Changelog + +| [QUnit 2.12](https://github.com/qunitjs/qunit/releases/tag/2.12.0) | The `QUnit.only()` method was renamed to `QUnit.test.only()`.
Use of `QUnit.only()` remains supported as an alias. +| [QUnit 1.20](https://github.com/qunitjs/qunit/releases/tag/1.20.0) | The `QUnit.only()` method was introduced. + +## Examples + +How to use `QUnit.test.only` to filter which tests are run. + +```js +QUnit.module('robot', hooks => { + let robot; + hooks.beforeEach(() => { + robot = new Robot(); + }); + + QUnit.test('say()', assert => { + assert.true(robot.say('Hello')); + }); + + // Run only this test + // For example, you are working on changing this method. + QUnit.test.only('laser()', assert => { + assert.true(robot.laser()); + }); + + QUnit.test('take()', assert => { + assert.true(robot.take(5)); + }); +}); +``` diff --git a/api/QUnit/test.skip.md b/api/QUnit/test.skip.md new file mode 100644 index 0000000..61da001 --- /dev/null +++ b/api/QUnit/test.skip.md @@ -0,0 +1,55 @@ +--- +layout: page-api +title: QUnit.test.skip() +excerpt: Add a test that will be skipped. +groups: + - main +redirect_from: + - "/QUnit.skip/" + - "/QUnit/skip/" + - "/QUnit/test.skip/" +version_added: "1.16.0" +--- + +`QUnit.test.skip( name, callback )`
+`QUnit.skip( name, callback )` + +Add a test that will be skipped during the run. + +| parameter | description | +|-----------|-------------| +| `name` (string) | Title of unit being tested | +| `callback` (function) | Function that performs the test | + +Use this method to disable a [`QUnit.test()`](./test.md), as alternative to commenting out the test. + +This test will be listed in the results as a "skipped" test. The callback and the respective module's hooks will not run. + +As a codebase becomes bigger, you may sometimes want to temporarily disable an entire group of tests at once. You can use [`QUnit.module.skip()`](./module.md) to recursively skip all tests in the same module. + +## Changelog + +| [QUnit 2.12](https://github.com/qunitjs/qunit/releases/tag/2.12.0) | The `QUnit.skip()` method was renamed to `QUnit.test.skip()`.
Use of `QUnit.skip()` remains supported as an alias. +| [QUnit 1.16](https://github.com/qunitjs/qunit/releases/tag/1.16.0) | The `QUnit.skip()` method was introduced. + +## Examples + +How to use `skip` as a placeholder for future or temporarily broken tests. + +```js +QUnit.module('robot', hooks => { + let robot; + hooks.beforeEach(() => { + robot = new Robot(); + }); + + QUnit.test('say', assert => { + assert.strictEqual(robot.say(), 'Exterminate!'); + }); + + // Robot does not yet have a laser() method yet, skip this test for now + QUnit.test.skip('laser', assert => { + assert.true(robot.laser()); + }); +}); +``` diff --git a/api/QUnit/test.todo.md b/api/QUnit/test.todo.md new file mode 100644 index 0000000..2dcb9a6 --- /dev/null +++ b/api/QUnit/test.todo.md @@ -0,0 +1,58 @@ +--- +layout: page-api +title: QUnit.test.todo() +excerpt: Add a test which expects at least one failing assertion. +groups: + - main +redirect_from: + - "/QUnit.todo/" + - "/QUnit/todo/" + - "/QUnit/test.todo/" +version_added: "2.2.0" +--- + +`QUnit.test.todo( name, callback )`
+`QUnit.todo( name, callback )` + +Add a test which expects at least one failing assertion or exception during its run. + +| parameter | description | +|-----------|-------------| +| `name` (string) | Title of unit being tested | +| `callback` (function) | Function that performs the test | + +### Callback parameters + +| parameter | description | +|-----------|-------------| +| `assert` (object) | A new instance object with the [assertion methods](../assert/index.md) | + +Use this method to test a unit of code that is still under development (in a "todo" state). The "todo" test will pass as long as there is at least one assertion still failing, or if an exception is thrown. + +When all assertions are passing, the "todo" test will fail, thus signaling that `QUnit.test.todo()` should be changed to [`QUnit.test()`](./test.md). + +You can also use [`QUnit.module.todo()`](./module.md) to manage the "todo" state for all tests within a module at once. + +## Changelog + +| [QUnit 2.12](https://github.com/qunitjs/qunit/releases/tag/2.12.0) | The `QUnit.todo()` method was renamed to `QUnit.test.todo()`.
Use of `QUnit.todo()` remains supported as an alias. +| [QUnit 2.2](https://github.com/qunitjs/qunit/releases/tag/2.2.0) | The `QUnit.todo()` method was introduced. + +## Examples + +How to use `QUnit.test.todo` to denote code that is still under development. + +```js +QUnit.module('Robot', hooks => { + let robot; + hooks.beforeEach(() => { + robot = new Robot(); + }); + + // Robot is not yet finished + QUnit.test.todo('fireLazer', assert => { + const result = robot.fireLazer(); + assert.equal(result, "I'm firing my lazer!"); + }); +}); +``` diff --git a/api/assert/async.md b/api/assert/async.md new file mode 100644 index 0000000..cc1cb21 --- /dev/null +++ b/api/assert/async.md @@ -0,0 +1,100 @@ +--- +layout: page-api +title: assert.async() +excerpt: Instruct QUnit to wait for an asynchronous operation. +groups: + - assert + - async +redirect_from: + - "/assert/async/" + - "/QUnit.stop/" + - "/QUnit/stop/" + - "/stop/" +version_added: "1.16.0" +--- + +`async( count = 1 )` + +Instruct QUnit to wait for an asynchronous operation. + +| name | description | +|------|-------------| +| `count` (number) | Number of expected calls. Defaults to `1`. | + +`assert.async()` returns a callback function and pauses test processing until the callback function is called. The callback will throw an `Error` if it is invoked more often than the required call count. + +## See also + +* [Migration guide](../../upgrade-guide-2.x.md#introducing-assertasync) from QUnit 1.x `stop()` and `start()`. + +## Examples + +### Wait for callback + +Tell QUnit to wait for the `done()` call from a callback. + +```js +function fetchDouble (num, callback) { + const double = num * 2; + callback(double); +} + +QUnit.test('async example', assert => { + const done = assert.async(); + + fetchDouble(21, res => { + assert.strictEqual(res, 42, 'Result'); + done(); + }); +}); +``` +### Wait for multiple callbacks + +Call `assert.async()` multiple times to wait for multiple async operations. Each `done` callback must be called exactly once for the test to pass. + +```js +QUnit.test('two async calls', assert => { + const done1 = assert.async(); + const done2 = assert.async(); + + fetchDouble(3, res => { + assert.strictEqual(res, 6, 'double of 3'); + done1(); + }); + fetchDouble(9, res => { + assert.strictEqual(res, 18, 'double of 9'); + done2(); + }); +}); +``` + +### Require multiple calls + +The `count` parameter can be used to require multiple calls to the same callback. In the below example, the test passes after exactly three calls. + +```js +function uploadBatch (batch, notify, complete) { + batch.forEach((item) => { + // Do something with item + notify(); + }); + complete(null); +} + +QUnit.test('multiple calls example', assert => { + assert.timeout(1000); + + const notify = assert.async(3); + const done = assert.async(); + + uploadBatch( + ['a', 'b', 'c'], + notify, + (err) => { + assert.strictEqual(err, null, 'complete error parameter'); + + done(); + } + ); +}); +``` diff --git a/api/assert/deepEqual.md b/api/assert/deepEqual.md new file mode 100644 index 0000000..c593498 --- /dev/null +++ b/api/assert/deepEqual.md @@ -0,0 +1,58 @@ +--- +layout: page-api +title: assert.deepEqual() +excerpt: A recursive and strict comparison. +groups: + - assert +redirect_from: + - "/assert/deepEqual/" + - "/deepEqual/" +version_added: "1.0.0" +--- + +`deepEqual( actual, expected, message = "" )` + +A recursive and strict comparison, considering all own and inherited properties. + +| name | description | +|------|-------------| +| `actual` | Expression being tested | +| `expected` | Known comparison value | +| `message` (string) | Short description of the actual expression | + +This assertion compares the full objects as passed. For primitive values, a strict comparison is performed. For objects, the object identity is disregarded and instead a recursive comparison of all own and inherited properties is used. This means arrays, plain objects, and arbitrary class instance objects can all be compared in this way. + +The deep comparison includes built-in support for Date objects, regular expressions (RegExp), NaN, as well as ES6 features such as Symbol, Set, and Map objects. + +To assert strict equality on own properties only, refer to [`assert.propEqual()`](./propEqual.md) instead. + +[`assert.notDeepEqual()`](./notDeepEqual.md) can be used to check for inequality instead. + +## Examples + +Validate the properties and values of a given object. + +```js +QUnit.test('passing example', assert => { + const result = { foo: 'bar' }; + + assert.deepEqual(result, { foo: 'bar' }); +}); +``` + +```js +QUnit.test('failing example', assert => { + const result = { + a: 'Albert', + b: 'Berta', + num: 123 + }; + + // fails because the number 123 is not strictly equal to the string "123". + assert.deepEqual(result, { + a: 'Albert', + b: 'Berta', + num: '123' + }); +}); +``` diff --git a/api/assert/equal.md b/api/assert/equal.md new file mode 100644 index 0000000..5488fcc --- /dev/null +++ b/api/assert/equal.md @@ -0,0 +1,58 @@ +--- +layout: page-api +title: assert.equal() +excerpt: A non-strict comparison. +groups: + - assert +redirect_from: + - "/equal/" + - "/equals/" + - "/assert/equal/" + - "/assert/equals/" +version_added: "1.0.0" +--- + +`equal( actual, expected, message = "" )` + +A non-strict comparison of two values. + +| name | description | +|------|-------------| +| `actual` | Expression being tested | +| `expected` | Known comparison value | +| `message` (string) | Short description of the actual expression | + +The `equal` assertion uses the simple comparison operator (`==`) to compare the actual and expected arguments. When they are equal, the assertion passes; otherwise, it fails. When it fails, both actual and expected values are displayed in the test result, in addition to a given message. + +This method is similar to the `assertEquals()` method found in xUnit-style frameworks. + +To explicitly test inequality, use [`assert.notEqual()`](./notEqual.md). + +To test for strict equality, use [`assert.strictEqual()`](./strictEqual.md). + +## Changelog + +* Prior to QUnit 1.1, this method was known as `assert.equals`.
The alias was removed in QUnit 1.3. + +## Examples + +The simplest assertion example: + +```js +QUnit.test('a test', function (assert) { + assert.equal(1, '1', "String '1' and number 1 have the same value"); +}); +``` + +A slightly more thorough set of assertions: + +```js +QUnit.test('equal test', function (assert) { + assert.equal(0, 0, 'Zero, Zero; equal succeeds'); + assert.equal('', 0, 'Empty, Zero; equal succeeds'); + assert.equal('', '', 'Empty, Empty; equal succeeds'); + + assert.equal('three', 3, 'Three, 3; equal fails'); + assert.equal(null, false, 'null, false; equal fails'); +}); +``` diff --git a/api/assert/expect.md b/api/assert/expect.md new file mode 100644 index 0000000..707c3d1 --- /dev/null +++ b/api/assert/expect.md @@ -0,0 +1,83 @@ +--- +layout: page-api +title: assert.expect() +excerpt: Specify how many assertions are expected in a test. +groups: + - assert +redirect_from: + - "/assert/expect/" + - "/expect/" +version_added: "1.0.0" +--- + +`expect( amount )` + +Specify how many assertions are expected in a test. + +| name | description | +|------|-------------| +| `amount` | Number of expected assertions in this test | + +This is most commonly used as `assert.expect(0)`, which indicates that a test may pass without making any assertions. This means the test is only used to verify that the code runs to completion, without any uncaught errors. This is is essentially the inverse of [`assert.throws()`](./throws.md). + +This can also be used to explicitly require a certain number of assertions to be recorded in a given test. If afterwards the number of assertions does not match the expected count, the test will fail. + +It is recommended to test asynchronous code with [`assert.step()`](./step.md) or [`assert.async()`](./async.md) instead. + +## Examples + +### Example: No assertions + +A test without any assertions: + +```js +QUnit.test('example', function (assert) { + assert.expect(0); + + var android = new Robot(); + android.up(2); + android.down(2); + android.left(); + android.right(); + android.left(); + android.right(); + android.attack(); + android.jump(); +}); +``` + +### Example: Custom assert + +If you use a generic assertion library that throws when an expectation is not met, you can use `assert.expect(0)` if there are no other assertions needed in the test. + +```js +QUnit.test('example', function (assert) { + assert.expect(0); + + var android = new Robot(database); + android.run(); + + database.assertNoOpenConnections(); +}); +``` + +### Example: Explicit count + +Require an explicit assertion count. + +```js +QUnit.test('example', function (assert) { + assert.expect(2); + + function calc (x, operation) { + return operation(x); + } + + let result = calc(2, function (x) { + assert.true(true, 'calc() calls operation function'); + return x * x; + }); + + assert.strictEqual(result, 4, '2 squared equals 4'); +}); +``` diff --git a/api/assert/false.md b/api/assert/false.md new file mode 100644 index 0000000..ff690b9 --- /dev/null +++ b/api/assert/false.md @@ -0,0 +1,43 @@ +--- +layout: page-api +title: assert.false() +excerpt: A strict boolean false comparison. +groups: + - assert +redirect_from: + - "/assert/false/" +version_added: "2.11.0" +--- + +`false( actual, message = "" )` + +A strict comparison that passes if the first argument is boolean `false`. + +| name | description | +|------|-------------| +| `actual` | Expression being tested | +| `message` (string) | Short description | + +`false` requires just one argument. If the argument evaluates to false, the assertion passes; otherwise, it fails. + +This method is similar to the `assertFalse()` method found in xUnit-style frameworks. + +[`assert.true()`](./true.md) can be used to explicitly test for a true value. + +## Examples + +```js +QUnit.test('example', assert => { + // success + assert.false(false, 'boolean false'); + + // failure + assert.false('foo', 'non-empty string'); + assert.false('', 'empty string'); + assert.false(0, 'number zero'); + assert.false(true, 'boolean true'); + assert.false(NaN, 'NaN value'); + assert.false(null, 'null value'); + assert.false(undefined, 'undefined value'); +}); +``` diff --git a/api/assert/index.md b/api/assert/index.md new file mode 100644 index 0000000..78571df --- /dev/null +++ b/api/assert/index.md @@ -0,0 +1,15 @@ +--- +layout: group +group: assert +title: Assertions +amethyst: + robots: index +redirect_from: + - "/assert/" + - "/QUnit.assert/" + - "/category/assert/" +--- + +To define additional assertion methods, refer to the [`QUnit.assert`](../extension/QUnit.assert.md) extension page. + +Some of these methods are comparable to the assert methods one might find in xUnit-style frameworks such as SUnit, JUnit, RUnit, or PHPUnit. diff --git a/api/assert/notDeepEqual.md b/api/assert/notDeepEqual.md new file mode 100644 index 0000000..f6feeef --- /dev/null +++ b/api/assert/notDeepEqual.md @@ -0,0 +1,40 @@ +--- +layout: page-api +title: assert.notDeepEqual() +excerpt: An inverted deep equal comparison. +groups: + - assert +redirect_from: + - "/assert/notDeepEqual/" + - "/notDeepEqual/" +version_added: "1.0.0" +--- + +`notDeepEqual( actual, expected, message = "" )` + +An inverted deep equal comparison. + +| name | description | +|------|-------------| +| `actual` | Expression being tested | +| `expected` | Known comparison value | +| `message` (string) | Short description | + +This assertion fails if the actual and expected values are recursively equal by strict comparison, considering both own and inherited properties. + +The assertion passes if there are structural differences, type differences, or even a subtle difference in a particular property value. + +This is the inverse of [`assert.deepEqual()`](./deepEqual.md). + +## Examples + +Compare the value of two objects. + +```js +QUnit.test('example', assert => { + const result = { foo: 'yep' }; + + // succeeds, objects are similar but have a different foo value. + assert.notDeepEqual(result, { foo: 'nope' }); +}); +``` diff --git a/api/assert/notEqual.md b/api/assert/notEqual.md new file mode 100644 index 0000000..41155f1 --- /dev/null +++ b/api/assert/notEqual.md @@ -0,0 +1,48 @@ +--- +layout: page-api +title: assert.notEqual() +excerpt: A loose inequality comparison. +groups: + - assert +redirect_from: + - "/assert/notEqual/" + - "/notEqual/" +version_added: "1.0.0" +--- + +`notEqual( actual, expected, message = "" )` + +A loose inequality comparison, checking for non-strict differences between two values. + +| name | description | +|------|-------------| +| `actual` | Expression being tested | +| `expected` | Known comparison value | +| `message` (string) | Short description | + +The `notEqual` assertion uses the simple inverted comparison operator (`!=`) to compare the actual and expected values. When they aren't equal, the assertion passes; otherwise, it fails. When it fails, both actual and expected values are displayed in the test result, in addition to a given message. + +[`assert.equal()`](./equal.md) can be used to test equality. + +[`assert.notStrictEqual()`](./notStrictEqual.md) can be used to test strict inequality. + +## Examples + +The simplest assertion example: + +```js +QUnit.test('passing example', assert => { + const result = '2'; + + // succeeds, 1 and 2 are different. + assert.notEqual(result, 1); +}); + +QUnit.test('failing example', assert => { + const result = '2'; + + // fails, the number 2 and the string "2" are considered equal when + // compared loosely. Use `assert.notStrictEqual` to consider them different. + assert.notEqual(result, 2); +}); +``` diff --git a/api/assert/notOk.md b/api/assert/notOk.md new file mode 100644 index 0000000..a8bcf9d --- /dev/null +++ b/api/assert/notOk.md @@ -0,0 +1,43 @@ +--- +layout: page-api +title: assert.notOk() +excerpt: Check if the first argument is falsy. +groups: + - assert +redirect_from: + - "/assert/notOk/" + - "/notOk/" +version_added: "1.18.0" +--- + +`notOk( state, message = "" )` + +A boolean check that passes when the first argument is falsy. + +| name | description | +|------|-------------| +| `state` | Expression being tested | +| `message` (string) | Short description | + +This assertion requires only one argument. If the argument evaluates to false, the assertion passes; otherwise, it fails. + +To strictly compare against boolean false, use [`assert.false()`](./false.md). + +## Examples + +```js +QUnit.test('example', assert => { + // success + assert.notOk(false, 'boolean false'); + assert.notOk('', 'empty string'); + assert.notOk(0, 'number zero'); + assert.notOk(NaN, 'NaN value'); + assert.notOk(null, 'null value'); + assert.notOk(undefined, 'undefined value'); + + // failure + assert.notOk('foo', 'non-empty string'); + assert.notOk(true, 'boolean true'); + assert.notOk(1, 'number one'); +}); +``` diff --git a/api/assert/notPropContains.md b/api/assert/notPropContains.md new file mode 100644 index 0000000..1384d7f --- /dev/null +++ b/api/assert/notPropContains.md @@ -0,0 +1,78 @@ +--- +layout: page-api +title: assert.notPropContains() +excerpt: Check that an object does not contain certain properties. +groups: + - assert +redirect_from: + - "/assert/notPropContains/" +version_added: "2.18.0" +--- + +`notPropContains( actual, expected, message = "" )` + +Check that an object does not contain certain properties. + +| name | description | +|------|-------------| +| `actual` | Expression being tested | +| `expected` | Known comparison value | +| `message` (string) | Short description | + +The `notPropContains` assertion compares the subset of properties in the expected object, and tests that these keys are either absent or hold a value that is different according to a strict equality comparison. + +This method is recursive and allows partial comparison of nested objects as well. + +## See also + +* Use [`assert.propContains()`](./propContains.md) to test for the presence and equality of properties instead. + +## Examples + +```js +QUnit.test('example', assert => { + const result = { + foo: 0, + vehicle: { + timeCircuits: 'on', + fluxCapacitor: 'fluxing', + engine: 'running' + }, + quux: 1 + }; + + // succeeds, property "timeCircuits" is actually "on" + assert.notPropContains(result, { + vehicle: { + timeCircuits: 'off' + } + }); + + // succeeds, property "wings" is not in the object + assert.notPropContains(result, { + vehicle: { + wings: 'flapping' + } + }); + + function Point (x, y) { + this.x = x; + this.y = y; + } + + assert.notPropContains( + new Point(10, 20), + { z: 30 } + ); + + const nested = { + north: [ /* ... */ ], + east: new Point(10, 20), + south: [ /* ... */ ], + west: [ /* ... */ ] + }; + + assert.notPropContains(nested, { east: new Point(88, 42) }); + assert.notPropContains(nested, { east: { x: 88 } }); +}); +``` diff --git a/api/assert/notPropEqual.md b/api/assert/notPropEqual.md new file mode 100644 index 0000000..5e70768 --- /dev/null +++ b/api/assert/notPropEqual.md @@ -0,0 +1,57 @@ +--- +layout: page-api +title: assert.notPropEqual() +excerpt: Compare an object's own properties for inequality. +groups: + - assert +redirect_from: + - "/assert/notPropEqual/" + - "/notPropEqual/" +version_added: "1.11.0" +--- + +`notPropEqual( actual, expected, message = "" )` + +Compare an object's own properties using a strict inequality comparison. + +| name | description | +|------|-------------| +| `actual` | Expression being tested | +| `expected` | Known comparison value | +| `message` (string) | Short description | + +The `notPropEqual` assertion compares only an object's own properties, using the strict inequality operator (`!==`). + +The test passes if there are properties with different values, or extra properties, or missing properties. + +## See also + +* Use [`assert.notPropContains()`](./notPropContains.md) to only check for the absence or inequality of some properties. +* Use [`assert.propEqual()`](./propEqual.md) to test for equality of properties instead. + +## Examples + +Compare the values of two objects properties. + +```js +QUnit.test('example', assert => { + class Foo { + constructor () { + this.x = '1'; + this.y = 2; + } + + walk () {} + run () {} + } + + const foo = new Foo(); + + // succeeds, only own property values are compared (using strict equality), + // and property "x" is indeed not equal (string instead of number). + assert.notPropEqual(foo, { + x: 1, + y: 2 + }); +}); +``` diff --git a/api/assert/notStrictEqual.md b/api/assert/notStrictEqual.md new file mode 100644 index 0000000..201ad58 --- /dev/null +++ b/api/assert/notStrictEqual.md @@ -0,0 +1,38 @@ +--- +layout: page-api +title: assert.notStrictEqual() +excerpt: A strict comparison, checking for inequality. +groups: + - assert +redirect_from: + - "/assert/notStrictEqual/" + - "/notStrictEqual/" +version_added: "1.0.0" +--- + +`notStrictEqual( actual, expected, message = "" )` + +A strict comparison, checking for inequality. + +| name | description | +|------|-------------| +| `actual` | Expression being tested | +| `expected` | Known comparison value | +| `message` (string) | Short description | + +The `notStrictEqual` assertion uses the strict inverted comparison operator (`!==`) to compare the actual and expected arguments. When they aren't equal, the assertion passes; otherwise, it fails. When it fails, both actual and expected values are displayed in the test result, in addition to a given message. + +[`assert.equal()`](./equal.md) can be used to test equality. + +[`assert.strictEqual()`](./strictEqual.md) can be used to test strict equality. + +## Examples + +```js +QUnit.test('example', assert => { + const result = '2'; + + // succeeds, while the number 2 and string 2 are similar, they are strictly different. + assert.notStrictEqual(result, 2); +}); +``` diff --git a/api/assert/ok.md b/api/assert/ok.md new file mode 100644 index 0000000..f12592e --- /dev/null +++ b/api/assert/ok.md @@ -0,0 +1,45 @@ +--- +layout: page-api +title: assert.ok() +excerpt: Check if the first argument is truthy. +groups: + - assert +redirect_from: + - "/assert/ok/" + - "/ok/" +version_added: "1.0.0" +--- + +`ok( state, message = "" )` + +A boolean check that passes when the first argument is truthy. + +| name | description | +|------|-------------| +| `state` | Expression being tested | +| `message` (string) | Short description | + +This assertion requires only one argument. If the argument evaluates to true, the assertion passes; otherwise, it fails. + +To strictly compare against boolean true, use [`assert.true()`](./true.md). + +For the inverse of `ok()`, refer to [`assert.notOk()`](./notOk.md) + +## Examples + +```js +QUnit.test('example', assert => { + // success + assert.ok(true, 'boolean true'); + assert.ok('foo', 'non-empty string'); + assert.ok(1, 'number one'); + + // failure + assert.ok(false, 'boolean false'); + assert.ok('', 'empty string'); + assert.ok(0, 'number zero'); + assert.ok(NaN, 'NaN value'); + assert.ok(null, 'null value'); + assert.ok(undefined, 'undefined value'); +}); +``` diff --git a/api/assert/propContains.md b/api/assert/propContains.md new file mode 100644 index 0000000..05bebe3 --- /dev/null +++ b/api/assert/propContains.md @@ -0,0 +1,77 @@ +--- +layout: page-api +title: assert.propContains() +excerpt: Check that an object contains certain properties. +groups: + - assert +redirect_from: + - "/assert/propContains/" +version_added: "2.18.0" +--- + +`propContains( actual, expected, message = "" )` + +Check that an object contains certain properties. + +| name | description | +|------|-------------| +| `actual` | Expression being tested | +| `expected` | Known comparison value | +| `message` (string) | Short description of the actual expression | + +The `propContains` assertion compares only the **subset** of properties in the expected object, +and tests that these keys exist as own properties with strictly equal values. + +This method is recursive and allows partial comparison of nested objects as well. + +## See also + +* Use [`assert.propEqual()`](./propEqual.md) to compare all properties, considering extra properties as unexpected. +* Use [`assert.notPropContains()`](./notPropContains.md) to test for the absence or inequality of properties. + +## Examples + +```js +QUnit.test('example', assert => { + const result = { + foo: 0, + vehicle: { + timeCircuits: 'on', + fluxCapacitor: 'fluxing', + engine: 'running' + }, + quux: 1 + }; + + assert.propContains(result, { + foo: 0, + vehicle: { fluxCapacitor: 'fluxing' } + }); + + function Point (x, y) { + this.x = x; + this.y = y; + } + + assert.propContains( + new Point(10, 20), + { y: 20 } + ); + + assert.propContains( + [ 'a', 'b' ], + { 1: 'b' } + ); + + const nested = { + north: [ /* ... */ ], + east: new Point(10, 20), + south: [ /* ... */ ], + west: [ /* ... */ ] + }; + + assert.propContains(nested, { east: new Point(10, 20) }); + assert.propContains(nested, { east: { x: 10, y: 20 } }); + assert.propContains(nested, { east: { x: 10 } }); +}); +``` diff --git a/api/assert/propEqual.md b/api/assert/propEqual.md new file mode 100644 index 0000000..c902075 --- /dev/null +++ b/api/assert/propEqual.md @@ -0,0 +1,81 @@ +--- +layout: page-api +title: assert.propEqual() +excerpt: Compare an object's own properties. +groups: + - assert +redirect_from: + - "/assert/propEqual/" + - "/propEqual/" +version_added: "1.11.0" +--- + +`propEqual( actual, expected, message = "" )` + +Compare an object's own properties using a strict comparison. + +| name | description | +|------|-------------| +| `actual` | Expression being tested | +| `expected` | Known comparison value | +| `message` (string) | Short description of the actual expression | + +The `propEqual` assertion only compares an object's own properties. This means the expected value does not need to be an instance of the same class or otherwise inherit the same prototype. This is in contrast with [`assert.deepEqual()`](./deepEqual.md). + +This assertion fails if the values differ, or if there are extra properties, or if some properties are missing. + +This method is recursive and can compare any nested or complex object via a plain object. + +## See also + +* Use [`assert.propContains()`](./propContains.md) to only check a subset of properties. +* Use [`assert.notPropEqual()`](./notPropEqual.md) to test for the inequality of object properties instead. + +## Examples + +Compare the property values of two objects. + +```js +QUnit.test('example', assert => { + class Foo { + constructor () { + this.x = 1; + this.y = 2; + } + + walk () {} + run () {} + } + + const foo = new Foo(); + + // succeeds, own properties are strictly equal, + // and inherited properties (such as which constructor) are ignored. + assert.propEqual(foo, { + x: 1, + y: 2 + }); +}); +``` + +Using classic ES5 syntax: + +```js +QUnit.test('example', function (assert) { + function Foo () { + this.x = 1; + this.y = 2; + } + Foo.prototype.walk = function () {}; + Foo.prototype.run = function () {}; + + var foo = new Foo(); + + // succeeds, own properties are strictly equal. + var expected = { + x: 1, + y: 2 + }; + assert.propEqual(foo, expected); +}); +``` diff --git a/api/assert/pushResult.md b/api/assert/pushResult.md new file mode 100644 index 0000000..886095c --- /dev/null +++ b/api/assert/pushResult.md @@ -0,0 +1,87 @@ +--- +layout: page-api +title: assert.pushResult() +excerpt: Report the result of a custom assertion. +groups: + - assert +redirect_from: + - "/assert/pushResult/" +version_added: "1.22.0" +--- + +`pushResult( data )` + +Report the result of a custom assertion. + +| name | description | +|------|-------------| +| `data.result` (boolean) | Result of the assertion | +| `data.actual` | Expression being tested | +| `data.expected` | Known comparison value | +| `data.message` (string or undefined) | Short description of the assertion | + +## Examples + +If you need to express an expectation that is not abstracted by a built-in QUnit assertion, you can perform your own logic ad-hoc in an expression, and then pass two directly comparable values to [`assert.strictEqual()`](./strictEqual.md), or pass your own representative boolean result to [`assert.true()`](./true.md). + +```js +QUnit.test('bad example of remainder', assert => { + const result = 4; + const actual = (result % 3) === 2; + assert.true(actual, 'remainder'); + // In case of failure: + // > Actual: false + // > Expected: true + // + // No mention of the actual remainder. + // No mention of the expected value. +}); + +QUnit.test('good example of remainder', assert => { + const result = 4; + assert.strictEqual(result % 3, 2, 'remainder'); + // In case of failure: + // > Actual: 1 + // > Expected: 2 +}); + +QUnit.test('bad example of between', assert => { + const actual = 3; + const isBetween = (actual >= 1 && actual <= 10); + assert.true(isBetween, 'result between 1 and 10'); + // In case of failure: + // > Actual: false + // > Expected: true + // + // No mention of the actual remainder. + // No mention of the expected value. + // Cannot be expressed in a useful way with strictEqual() +}); +``` + +### Custom assertion + +With a custom assertion method, you can control how an assertion should be evaluated, separately from how its actual and expected values are described in case of a failure. + +For example: + +```js +QUnit.assert.between = function (actual, from, to, message) { + const isBetween = (actual >= from && actual <= to); + + this.pushResult({ + result: isBetween, + actual: actual, + expected: `between ${from} and ${to} inclusive`, + message: message + }); +}; + +QUnit.test('custom assertion example', assert => { + const result = 3; + assert.between(result, 1, 10, 'result'); + // Example of failure if result is out of range + // > actual: 42 + // > expected: between 1 and 10 +}); +``` diff --git a/api/assert/rejects.md b/api/assert/rejects.md new file mode 100644 index 0000000..6821fe4 --- /dev/null +++ b/api/assert/rejects.md @@ -0,0 +1,120 @@ +--- +layout: page-api +title: assert.rejects() +excerpt: Test if the provided promise rejects. +groups: + - assert +redirect_from: + - "/assert/rejects/" +version_added: "2.5.0" +--- + +`rejects( promise, message = "" )`
+`rejects( promise, expectedMatcher, message = "" )` + +Test if the provided promise rejects, and optionally compare the rejection value. + +| name | description | +|------|-------------| +| `promise` (thenable) | Promise to test for rejection | +| `expectedMatcher` | Rejection value matcher | +| `message` (string) | Short description of the assertion | + +When testing code that is expected to return a rejected promise based on a +specific set of circumstances, use `assert.rejects()` for testing and +comparison. + +The `expectedMatcher` argument can be: + +* A function that returns `true` when the assertion should be considered passing. +* An Error object. +* A base constructor to use ala `rejectionValue instanceof expectedMatcher`. +* A RegExp that matches (or partially matches) `rejectionValue.toString()`. + +Note: in order to avoid confusion between the `message` and the `expectedMatcher`, the `expectedMatcher` **can not** be a string. + +## Examples + +```js +QUnit.test('rejects example', assert => { + // simple check + assert.rejects(Promise.reject('some error')); + + // simple check + assert.rejects( + Promise.reject('some error'), + 'optional description here' + ); + + // match pattern on actual error + assert.rejects( + Promise.reject(new Error('some error')), + /some error/, + 'optional description here' + ); + + // Using a custom error constructor + function CustomError (message) { + this.message = message; + } + CustomError.prototype.toString = function () { + return this.message; + }; + + // actual error is an instance of the expected constructor + assert.rejects( + Promise.reject(new CustomError('some error')), + CustomError + ); + + // actual error has strictly equal `constructor`, `name` and `message` properties + // of the expected error object + assert.rejects( + Promise.reject(new CustomError('some error')), + new CustomError('some error') + ); + + // custom validation arrow function + assert.rejects( + Promise.reject(new CustomError('some error')), + (err) => err.toString() === 'some error' + ); + + // custom validation function + assert.rejects( + Promise.reject(new CustomError('some error')), + function (err) { + return err.toString() === 'some error'; + } + ); +}); +``` + +The `assert.rejects()` method returns a `Promise` which handles the (often asynchronous) resolution and rejection logic for test successes and failures. It is not required to `await` the returned value, since QUnit internally handles the async control for you and waits for a settled state. However, if your test code requires a consistent and more isolated state between `rejects` calls, then this should be explicitly awaited to hold back the next statements. + +```js +QUnit.test('stateful rejects example', async assert => { + let value; + + // asynchronously resolve if value < 5, and reject otherwise + function asyncChecker () { + return new Promise((resolve, reject) => { + setTimeout(() => { + if (value < 5) { + resolve(); + } else { + reject('bad value: ' + value); + } + }, 10); + }); + } + + value = 8; + await assert.rejects(asyncChecker(), /bad value: 8/); + + // if the above was not awaited, then the next line would change the value + // before the previous assertion could occur, and would cause a test failure + value = Infinity; + await assert.rejects(asyncChecker(), /bad value: Infinity/); +}); +``` diff --git a/api/assert/step.md b/api/assert/step.md new file mode 100644 index 0000000..24e318e --- /dev/null +++ b/api/assert/step.md @@ -0,0 +1,45 @@ +--- +layout: page-api +title: assert.step() +excerpt: Record a step for later verification. +groups: + - assert +redirect_from: + - "/assert/step/" +version_added: "2.2.0" +--- + +`step( value )` + +Record a step for later verification. + +| name | description | +|------|-------------| +| `value` (string) | Relevant string value, or short description, to mark this step. | + +This assertion registers a passing assertion with the provided string. This and any other steps should be verified later in the test via [`assert.verifySteps()`](./verifySteps.md). + +The Step API provides an easy way to verify execution logic to a high degree of accuracy and precision, whether for asynchronous code, event-driven code, or callback-driven code. + +## Examples + +```js +QUnit.test('example', function (assert) { + var maker = new WordMaker(); + maker.on('start', () => { + assert.step('start'); + }); + maker.on('data', (word) => { + assert.step(word); + }); + maker.on('end', () => { + assert.step('end'); + }); + + maker.process('3.1'); + + assert.verifySteps([ 'start', '3', 'point', '1', 'end' ]); +}); +``` + +_Note: See [`assert.verifySteps()`](./verifySteps.md) for more detailed examples._ diff --git a/api/assert/strictEqual.md b/api/assert/strictEqual.md new file mode 100644 index 0000000..bb68904 --- /dev/null +++ b/api/assert/strictEqual.md @@ -0,0 +1,45 @@ +--- +layout: page-api +title: assert.strictEqual() +excerpt: A strict type and value comparison. +groups: + - assert +redirect_from: + - "/same/" + - "/strictEqual/" + - "/assert/same/" + - "/assert/strictEqual/" +version_added: "1.0.0" +--- + +`strictEqual( actual, expected, message = "" )` + +A strict type and value comparison. + +| name | description | +|------|-------------| +| `actual` | Expression being tested | +| `expected` | Known comparison value | +| `message` (string) | Short description of the actual expression | + +The `strictEqual()` assertion provides the most rigid comparison of type and value with the strict equality operator (`===`). + +[`assert.equal()`](./equal.md) can be used to test non-strict equality. + +[`assert.notStrictEqual()`](./notStrictEqual.md) can be used to explicitly test strict inequality. + +## Changelog + +* Prior to QUnit 1.1, this method was known as `assert.same()`.
The alias was removed in QUnit 1.3. + +## Examples + +Compare the value of two primitives, having the same value and type. + +```js +QUnit.test('strictEqual example', assert => { + const result = 2; + + assert.strictEqual(result, 2); +}); +``` diff --git a/api/assert/throws.md b/api/assert/throws.md new file mode 100644 index 0000000..f854eb1 --- /dev/null +++ b/api/assert/throws.md @@ -0,0 +1,110 @@ +--- +layout: page-api +title: assert.throws() +excerpt: Test if a callback throws an exception. +groups: + - assert +redirect_from: + - "/assert/raises/" + - "/assert/throws/" + - "/throws/" +version_added: "1.0.0" +--- + +`throws( blockFn, message = "" )`
+`throws( blockFn, expectedMatcher, message = "" )` + +Test if a callback throws an exception, and optionally compare the thrown error. + +| name | description | +|------|-------------| +| `blockFn` (function) | Function to execute | +| `expectedMatcher` | Expected error matcher | +| `message` (string) | Short description of the assertion | + +When testing code that is expected to throw an exception based on a specific set of circumstances, use `assert.throws()` to catch the error object for testing and comparison. + +The `expectedMatcher` argument can be: + +* An Error object. +* An Error constructor to use ala `errorValue instanceof expectedMatcher`. +* A RegExp that matches (or partially matches) the string representation. +* A callback Function that must return `true` to pass the assertion check. + +

In very few environments, like Closure Compiler, `throws` may cause an error. There you can use `assert.raises()`. It has the same signature and behaviour, just a different name.

+ +## Changelog + +| [QUnit 2.12](https://github.com/qunitjs/qunit/releases/tag/2.12.0) | Added support for arrow functions as `expectedMatcher` callback function. +| [QUnit 1.9](https://github.com/qunitjs/qunit/releases/tag/v1.9.0) | `assert.raises()` was renamed to `assert.throws()`.
The `assert.raises()` method remains supported as an alias. + +## Examples + +```js +QUnit.test('throws example', assert => { + // simple check + assert.throws(function () { + throw new Error('boo'); + }); + + // simple check + assert.throws( + function () { + throw new Error('boo'); + }, + 'optional description here' + ); + + // match pattern on actual error + assert.throws( + function () { + throw new Error('some error'); + }, + /some error/, + 'optional description here' + ); + + // using a custom error constructor + function CustomError (message) { + this.message = message; + } + CustomError.prototype.toString = function () { + return this.message; + }; + + // actual error is an instance of the expected constructor + assert.throws( + function () { + throw new CustomError('some error'); + }, + CustomError + ); + + // actual error has strictly equal `constructor`, `name` and `message` properties + // of the expected error object + assert.throws( + function () { + throw new CustomError('some error'); + }, + new CustomError('some error') + ); + + // custom validation arrow function + assert.throws( + function () { + throw new CustomError('some error'); + }, + (err) => err.toString() === 'some error' + ); + + // custom validation function + assert.throws( + function () { + throw new CustomError('some error'); + }, + function (err) { + return err.toString() === 'some error'; + } + ); +}); +``` diff --git a/api/assert/timeout.md b/api/assert/timeout.md new file mode 100644 index 0000000..8275310 --- /dev/null +++ b/api/assert/timeout.md @@ -0,0 +1,64 @@ +--- +layout: page-api +title: assert.timeout() +excerpt: How long to wait for async operations. +groups: + - assert + - async +redirect_from: + - "/assert/timeout/" +version_added: "2.4.0" +--- + +`timeout( duration )` + +Set how long to wait for async operations to finish. + +| name | description | +|------|-------------| +| `duration` (number) | The length of time to wait, in milliseconds. | + +This assertion defines how long to wait (at most) in the current test. It overrides [`QUnit.config.testTimeout`](../config/testTimeout.md) on a per-test basis. + +The timeout length only applies when a test actually involves asynchronous functions or promises. If `0` is passed, then awaiting or returning any Promise may fail the test. + +If `assert.timeout()` is called after a different timeout is already set, the old timeout will be cleared and the new duration will be used to start a new timer. + +## Examples + +```js +QUnit.test('wait for an event', assert => { + assert.timeout(1000); // Timeout after 1 second + const done = assert.async(); + + const adder = new NumberAdder(); + adder.on('ready', res => { + assert.strictEqual(res, 12); + done(); + }); + adder.run([ 1, 1, 2, 3, 5 ]); +}); +``` + +```js +QUnit.test('wait for an async function', async assert => { + assert.timeout(500); // Timeout after 0.5 seconds + + const result = await asyncAdder(5, 7); + assert.strictEqual(result, 12); +}); +``` + +Using classic ES5 syntax: + +```js +QUnit.test('wait for a returned promise', function (assert) { + assert.timeout(500); // Timeout after 0.5 seconds + + var promise = asyncAdder(5, 7); + + return promise.then(function (result) { + assert.strictEqual(result, 12); + }); +}); +``` diff --git a/api/assert/true.md b/api/assert/true.md new file mode 100644 index 0000000..5ee7ce9 --- /dev/null +++ b/api/assert/true.md @@ -0,0 +1,44 @@ +--- +layout: page-api +title: assert.true() +excerpt: A strict boolean true comparison. +groups: + - assert +redirect_from: + - "/assert/true/" +version_added: "2.11.0" +--- + +`true( actual, message = "" )` + +A strict comparison that passes if the first argument is boolean `true`. + +| name | description | +|------|-------------| +| `actual` | Expression being tested | +| `message` (string) | Short description of the actual expression | + +`true()` requires just one argument. If the argument evaluates to true, the assertion passes; otherwise, it fails. + +This method is similar to the `assertTrue()` method found in xUnit-style frameworks. + +[`assert.false()`](./false.md) can be used to explicitly test for a false value. + +## Examples + +```js +QUnit.test('example', assert => { + // success + assert.true(true, 'boolean true'); + + // failure + assert.true('foo', 'non-empty string'); + assert.true('', 'empty string'); + assert.true(0, 'number zero'); + assert.true(false, 'boolean false'); + assert.true(NaN, 'NaN value'); + assert.true(null, 'null value'); + assert.true(undefined, 'undefined value'); +}); +``` + diff --git a/api/assert/verifySteps.md b/api/assert/verifySteps.md new file mode 100644 index 0000000..e1a8dc2 --- /dev/null +++ b/api/assert/verifySteps.md @@ -0,0 +1,148 @@ +--- +layout: page-api +title: assert.verifySteps() +excerpt: Verify the exact order of steps. +groups: + - assert +redirect_from: + - "/assert/verifySteps/" +version_added: "2.2.0" +--- + +`verifySteps( steps, message = "" )` + +Verify the presence and exact order of previously marked steps in a test. + +| name | description | +|------|-------------| +| `steps` (array) | List of strings | +| `message` (string) | Short description | + +The Step API provides an easy way to verify execution logic to a high degree of accuracy and precision, whether for asynchronous code, event-driven code, or callback-driven code. + +For example, you can mark steps to observe and validate whether parts of your code are reached correctly, or to check the frequency (how often) an asynchronous code path is executed. You can also capture any unexpected steps, which are automatically detected and shown as part of the test failure. + +This assertion compares a given array of string values to a list of previously recorded steps, as marked via previous calls to [`assert.step()`](./step.md). + +Calling `verifySteps()` will clear and reset the internal list of steps. This allows multiple independent sequences of `assert.step()` to exist within the same test. + +Refer to the below examples and learn how to use the Step API in your test suite. + +## Examples + +### Test event-based interface + +This example uses a class based on an [`EventEmitter`](https://nodejs.org/api/events.html), such as the one provided by Node.js and other environments: + +```js +QUnit.test('good example', async assert => { + const maker = new WordMaker(); + maker.on('start', () => { + assert.step('start'); + }); + maker.on('data', (word) => { + assert.step(word); + }); + maker.on('end', () => { + assert.step('end'); + }); + maker.on('error', message => { + assert.step('error: ' + message); + }); + + await maker.process('3.1'); + + assert.verifySteps(['start', '3', 'point', '1', 'end']); +}); +``` + +When approaching this scenario **without the Step API** one might be tempted to place comparison checks directly inside event callbacks. It is considered an anti-pattern to make dummy assertions in callbacks that the test does not have control over. This creates loose assurances, and can easily cause false positives (a callback might not run, run out of order, or run multiple times). It also offers rather limited debugging information. + +```js +// WARNING: This is a BAD example +QUnit.test('bad example 1', async assert => { + const maker = new WordMaker(); + maker.on('start', () => { + assert.true(true, 'start'); + }); + maker.on('middle', () => { + assert.true(true, 'middle'); + }); + maker.on('end', () => { + assert.true(true, 'end'); + }); + maker.on('error', () => { + assert.true(false, 'error'); + }); + + await maker.process(); +}); +``` + +A less fragile approach could involve a local array that we check afterwards with [`deepEqual`](./deepEqual.md). This catches out-of-order issues, unexpected values, and duplicate values. It also provides detailed debugging information in case of problems. The below is in essence how the Step API works: + +```js +QUnit.test('manual example without Step API', async assert => { + const values = []; + + const maker = new WordMaker(); + maker.on('start', () => { + values.push('start'); + }); + maker.on('middle', () => { + values.push('middle'); + }); + maker.on('end', () => { + values.push('end'); + }); + maker.on('error', () => { + values.push('error'); + }); + + await maker.process(); + + assert.deepEqual(values, ['start', 'middle', 'end']); +}); +``` + +### Test publish/subscribe system + +Use the **Step API** to verify messages received in a Pub-Sub channel or topic. + +```js +QUnit.test('good example', assert => { + const publisher = new Publisher(); + + const subscriber1 = (message) => assert.step(`Sub 1: ${message}`); + const subscriber2 = (message) => assert.step(`Sub 2: ${message}`); + + publisher.subscribe(subscriber1); + publisher.subscribe(subscriber2); + publisher.publish('Hello!'); + + publisher.unsubscribe(subscriber1); + publisher.publish('World!'); + + assert.verifySteps([ + 'Sub 1: Hello!', + 'Sub 2: Hello!', + 'Sub 2: World!' + ]); +}); +``` + +### Multiple steps verifications in one test + +The internal buffer of observed steps is automatically reset when calling `verifySteps()`. + +```js +QUnit.test('multiple verifications example', assert => { + assert.step('one'); + assert.step('two'); + assert.verifySteps(['one', 'two']); + + assert.step('three'); + assert.step('four'); + assert.verifySteps(['three', 'four']); +}); + ``` diff --git a/api/async.md b/api/async.md new file mode 100644 index 0000000..90dcb4b --- /dev/null +++ b/api/async.md @@ -0,0 +1,7 @@ +--- +layout: group +group: async +title: Async control +redirect_from: + - "/async/" +--- diff --git a/api/callbacks/QUnit.begin.md b/api/callbacks/QUnit.begin.md new file mode 100644 index 0000000..ba8bb75 --- /dev/null +++ b/api/callbacks/QUnit.begin.md @@ -0,0 +1,77 @@ +--- +layout: page-api +title: QUnit.begin() +excerpt: Register a callback to fire when the test run begins. +groups: + - callbacks +redirect_from: + - "/callbacks/QUnit.begin/" + - "/QUnit.begin/" +version_added: "1.0.0" +--- + +`QUnit.begin( callback )` + +Register a callback to fire when the test run begins. The callback may be an async function, or a function that returns a Promise, which will be waited for before the next callback is handled. + +The callback will be called once, before QUnit runs any tests. + +| parameter | description | +|-----------|-------------| +| `callback` (function) | Callback to execute, called with a `details` object. | + +### Details object + +| property | description | +|-----------|-------------| +| `totalTests` (number) | Number of registered tests | +| `modules` (array) | List of registered modules,
as `{ name: string, moduleId: string }` objects. | + +## Changelog + +| [QUnit 2.19.0](https://github.com/qunitjs/qunit/releases/tag/2.19.0) | Added `moduleId` to the `details.modules` objects. +| [QUnit 1.16](https://github.com/qunitjs/qunit/releases/tag/1.16.0) | Added `details.modules` property, containing `{ name: string }` objects. +| [QUnit 1.15](https://github.com/qunitjs/qunit/releases/tag/1.15.0) | Added `details.totalTests` property. + +## Examples + +Get total number of tests known at the start. + +```js +QUnit.begin(details => { + console.log(`Test amount: ${details.totalTests}`); +}); +``` + +Use async-await to wait for some asynchronous work: + +```js +QUnit.begin(async details => { + await someAsyncWork(); + + console.log(`Test amount: ${details.totalTests}`); +}); +``` + +Using classic ES5 syntax: + +```js +QUnit.begin(function (details) { + console.log('Test amount:' + details.totalTests); +}); +``` + +```js +function someAsyncWork () { + return new Promise(function (resolve, reject) { + // do some async work + resolve(); + }); +} + +QUnit.begin(function (details) { + return someAsyncWork().then(function () { + console.log('Test amount:' + details.totalTests); + }); +}); +``` diff --git a/api/callbacks/QUnit.done.md b/api/callbacks/QUnit.done.md new file mode 100644 index 0000000..cc71772 --- /dev/null +++ b/api/callbacks/QUnit.done.md @@ -0,0 +1,59 @@ +--- +layout: page-api +title: QUnit.done() +excerpt: Register a callback to fire when the test run has ended. +groups: + - callbacks +redirect_from: + - "/callbacks/QUnit.done/" + - "/QUnit.done/" +version_added: "1.0.0" +--- + +`QUnit.done( callback )` + +Register a callback to fire when the test run has ended. The callback may be an async function, or a function that return a Promise which will be waited for before the next callback is handled. + +| parameter | description | +|-----------|-------------| +| `callback` (function) | Callback to execute, called with a `details` object: + +### Details object + +| property | description | +|-----------|-------------| +| `failed` (number) | Number of failed assertions | +| `passed` (number) | Number of passed assertions | +| `total` (number) | Total number of assertions | +| `runtime` (number) | Duration of the test run in milliseconds | + +
+ +Use of `details` is __deprecated__ and it's recommended to use [`QUnit.on('runEnd')`](./QUnit.on.md#the-runend-event) instead. + +Caveats: + +* This callback reports the **internal assertion count**. + +* The default browser and CLI interfaces for QUnit and other popular test frameworks, and most CI integrations, report the number of tests. Reporting the number _assertions_ may be confusing to developers. + +* Failed assertions of a [`test.todo()`](../QUnit/test.todo.md) test are reported exactly as such. While rare, this means that a test run and all tests within it may be reported as passing, while internally there were some failed assertions. Unfortunately, this internal detail is exposed for compatibility reasons. + +
+ +## Changelog + +| [QUnit 2.2](https://github.com/qunitjs/qunit/releases/tag/2.2.0) | Deprecate `details` parameter in favour of `QUnit.on('runEnd')`. + +## Examples + +Register a callback that logs internal assertion counts. + +```js +QUnit.done(function (details) { + console.log( + 'Total: ' + details.total + ' Failed: ' + details.failed + + ' Passed: ' + details.passed + ' Runtime: ' + details.runtime + ); +}); +``` diff --git a/api/callbacks/QUnit.log.md b/api/callbacks/QUnit.log.md new file mode 100644 index 0000000..c9ae32b --- /dev/null +++ b/api/callbacks/QUnit.log.md @@ -0,0 +1,73 @@ +--- +layout: page-api +title: QUnit.log() +excerpt: Register a callback to fire whenever an assertion completes. +groups: + - callbacks +redirect_from: + - "/callbacks/QUnit.log/" + - "/QUnit.log/" +version_added: "1.0.0" +--- + +`QUnit.log( callback )` + +Register a callback to fire whenever an assertion completes. + +**NOTE: The QUnit.log() callback does not handle promises and MUST be synchronous.** + +| parameter | description | +|-----------|-------------| +| callback (function) | Callback to execute. Provides a single argument with the callback Details object | + +### Details object + +Passed to the callback: + +| property | description | +|-----------|-------------| +| `result` (boolean) | The boolean result of an assertion, `true` means passed, `false` means failed. | +| `actual` | One side of a comparison assertion. Can be _undefined_ when `ok()` is used. | +| `expected` | One side of a comparison assertion. Can be _undefined_ when `ok()` is used. | +| `message` (string) | A string description provided by the assertion. | +| `source` (string) | The associated stacktrace, either from an exception or pointing to the source of the assertion. Depends on browser support for providing stacktraces, so can be undefined. | +| `module` (string) | The test module name of the assertion. If the assertion is not connected to any module, the property's value will be _undefined_. | +| `name` (string) | The test block name of the assertion. | +| `runtime` (number) | The time elapsed in milliseconds since the start of the containing [`QUnit.test()`](../QUnit/test.md), including setup. | +| `todo` (boolean) | Indicates whether or not this assertion was part of a todo test. | + +## Examples + +Register a callback that logs the assertion result and its message: + +```js +QUnit.log(details => { + console.log(`Log: ${details.result}, ${details.message}`); +}); +``` + +--- + +Log the module name and test result whenever an assertion fails: + +```js +QUnit.log(details => { + if (details.result) { + return; + } + + let output = `[FAILED] ${details.module} > ${details.name}`; + + if (details.message) { + output += `: ${details.message}`; + } + if (details.actual) { + output += `\nexpected: ${details.expected}\nactual: ${details.actual}`; + } + if (details.source) { + output += `\n${details.source}`; + } + + console.log(output); +}); +``` diff --git a/api/callbacks/QUnit.moduleDone.md b/api/callbacks/QUnit.moduleDone.md new file mode 100644 index 0000000..61867a9 --- /dev/null +++ b/api/callbacks/QUnit.moduleDone.md @@ -0,0 +1,41 @@ +--- +layout: page-api +title: QUnit.moduleDone() +excerpt: Register a callback to fire whenever a module ends. +groups: + - callbacks +redirect_from: + - "/callbacks/QUnit.moduleDone/" + - "/QUnit.moduleDone/" +version_added: "1.0.0" +--- + +`QUnit.moduleDone( callback )` + +Register a callback to fire whenever a module ends. The callback may be an async function, or a function that return a promise which will be waited for before the next callback is handled. + +| parameter | description | +|-----------|-------------| +| callback (function) | Callback to execute. Provides a single argument with the callback Details object | + +### Details object + +Passed to the callback: + +| property | description | +|-----------|-------------| +| `name` (string) | Name of this module | +| `failed` (number) | The number of failed assertions | +| `passed` (number) | The number of passed assertions | +| `total` (number) | The total number of assertions | +| `runtime` (number) | The execution time in milliseconds of this module | + +## Examples + +Register a callback that logs the module results + +```js +QUnit.moduleDone(details => { + console.log(`Finished running: ${details.name} Failed/total: ${details.failed}/${details.total}`); +}); +``` diff --git a/api/callbacks/QUnit.moduleStart.md b/api/callbacks/QUnit.moduleStart.md new file mode 100644 index 0000000..967e8d8 --- /dev/null +++ b/api/callbacks/QUnit.moduleStart.md @@ -0,0 +1,37 @@ +--- +layout: page-api +title: QUnit.moduleStart() +excerpt: Register a callback to fire whenever a module begins. +groups: + - callbacks +redirect_from: + - "/callbacks/QUnit.moduleStart/" + - "/QUnit.moduleStart/" +version_added: "1.0.0" +--- + +`QUnit.moduleStart( callback )` + +Register a callback to fire whenever a module begins. The callback can return a promise that will be waited for before the next callback is handled. + +| parameter | description | +|-----------|-------------| +| callback (function) | Callback to execute. Provides a single argument with the callback Details object | + +### Details object + +Passed to the callback: + +| property | description | +|-----------|-------------| +| `name` (string) | Name of the next module to run | + +## Examples + +Register a callback that logs the module name + +```js +QUnit.moduleStart(details => { + console.log(`Now running: ${details.name}`); +}); +``` diff --git a/api/callbacks/QUnit.on.md b/api/callbacks/QUnit.on.md new file mode 100644 index 0000000..fe8282c --- /dev/null +++ b/api/callbacks/QUnit.on.md @@ -0,0 +1,144 @@ +--- +layout: page-api +title: QUnit.on() +excerpt: Register a callback to fire whenever the specified event is emitted. +groups: + - callbacks +redirect_from: + - "/callbacks/QUnit.on/" +version_added: "2.2.0" +--- + +`QUnit.on( eventName, callback )` + +Register a callback to fire whenever a specified event is emitted. + +This API implements the [js-reporters CRI standard](https://github.com/js-reporters/js-reporters/blob/v2.1.0/spec/cri-draft.adoc), and is the primary interface for use by continuous integration plugins and other reporting software. + +| type | parameter | description +|--|--|-- +| `string` | `eventName` | Name of an event. +| `Function` | `callback`| A callback function. + +## The `runStart` event + +The `runStart` event indicates the beginning of a test run. It is emitted exactly once, and before any other events. + +| `Object` | `testCounts` | Aggregate counts about tests. +| `number` | `testCounts.total` | Total number of registered tests. + +```js +QUnit.on('runStart', runStart => { + console.log(`Test plan: ${runStart.testCounts.total}`); +}); +``` +## The `suiteStart` event + +The `suiteStart` event indicates the beginning of a module. It is eventually be followed by a corresponding `suiteEnd` event. + +| `string` | `name` | Name of the module. +| `Array` | `fullName`| List of one or more strings, containing (in order) the names of any ancestor modules and the name of the current module. + +```js +QUnit.on('suiteStart', suiteStart => { + console.log('suiteStart', suiteStart); + // name: 'my module' + // fullName: ['grandparent', 'parent', 'my module'] +}); +``` + +## The `suiteEnd` event + +The `suiteEnd` event indicates the end of a module. It is emitted after its corresponding `suiteStart` event. + +| `string` | `name` | Name of the module. +| `Array` | `fullName`| List of one or more strings, containing (in order) the names of any ancestor modules and the name of the current module. +| `string` | `status` | Aggregate result of tests in this module, one of:
`failed`: at least one test has failed;
`passed`: there were no failing tests, which means there were only tests with a passed, skipped, or todo status. +| `number` | `runtime` | Duration of the module in milliseconds. + +```js +QUnit.on('suiteEnd', suiteEnd => { + console.log(suiteEnd); + // … +}); +``` + +## The `testStart` event + +The `testStart` event indicates the beginning of a test. It is eventually followed by a corresponding `testEnd` event. + +| `string` | `name` | Name of the test. +| `string|null` | `moduleName` | The module the test belongs to, or null for a global test. +| `Array` | `fullName` | List (in order) of the names of any ancestor modules and the name of the test itself. + +```js +QUnit.on('testStart', testStart => { + console.log(testStart); + // name: 'my test' + // moduleName: 'my module' + // fullName: ['parent', 'my module', 'my test'] + + // name: 'global test' + // moduleName: null + // fullName: ['global test'] +}); +``` + +## The `testEnd` event + +The `testEnd` event indicates the end of a test. It is emitted after its corresponding `testStart` event. + +Properties of a testEnd object: + +| `string` | `name` | Name of the test. +| `string|null` | `moduleName` | The module the test belongs to, or null for a global test. +| `Array` | `fullName` | List (in order) of the names of any ancestor modules and the name of the test itself. +| `string` | `status` | Result of the test, one of:
`passed`: all assertions passed or no assertions found;
`failed`: at least one assertion failed or it is a [todo test](../QUnit/test.todo.md) that no longer has any failing assertions;
`skipped`: the test was intentionally not run; or
`todo`: the test is "todo" and still has a failing assertion. +| `number` | `runtime` | Duration of the test in milliseconds. +| `Array` | `errors` | For tests with status `failed` or `todo`, there will be at least one failed assertion. However, the list may be empty if the status is `failed` due to a "todo" test having no failed assertions.

Note that all negative test outcome communicate their details in this manner. For example, timeouts, uncaught errors, and [global pollution](../config/noglobals.md) also synthesize a failed assertion. + +Properties of a FailedAssertion object: + +| `boolean` | `passed` | False for a failed assertion. +| `string|undefined` | `message` | Description of what the assertion checked. +| `any` | `actual` | The actual value passed to the assertion. +| `any` | `expected` | The expected value passed to the assertion. +| `string|undefined` | `stack` | Stack trace, may be undefined if the result came from an old web browsers. + +```js +QUnit.on('testEnd', testEnd => { + if (testEnd.status === 'failed') { + console.error('Failed! ' + testEnd.fullName.join(' > ')); + testEnd.errors.forEach(assertion => { + console.error(assertion); + // message: speedometer + // actual: 75 + // expected: 88 + // stack: at dmc.test.js:12 + }); + } +}); +``` + +## The `runEnd` event + +The `runEnd` event indicates the end of a test run. It is emitted exactly once. + +| `string` | `status` | Aggregate result of all tests, one of:
`failed`: at least one test failed or a global error occurred;
`passed`: there were no failed tests, which means there were only tests with a passed, skipped, or todo status. If [`QUnit.config.failOnZeroTests`](../config/failOnZeroTests.md) is disabled, then the run may also pass if there were no tests. +| `Object` | `testCounts` | Aggregate counts about tests: +| `number` | `testCounts.passed` | Number of passed tests. +| `number` | `testCounts.failed` | Number of failed tests. +| `number` | `testCounts.skipped` | Number of skipped tests. +| `number` | `testCounts.todo` | Number of todo tests. +| `number` | `testCounts.total` | Total number of tests, equal to the sum of the above properties. +| `number` | `runtime` | Total duration of the run in milliseconds. + +```js +QUnit.on('runEnd', runEnd => { + console.log(`Passed: ${runEnd.passed}`); + console.log(`Failed: ${runEnd.failed}`); + console.log(`Skipped: ${runEnd.skipped}`); + console.log(`Todo: ${runEnd.todo}`); + console.log(`Total: ${runEnd.total}`); +}); +``` diff --git a/api/callbacks/QUnit.testDone.md b/api/callbacks/QUnit.testDone.md new file mode 100644 index 0000000..f211989 --- /dev/null +++ b/api/callbacks/QUnit.testDone.md @@ -0,0 +1,57 @@ +--- +layout: page-api +title: QUnit.testDone() +excerpt: Register a callback to fire whenever a test ends. +groups: + - callbacks +redirect_from: + - "/callbacks/QUnit.testDone/" + - "/QUnit.testDone/" +version_added: "1.0.0" +--- + +`QUnit.testDone( callback )` + +Register a callback to fire whenever a test ends. The callback may be an async function, or a function that return a promise which will be waited for before the next callback is handled. + +| parameter | description | +|-----------|-------------| +| callback (function) | Callback to execute. Provides a single argument with the callback Details object | + +### Details object + +Passed to the callback: + +| property | description | +|-----------|-------------| +| `name` (string) | Name of the current test | +| `module` (string) | Name of the current module | +| `failed` (number) | The number of failed assertions | +| `passed` (number) | The number of passed assertions | +| `total` (number) | The total number of assertions | +| `runtime` (number) | The execution time in milliseconds of the test, including beforeEach and afterEach calls | +| `skipped` (boolean) | Indicates whether or not the current test was skipped | +| `todo` (boolean) | Indicates whether or not the current test was a todo | + +## Examples + +Register a callback that logs results of a single test: + +```js +QUnit.testDone(details => { + const result = { + 'Module name': details.module, + 'Test name': details.name, + Assertions: { + Total: details.total, + Passed: details.passed, + Failed: details.failed + }, + Skipped: details.skipped, + Todo: details.todo, + Runtime: details.runtime + }; + + console.log(JSON.stringify(result, null, 2)); +}); +``` diff --git a/api/callbacks/QUnit.testStart.md b/api/callbacks/QUnit.testStart.md new file mode 100644 index 0000000..b26af20 --- /dev/null +++ b/api/callbacks/QUnit.testStart.md @@ -0,0 +1,40 @@ +--- +layout: page-api +title: QUnit.testStart() +excerpt: Register a callback to fire whenever a test begins. +groups: + - callbacks +redirect_from: + - "/callbacks/QUnit.testStart/" + - "/QUnit.testStart/" +version_added: "1.0.0" +--- + +`QUnit.testStart( callback )` + +Register a callback to fire whenever a test begins. The callback may be an async function, or a function that return a promise which will be waited for before the next callback is handled. + +| parameter | description | +|-----------|-------------| +| callback (function) | Callback to execute. Provides a single argument with the callback Details object | + +### Details object + +Passed to the callback: + +| property | description | +|-----------|-------------| +| `name` (string) | Name of the next test to run | +| `module` (string) | Name of the current module | +| `testId` (string) | Id of the next test to run | +| `previousFailure` (boolean) | Whether the next test failed on a previous run | + +## Examples + +Register a callback that logs the module and test name: + +```js +QUnit.testStart(details => { + console.log(`Now running: ${details.module} ${details.name}`); +}); +``` diff --git a/api/callbacks/index.md b/api/callbacks/index.md new file mode 100644 index 0000000..89412aa --- /dev/null +++ b/api/callbacks/index.md @@ -0,0 +1,8 @@ +--- +layout: group +group: callbacks +title: Callback events +redirect_from: + - "/callbacks/" + - "/category/callbacks/" +--- diff --git a/api/config/altertitle.md b/api/config/altertitle.md new file mode 100644 index 0000000..d90e7f9 --- /dev/null +++ b/api/config/altertitle.md @@ -0,0 +1,27 @@ +--- +layout: page-api +title: QUnit.config.altertitle +excerpt: Insert a success or failure symbol in the document title (HTML Reporter). +groups: + - config +redirect_from: + - "/config/altertitle/" +version_added: "1.0.0" +--- + +In the HTML Reporter, whether to insert a success or failure symbol in the document title. + + + + + + + + + + +
type`boolean`
default`true`
+ +By default, QUnit updates `document.title` to insert a checkmark or cross symbol to indicate whether the test run passed or failed. This helps quickly spot from the tab bar whether a run passed, without opening it. + +If you're integration-testing code that makes changes to `document.title`, or otherwise conflicts with this feature, you can disable it. diff --git a/api/config/autostart.md b/api/config/autostart.md new file mode 100644 index 0000000..84ed082 --- /dev/null +++ b/api/config/autostart.md @@ -0,0 +1,72 @@ +--- +layout: page-api +title: QUnit.config.autostart +excerpt: Control when the test run may start. +groups: + - config +redirect_from: + - "/config/autostart/" +version_added: "1.0.0" +--- + +Control when the test run may start, e.g. after asynchronously loading test files with RequireJS, AMD, ES6 dynamic imports, or other means. + + + + + + + + + + +
type`boolean`
default`true`
+ +In the browser, QUnit by default waits for all ` + +``` + +```js +// tests.js +QUnit.config.autostart = false; + +Promise.all([ + import('./foo.js'), + import('./bar.js') +]).then(function () { + QUnit.start(); +}); +``` + +### Loading with RequireJS + +This example uses [RequireJS](https://requirejs.org/) to call a "require" function as defined by the [AMD specification](https://github.com/amdjs/amdjs-api/blob/master/require.md) (Asynchronous Module Definition). + +```js +QUnit.config.autostart = false; + +require( + [ + 'tests/testModule1', + 'tests/testModule2' + ], + function () { + QUnit.start(); + } +); +``` diff --git a/api/config/collapse.md b/api/config/collapse.md new file mode 100644 index 0000000..d8d88d6 --- /dev/null +++ b/api/config/collapse.md @@ -0,0 +1,27 @@ +--- +layout: page-api +title: QUnit.config.collapse +excerpt: Collapse the details of failing tests after the first one (HTML Reporter). +groups: + - config +redirect_from: + - "/config/collapse/" +version_added: "1.0.0" +--- + +In the HTML Reporter, collapse the details of failing tests after the first one. + + + + + + + + + + +
type`boolean`
default`true`
+ +By default, QUnit's HTML Reporter collapses consecutive failing tests showing only the details for the first failed test. The other tests can be expanded manually with a single click on the test title. + +Set this option to `false` to expand the details for all failing tests. diff --git a/api/config/current.md b/api/config/current.md new file mode 100644 index 0000000..2944aa4 --- /dev/null +++ b/api/config/current.md @@ -0,0 +1,40 @@ +--- +layout: page-api +title: QUnit.config.current +excerpt: Internal object representing the currently running test. +groups: + - config + - extension +redirect_from: + - "/config/current/" +version_added: "1.0.0" +--- + +Internal object representing the currently running test. + + + + + + +
type`object` (read-only)
+ +This property is not actually a configuration option, but is exposed under `QUnit.config` for use by plugins and other integrations. This offers access to QUnit's internal `Test` object at runtime. + +Internals may change without notice. When possible, use [QUnit.on](../callbacks/QUnit.on.md) or [other callbacks](../callbacks/index.md) instead. + +## Example + +Access `QUnit.config.current.testName` to observe the currently running test's name. + +```js +function whatsUp () { + console.log(QUnit.config.current.testName); +} + +QUnit.test('example', assert => { + whatsUp(); + + assert.true(true); +}); +``` diff --git a/api/config/failOnZeroTests.md b/api/config/failOnZeroTests.md new file mode 100644 index 0000000..0957167 --- /dev/null +++ b/api/config/failOnZeroTests.md @@ -0,0 +1,27 @@ +--- +layout: page-api +title: QUnit.config.failOnZeroTests +excerpt: Fail the test run if no tests were run. +groups: + - config +redirect_from: + - "/config/failOnZeroTests/" +version_added: "2.16.0" +--- + +Whether to fail the test run if no tests were run. + + + + + + + + + + +
type`boolean`
default`true`
+ +By default, it is considered an error if no tests were loaded, or if no tests matched the current filter. + +Set this option to `false` to let an empty test run result in a success instead. diff --git a/api/config/filter.md b/api/config/filter.md new file mode 100644 index 0000000..52a5e85 --- /dev/null +++ b/api/config/filter.md @@ -0,0 +1,80 @@ +--- +layout: page-api +title: QUnit.config.filter +excerpt: Select tests to run based on a substring or pattern match. +groups: + - config +redirect_from: + - "/config/filter/" +version_added: "1.0.0" +--- + +Select tests to run based on a substring or pattern match. + + + + + + + + + + +
type`string` or `undefined`
default`undefined`
+ +

+ +This option is available as [CLI option](../../cli.md), as control in the [HTML Reporter](../../intro.md#in-the-browser), and supported as URL query parameter. + +

+ +QUnit only runs tests of which the module name or test name are a case-insensitive substring match for the filter string. You can invert the filter by prefixing an exclamation mark (`!`) to the string, in which case we skip the matched tests, and run the tests that don't match the filter. + +You can also match via a regular expression by setting the filter to a regular expression literal, enclosed by slashes, such as `/(this|that)/`. + +While substring filters are always **case-insensitive**, a regular expression is case-sensitive by default. + +See also: +* [QUnit.config.module](./module.md) + +## Examples + +### Substring filter + +The below matches `FooBar` and `foo > bar`, because string matching is case-insensitive. + +```js +QUnit.config.filter = 'foo'; +``` + +As inversed filter, the below skips `FooBar` and `foo > bar`, but runs `Bar` and `bar > sub`. + +```js +QUnit.config.filter = '!foo'; +``` + +### Regular expression filter + +The below matches `foo` but not `Foo`, because regexes are case-sensitive by default. + +```js +QUnit.config.filter = '/foo/'; +``` + +The below matches both `foo` and `Foo`. + +```js +QUnit.config.filter = '/foo/i'; +``` + +The below skips both `foo` and `Foo`. + +```js +QUnit.config.filter = '!/foo/i'; +``` + +The below matches `foo`, `foo > sub`, and `foo.sub`, but skips `bar`, `bar.foo`, and `FooBar`. + +```js +QUnit.config.filter = '/^foo/'; +``` diff --git a/api/config/fixture.md b/api/config/fixture.md new file mode 100644 index 0000000..b1cf7f3 --- /dev/null +++ b/api/config/fixture.md @@ -0,0 +1,27 @@ +--- +layout: page-api +title: QUnit.config.fixture +excerpt: HTML content to render in the fixture container at the start of each test (HTML Reporter). +groups: + - config +redirect_from: + - "/config/fixture/" +version_added: "1.0.0" +--- + +In the HTML Reporter, this HTML content will be rendered in the fixture container at the start of each test. + + + + + + + + + + +
type`string` or `null` or `undefined`
default`undefined`
+ +By default QUnit will observe the initial content of the `#qunit-fixture` element, and use that as the fixture content for all tests. Use this option to configure the fixture content through JavaScript instead. + +To disable QUnit's fixture resetting behaviour, set the option to `null`. diff --git a/api/config/hidepassed.md b/api/config/hidepassed.md new file mode 100644 index 0000000..a58767b --- /dev/null +++ b/api/config/hidepassed.md @@ -0,0 +1,27 @@ +--- +layout: page-api +title: QUnit.config.hidepassed +excerpt: Hide results of passed tests (HTML Reporter). +groups: + - config +redirect_from: + - "/config/hidepassed/" +version_added: "1.0.0" +--- + +In the HTML Reporter, hide results of passed tests. + + + + + + + + + + +
type`boolean`
default`false`
+ +

This option can also be controlled via the [HTML Reporter](../../intro.md#in-the-browser).

+ +By default, the HTML Reporter will list (in collapsed form) the names of all passed tests. Enable this option, to only list failing tests. diff --git a/api/config/index.md b/api/config/index.md new file mode 100644 index 0000000..137e9ed --- /dev/null +++ b/api/config/index.md @@ -0,0 +1,40 @@ +--- +layout: group +group: config +title: QUnit.config +redirect_from: + - "/QUnit.config/" + - "/config/" +--- + +General configuration options for QUnit. + +## Preconfiguring QUnit + +If you load QUnit asynchronously or otherwise need to configure QUnit before it is loaded, you can predefine the configuration by creating a global variable `QUnit` with a `config` property. + +The config values specified here will be carried over to the real `QUnit.config` object. Any other properties of this object will be ignored. + +```js +// Implicit global +// Supported everywhere, including old browsers. (But not ES strict mode.) +QUnit = { + config: { + autostart: false, + maxDepth: 12 + } +}; + +// Browser global +// For all browsers (including strict mode and old browsers) +window.QUnit = { /* .. */ }; + +// Isomorphic global +// For modern browsers, SpiderMonkey, and Node.js (incl. strict mode). +globalThis.QUnit = { /* .. */ }; +``` + +### Changelog + +| [QUnit 2.18.1](https://github.com/qunitjs/qunit/releases/tag/2.18.1) | Preconfig support added for SpiderMonkey and other environments.
Previously, it was limited to the browser environment. +| [QUnit 2.1.0](https://github.com/qunitjs/qunit/releases/tag/2.1.0) | Preconfig feature introduced. diff --git a/api/config/maxDepth.md b/api/config/maxDepth.md new file mode 100644 index 0000000..4a0f9b4 --- /dev/null +++ b/api/config/maxDepth.md @@ -0,0 +1,27 @@ +--- +layout: page-api +title: QUnit.config.maxDepth +excerpt: The depth up-to which an object will be serialized during a diff (HTML Reporter). +groups: + - config +redirect_from: + - "/config/maxDepth/" +version_added: "1.16.0" +--- + +In the HTML Reporter, the depth up-to which an object will be serialized during the diff of an assertion failure. + + + + + + + + + + +
type`number`
default`5`
+ +To disable the depth limit, use a value of `-1`. + +This is used by [`QUnit.dump.parse()`](../extension/QUnit.dump.parse.md). diff --git a/api/config/module.md b/api/config/module.md new file mode 100644 index 0000000..6bc0f4a --- /dev/null +++ b/api/config/module.md @@ -0,0 +1,38 @@ +--- +layout: page-api +title: QUnit.config.module +excerpt: Select a single test module to run. +groups: + - config +redirect_from: + - "/config/module/" +version_added: "1.8.0" +--- + +Select a single test module to run by name. + + + + + + + + + + +
type`string` or `undefined`
default`undefined`
+ +

This option can also be set by URL query parameter.

+ +When specified, only a single module will be run if its name is a complete case-insensitive match. If no module name matches, then no tests will be run. + +This option is undefined by default, which means all loaded test modules will be run. + +See also: +* [QUnit.config.filter](./filter.md) +* [QUnit.config.moduleId](./moduleId.md) + +## Changelog + +| [QUnit 1.23](https://github.com/qunitjs/qunit/releases/tag/1.23.0) | The public config property was restored. +| [QUnit 1.16](https://github.com/qunitjs/qunit/releases/tag/1.16.0) | The public config property was removed (the URL query parameter was unaffected). diff --git a/api/config/moduleId.md b/api/config/moduleId.md new file mode 100644 index 0000000..bcabfb5 --- /dev/null +++ b/api/config/moduleId.md @@ -0,0 +1,31 @@ +--- +layout: page-api +title: QUnit.config.moduleId +excerpt: Select one or more modules to run, by their internal ID (HTML Reporter). +groups: + - config +redirect_from: + - "/config/moduleId/" +version_added: "1.23.0" +--- + +In the HTML Reporter, select one or more modules to run by their internal ID. + + + + + + + + + + +
type`array` or `undefined`
default`undefined`
+ +

This option can be controlled via the [HTML Reporter](../../intro.md#in-the-browser) interface.

+ +Specify modules by their internally hashed identifier for a given module. You can specify one or multiple modules to run. This option powers the multi-select dropdown menu in the HTML Reporter. + +See also: +* [QUnit.config.module](./module.md) +* [QUnit.config.testId](./testId.md) diff --git a/api/config/modules.md b/api/config/modules.md new file mode 100644 index 0000000..2989f12 --- /dev/null +++ b/api/config/modules.md @@ -0,0 +1,41 @@ +--- +layout: page-api +title: QUnit.config.modules +excerpt: List of defined test modules. +groups: + - config + - extension +redirect_from: + - "/config/modules/" +version_added: "1.16.0" +--- + +List of defined test modules. + + + + + + +
type`Array` (read-only)
+ +This property is exposed under `QUnit.config` for use by plugins and other integrations. It returns an array of internal `Module` objects, one for each call to [`QUnit.module()`](../QUnit/module.md). + +Before accessing this property, wait for the [`QUnit.on('runStart')`](../callbacks/QUnit.on.md#the-runstart-event) event, or use a [`QUnit.begin()`](../callbacks/QUnit.begin.md) callback. + +### Module object + +The following properties are considered publicly supported: + +| property | description | +|-----------|-------------| +| `name` (string) | Module name, as passed to [`QUnit.module()`](../QUnit/module.md). +| `moduleId` (string) | Hashed identifier, for the [QUnit.config.moduleId](./moduleId.md) filter. + +## Example + +```js +QUnit.on('runStart', () => { + console.log(QUnit.config.modules.map(mod => mod.name)); +}); +``` diff --git a/api/config/noglobals.md b/api/config/noglobals.md new file mode 100644 index 0000000..1309eeb --- /dev/null +++ b/api/config/noglobals.md @@ -0,0 +1,27 @@ +--- +layout: page-api +title: QUnit.config.noglobals +excerpt: Check the global object after each test and report new properties as failures. +groups: + - config +redirect_from: + - "/config/noglobals/" +version_added: "1.0.0" +--- + +Check the global object after each test and report new properties as failures. + + + + + + + + + + +
type`boolean`
default`false`
+ +Enable this option to let QUnit keep track of which global variables and properties exist on the global object (e.g. `window` in browsers). When new global properties are found, they will result in test failures to you make sure your application and your tests are not leaking any state. + +

This option can also be controlled via the [HTML Reporter](../../intro.md#in-the-browser).

diff --git a/api/config/notrycatch.md b/api/config/notrycatch.md new file mode 100644 index 0000000..d4e9a65 --- /dev/null +++ b/api/config/notrycatch.md @@ -0,0 +1,29 @@ +--- +layout: page-api +title: QUnit.config.notrycatch +excerpt: Disable handling of uncaught exceptions during tests. +groups: + - config +redirect_from: + - "/config/notrycatch/" +version_added: "1.0.0" +--- + +Disable handling of uncaught exceptions during tests. + + + + + + + + + + +
type`boolean`
default`false`
+ +

This option can also be controlled via the [HTML Reporter](../../intro.md#in-the-browser) interface, and is supported as URL query parameter.

+ +By default, QUnit handles uncaught exceptions during test execution and reports them as test failures. This lets other tests continue running and allows reporters to summarise results. + +Enabling this flag will disable this error handling, allowing you to more easily debug uncaught exceptions through developer tools. diff --git a/api/config/reorder.md b/api/config/reorder.md new file mode 100644 index 0000000..9526b35 --- /dev/null +++ b/api/config/reorder.md @@ -0,0 +1,29 @@ +--- +layout: page-api +title: QUnit.config.reorder +excerpt: Allow re-running of previously failed tests out of order. +groups: + - config +redirect_from: + - "/config/reorder/" +version_added: "1.0.0" +--- + +Allow re-running of previously failed tests out of order, before all other tests. + + + + + + + + + + +
type`boolean`
default`true`
+ +By default, QUnit will first re-run any tests that failed on a previous run. For large test suites, this can speed up your feedback cycle by a lot. + +Note that this feature may lead to unexpected failures if you have non-atomic tests that rely on a very specific execution order. You should consider improving such tests, but this option allows you to disable the reordering behaviour. + +When a previously failed test is running first, the HTML Reporter displays "_Rerunning previously failed test_" in the summary whereas just "_Running_" is displayed otherwise. diff --git a/api/config/requireExpects.md b/api/config/requireExpects.md new file mode 100644 index 0000000..144a293 --- /dev/null +++ b/api/config/requireExpects.md @@ -0,0 +1,25 @@ +--- +layout: page-api +title: QUnit.config.requireExpects +excerpt: Fail tests that don't specify how many assertions they expect. +groups: + - config +redirect_from: + - "/config/requireExpects/" +version_added: "1.7.0" +--- + +Fail tests that don't specify how many assertions they expect. + + + + + + + + + + +
type`boolean`
default`false`
+ +Enabling this option will cause tests to fail if they don't call [`assert.expect()`](../assert/expect.md). diff --git a/api/config/scrolltop.md b/api/config/scrolltop.md new file mode 100644 index 0000000..0022fdd --- /dev/null +++ b/api/config/scrolltop.md @@ -0,0 +1,27 @@ +--- +layout: page-api +title: QUnit.config.scrolltop +excerpt: Scroll to the top of the page after the test run (HTML Reporter). +groups: + - config +redirect_from: + - "/config/scrolltop/" +version_added: "1.14.0" +--- + +In the HTML Reporter, ensure the browser is scrolled to the top of the page when the tests are done. + + + + + + + + + + +
type`boolean`
default`true`
+ +By default, QUnit scrolls the browser to the top of the page when tests are done. This reverses any programmatic scrolling performed by the application or its tests. + +Set this option to `false` to disable this behaviour, and thus leave the page in its final scroll position. diff --git a/api/config/seed.md b/api/config/seed.md new file mode 100644 index 0000000..cb28757 --- /dev/null +++ b/api/config/seed.md @@ -0,0 +1,33 @@ +--- +layout: page-api +title: QUnit.config.seed +excerpt: Enable randomized ordering of tests. +groups: + - config +redirect_from: + - "/config/seed/" +version_added: "1.23.0" +--- + +Enable randomized ordering of tests. + + + + + + + + + + +
type`string` or `boolean` or `undefined`
default`undefined`
+ +

This option is also available as [CLI option](../../cli.md), and as URL query parameter in the browser.

+ +When set to boolean true, or a string, QUnit will run tests in a [seeded-random order](https://en.wikipedia.org/wiki/Random_seed). + +The provided string will be used as the seed in a pseudo-random number generator to ensure that results are reproducible. The randomization will also respect the [reorder](./reorder.md) option if enabled and re-run failed tests first without randomizing them. + +Randomly ordering your tests can help identify non-atomic tests which either depend on a previous test or are leaking state to subsequent tests. + +If `seed` is boolean true (or set as URL query parameter without a value), then QUnit will generate on-demand a new random value to use as seed. You can then read the seed at runtime from the configuration value, and use it to reproduce the same test sequence later. diff --git a/api/config/storage.md b/api/config/storage.md new file mode 100644 index 0000000..e5a4dae --- /dev/null +++ b/api/config/storage.md @@ -0,0 +1,27 @@ +--- +layout: page-api +title: QUnit.config.storage +excerpt: The Storage object to use for remembering failed tests between runs. +groups: + - config +redirect_from: + - "/config/storage/" +version_added: "2.1.0" +--- + +The Storage object to use for remembering failed tests between runs. + + + + + + + + + + +
type`object` or `undefined`
default`globalThis.sessionStorage`
+ +This is mainly for use by the HTML Reporter, where `sessionStorage` will be used if supported by the browser. + +While Node.js and other non-browser environments are not known to offer something like this by default, one can attach any preferred form of persistence by assigning an object that implements the [`Storage` interface methods](https://html.spec.whatwg.org/multipage/webstorage.html#the-storage-interface) of the Web Storage API. diff --git a/api/config/testId.md b/api/config/testId.md new file mode 100644 index 0000000..2db6dbb --- /dev/null +++ b/api/config/testId.md @@ -0,0 +1,31 @@ +--- +layout: page-api +title: QUnit.config.testId +excerpt: Select one or more tests to run, by their internal ID (HTML Reporter). +groups: + - config +redirect_from: + - "/config/testId/" +version_added: "1.16.0" +--- + +In the HTML Reporter, select one or more tests to run by their internal ID. + + + + + + + + + + +
type`array` or `undefined`
default`undefined`
+ +

This option can be controlled via the [HTML Reporter](../../intro.md#in-the-browser) interface.

+ +This property allows QUnit to run specific tests by their internally hashed identifier. You can specify one or multiple tests to run. This option powers the "Rerun" button in the HTML Reporter. + +See also: +* [QUnit.config.filter](./filter.md) +* [QUnit.config.moduleId](./moduleId.md) diff --git a/api/config/testTimeout.md b/api/config/testTimeout.md new file mode 100644 index 0000000..1ed2534 --- /dev/null +++ b/api/config/testTimeout.md @@ -0,0 +1,25 @@ +--- +layout: page-api +title: QUnit.config.testTimeout +excerpt: Set a global default timeout after which a test will fail. +groups: + - config +redirect_from: + - "/config/testTimeout/" +version_added: "1.0.0" +--- + +Set a global default timeout in milliseconds after which a test will fail. This helps to detect async tests that are broken, and prevents tests from running indefinitely. + + + + + + + + + + +
type`number` or `undefined`
default`undefined`
+ +This can be overridden on a per-test basis via [assert.timeout()](../assert/timeout.md). If you don't have per-test overrides, it is recommended to set this to a relatively high value (e.g. `30000` for 30 seconds) to avoid intermittent test failures from unrelated delays one might in a browser or CI service. diff --git a/api/config/urlConfig.md b/api/config/urlConfig.md new file mode 100644 index 0000000..1524ab4 --- /dev/null +++ b/api/config/urlConfig.md @@ -0,0 +1,89 @@ +--- +layout: page-api +title: QUnit.config.urlConfig +excerpt: Register additional input fields in the toolbar (HTML Reporter). +groups: + - config + - extension +redirect_from: + - "/config/urlConfig/" +version_added: "1.0.0" +--- + +In the HTML Reporter, this array is used to generate additional input fields in the toolbar. + + + + + + + + + + +
type`array`
default`[]`
+ +This property controls which form controls to put into the QUnit toolbar. By default, the `noglobals` and `notrycatch` checkboxes are registered. By adding to this array, you can add your own checkboxes and select dropdowns. + +Each array item should be an object shaped as follows: + +```js +({ + id: string, + label: string, + tooltip: string, // optional + value: string | array | object // optional +}); +``` + +* The `id` property is used as the key for storing the value under `QUnit.config`, and as URL query parameter. +* The `label` property is used as text label in the user interface. +* The optional `tooltip` property is used as the `title` attribute and should explain what the control is used for. + +Each element should also have a `value` property controlling available options and rendering. + +If `value` is undefined, the option will render as a checkbox. The corresponding URL parameter will be set to "true" when the checkbox is checked, and otherwise will be absent. + +If `value` is a string, the option will render as a checkbox. The corresponding URL parameter will be set to the value when the checkbox is checked, and otherwise will be absent. + +If `value` is an array, the option will render as a "select one" menu with an empty value as first default option, followed by one option for each item in the array. The corresponding URL parameter will be absent when the empty option is selected, and otherwise will be set to the value of the selected array item. + +```js +value = [ 'foobar', 'baz' ]; +``` + +If `value` is an object, the option will render as a "select one" menu as for an array. The keys will be used as option values, and the values will be used as option display labels. The corresponding URL parameter will be absent when the empty option is selected, and otherwise will be set to the object key of the selected property. + +```js +value = { + foobar: 'Foo with bar', + baz: 'Baz' +}; +``` + +## Examples + +### Add toolbar checkbox + +Add a new checkbox to the toolbar. You can then use the `QUnit.config.min` property in your code to implement a behaviour based on it. + +```js +QUnit.config.urlConfig.push({ + id: 'min', + label: 'Minified source', + tooltip: 'Load minified source files instead of the regular unminified ones.' +}); +``` + +### Add dropdown menu + +Add a dropdown to the toolbar. + +```js +QUnit.config.urlConfig.push({ + id: 'jquery', + label: 'jQuery version', + value: [ '1.7.2', '1.8.3', '1.9.1' ], + tooltip: 'Which jQuery version to test against.' +}); +``` diff --git a/api/deprecated.md b/api/deprecated.md new file mode 100644 index 0000000..cae4b17 --- /dev/null +++ b/api/deprecated.md @@ -0,0 +1,7 @@ +--- +layout: group +group: deprecated +title: Deprecated methods +redirect_from: + - "/deprecated/" +--- diff --git a/api/extension/QUnit.assert.md b/api/extension/QUnit.assert.md new file mode 100644 index 0000000..7044291 --- /dev/null +++ b/api/extension/QUnit.assert.md @@ -0,0 +1,17 @@ +--- +layout: page-api +title: QUnit.assert +excerpt: Namespace for QUnit assertion methods. +groups: + - extension +redirect_from: + - "/config/QUnit.assert/" + - "/extension/QUnit.assert/" +version_added: "1.7.0" +--- + +Namespace for QUnit assertion methods. This object is the prototype for the internal Assert class of which instances are passed as the argument to [`QUnit.test()`](../QUnit/test.md) callbacks. + +This object contains QUnit's [built-in assertion methods](../assert/index.md), and may be extended by plugins to register additional assertion methods. + +See [`assert.pushResult()`](../assert/pushResult.md) for how to create a custom assertion. diff --git a/api/extension/QUnit.dump.parse.md b/api/extension/QUnit.dump.parse.md new file mode 100644 index 0000000..5aa6b7f --- /dev/null +++ b/api/extension/QUnit.dump.parse.md @@ -0,0 +1,84 @@ +--- +layout: page-api +title: QUnit.dump.parse() +excerpt: Extensible data dumping and string serialization. +groups: + - extension +redirect_from: + - "/QUnit.dump.parse/" + - "/QUnit.jsDump.parse/" + - "/config/QUnit.dump.parse/" + - "/extension/QUnit.dump.parse/" +version_added: "1.0.0" +--- + +`QUnit.dump.parse( data )` + +Extensible data dumping and string serialization. + +| name | description | +|------|-------------| +| `data` | Data structure or object to parse. | + +This method does string serialization by parsing data structures and objects. It parses DOM elements to a string representation of their outer HTML. By default, nested structures will be displayed up to five levels deep. Anything beyond that is replaced by `[object Object]` and `[object Array]` placeholders. + +If you need more or less output, change the value of `QUnit.dump.maxDepth`, representing how deep the elements should be parsed. + +## Changelog + +| [QUnit 2.1](https://github.com/qunitjs/qunit/releases/tag/2.1.0) | The `QUnit.jsDump` alias was removed. +| [QUnit 1.15](https://github.com/qunitjs/qunit/releases/tag/1.15.0) | The `QUnit.jsDump` interface was renamed to `QUnit.dump`.
The `QUnit.jsDump` alias is deprecated. + +## Examples + +The following is an example from [grunt-contrib-qunit][], which sends results from QUnit (running in Headless Chrome) to a CLI tool. + +[grunt-contrib-qunit]: https://github.com/gruntjs/grunt-contrib-qunit/blob/188a29af7817e1798fdd95f1ab7d3069231e4859/chrome/bridge.js#L42-L60 + +```js +QUnit.log(function (obj) { + var actual; + var expected; + + if (!obj.result) { + // Format before sending + actual = QUnit.dump.parse(obj.actual); + expected = QUnit.dump.parse(obj.expected); + } + + // ... +}); +``` + +--- + +This example shows the formatted representation of a DOM element. + +```js +var qHeader = document.getElementById('qunit-header'); +var parsed = QUnit.dump.parse(qHeader); + +console.log(parsed); + +// Logs: '

' +``` + +--- + +Limit output to one or two levels + +```js +var input = { + parts: { + front: [], + back: [] + } +}; +QUnit.dump.maxDepth = 1; +console.log(QUnit.dump.parse(input)); +// Logs: { "parts": [object Object] } + +QUnit.dump.maxDepth = 2; +console.log(QUnit.dump.parse(input)); +// Logs: { "parts": { "back": [object Array], "front": [object Array] } } +``` diff --git a/api/extension/QUnit.extend.md b/api/extension/QUnit.extend.md new file mode 100644 index 0000000..f804335 --- /dev/null +++ b/api/extension/QUnit.extend.md @@ -0,0 +1,50 @@ +--- +layout: page-api +title: QUnit.extend() +excerpt: Copy the properties from one object into a target object. +groups: + - extension + - deprecated +redirect_from: + - "/config/QUnit.extend/" + - "/extension/QUnit.extend/" +version_added: "1.0.0" +version_deprecated: "2.12.0" +--- + +`QUnit.extend( target, mixin )` + +Copy the properties defined by a mixin object into a target object. + +

This method is __deprecated__ and it's recommended to use [`Object.assign()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) instead.

+ +| name | description | +|------|-------------| +| `target` | An object whose properties are to be modified | +| `mixin` | An object describing which properties should be modified | + +This method will modify the `target` object to contain the "own" properties defined by the `mixin`. If the `mixin` object specifies the value of any attribute as `undefined`, this property will instead be removed from the `target` object. + +## Examples + +Use `QUnit.extend` to merge two objects. + +```js +QUnit.test('QUnit.extend', assert => { + const base = { + a: 1, + b: 2, + z: 3 + }; + QUnit.extend(base, { + b: 2.5, + c: 3, + z: undefined + }); + + assert.strictEqual(base.a, 1, 'Unspecified values are not modified'); + assert.strictEqual(base.b, 2.5, 'Existing values are updated'); + assert.strictEqual(base.c, 3, 'New values are defined'); + assert.false('z' in base, 'Values specified as `undefined` are removed'); +}); +``` diff --git a/api/extension/QUnit.onUncaughtException.md b/api/extension/QUnit.onUncaughtException.md new file mode 100644 index 0000000..0bbe505 --- /dev/null +++ b/api/extension/QUnit.onUncaughtException.md @@ -0,0 +1,36 @@ +--- +layout: page-api +title: QUnit.onUncaughtException() +excerpt: Handle a global error. +groups: + - extension +version_added: "2.17.0" +redirect_from: + - "/config/QUnit.onUncaughtException/" + - "/extension/QUnit.onUncaughtException/" +--- + +`QUnit.onUncaughtException( error )` + +Handle a global error that should result in a failed test run. + +| name | description | +|------|-------------| +| `error` (any) | Usually an `Error` object, but any other thrown or rejected value may be given as well. | + +## Examples + +```js +const error = new Error('Failed to reverse the polarity of the neutron flow'); +QUnit.onUncaughtException(error); +``` + +```js +process.on('uncaughtException', QUnit.onUncaughtException); +``` + +```js +window.addEventListener('unhandledrejection', function (event) { + QUnit.onUncaughtException(event.reason); +}); +``` diff --git a/api/extension/QUnit.push.md b/api/extension/QUnit.push.md new file mode 100644 index 0000000..eb24991 --- /dev/null +++ b/api/extension/QUnit.push.md @@ -0,0 +1,30 @@ +--- +layout: page-api +title: QUnit.push() +excerpt: Report the result of a custom assertion. +groups: + - extension + - deprecated +redirect_from: + - "/config/QUnit.push/" + - "/extension/QUnit.push/" +version_added: "1.0.0" +version_deprecated: "2.1.0" +--- + +`QUnit.push( result, actual, expected, message )` + +Report the result of a custom assertion. + +

This method is __deprecated__ and it's recommended to use [`pushResult`](../assert/pushResult.md) in the assertion context instead.

+ +| name | description | +|------|-------------| +| `result` (boolean) | Result of the assertion | +| `actual` | Expression being tested | +| `expected` | Known comparison value | +| `message` (string) | A short description of the assertion | + +`QUnit.push` reflects to the current running test, and it may leak assertions in asynchronous mode. Checkout [`assert.pushResult()`](../assert/pushResult.md) to set a proper custom assertion. + +Invoking `QUnit.push` allows to create a readable expectation that is not defined by any of QUnit's built-in assertions. diff --git a/api/extension/QUnit.stack.md b/api/extension/QUnit.stack.md new file mode 100644 index 0000000..4eb094c --- /dev/null +++ b/api/extension/QUnit.stack.md @@ -0,0 +1,45 @@ +--- +layout: page-api +title: QUnit.stack() +excerpt: Return a single line string representing the stacktrace. +groups: + - extension +redirect_from: + - "/config/QUnit.stack/" + - "/extension/QUnit.stack/" +version_added: "1.19.0" +--- + +`QUnit.stack( offset = 0 )` + +Return a single line string representing the stacktrace (call stack). + +| name | description | +|------|-------------| +| `offset` (number) | Set the stacktrace line offset. Defaults to `0` | + +This method returns a single line string representing the stacktrace from where it was called. According to its offset argument, `QUnit.stack()` will return the correspondent line from the call stack. + +The default offset is 0 and will return the current location where it was called. + +Not all [browsers support retrieving stracktraces][browsers]. In those, `QUnit.stack()` will return `undefined`. + +[browsers]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack#Browser_compatibility + +## Examples + +The stacktrace line can be used on custom assertions and reporters. The following example [logs](../callbacks/QUnit.log.md) the line of each passing assertion. + +```js +QUnit.log(function (details) { + if (details.result) { + // 5 is the line reference for the assertion method, not the following line. + console.log(QUnit.stack(5)); + } +}); + +QUnit.test('foo', assert => { + // the log callback will report the position of the following line. + assert.true(true); +}); +``` diff --git a/api/extension/index.md b/api/extension/index.md new file mode 100644 index 0000000..c5d63cf --- /dev/null +++ b/api/extension/index.md @@ -0,0 +1,7 @@ +--- +layout: group +group: extension +title: Extension interface +redirect_from: + - "/extension/" +--- diff --git a/api/index.md b/api/index.md new file mode 100644 index 0000000..fb7a91f --- /dev/null +++ b/api/index.md @@ -0,0 +1,21 @@ +--- +layout: page-api +title: QUnit API +excerpt: API reference documentation for QUnit. +amethyst: + prepend_description_heading: false +redirect_from: + - "/category/all/" +--- + +If you're new to QUnit, check out [Getting Started](../intro.md)! + +QUnit is a powerful, easy-to-use JavaScript unit test suite. QUnit has no dependencies and supports Node.js, SpiderMonkey, and all [major browsers](../intro.md#browser-support). + + + +* [Main methods](./QUnit/) +* [Assertions](./assert/) +* [Callback events](./callbacks/) +* [Configuration options](./config/) +* [Extension interface](./extension/) diff --git a/api/removed.md b/api/removed.md new file mode 100644 index 0000000..9dafa67 --- /dev/null +++ b/api/removed.md @@ -0,0 +1,7 @@ +--- +layout: group +group: removed +title: Removed methods +redirect_from: + - "/removed/" +--- diff --git a/brand.md b/brand.md index 49adde1..8717253 100644 --- a/brand.md +++ b/brand.md @@ -11,19 +11,23 @@ amethyst: * QUnit Secondary Purple `#390F39`

+ **QUnit Primary Purple**
HEX: #9C3493
CMYK: 0 67 6 39
RGB: 156 52 147
Pantone: 253 +

+ **QUnit Secondary Purple**
HEX: #390F39
CMYK: 0 74 0 78
RGB: 57 15 57
Pantone: 229 +

## Logo diff --git a/cli.md b/cli.md index 4124f35..1cd273f 100644 --- a/cli.md +++ b/cli.md @@ -7,7 +7,11 @@ amethyst: toc: true --- -

How to use the QUnit CLI (command-line interface), after [installing it from npm](./intro.md#in-nodejs).

+

+ +How to use the QUnit CLI (command-line interface), after [installing it from npm](./intro.md#in-nodejs). + +

## QUnit CLI options @@ -34,7 +38,7 @@ Only run tests that match the given filter. The filter is matched against the mo Examples: `--filter foo`, `--filter !foo`, `--filter "/foo/"`, `--filter "!/foo/"` -Check [`QUnit.config.filter`](https://api.qunitjs.com/config/filter/) for more information. +Check [`QUnit.config.filter`](./api/config/filter.md) for more information. ### `--module` @@ -42,7 +46,7 @@ Only run tests that belong to the specified module. The name is matched case-ins Examples: `--module foo`, `--module "Foo"` -Check [`QUnit.config.module`](https://api.qunitjs.com/config/module/) for more information. +Check [`QUnit.config.module`](./api/config/module.md) for more information. ### `--reporter` @@ -53,7 +57,7 @@ Run `qunit --reporter ` to use a different reporter, where `` can be Built-in reporters: * `tap`: [TAP compliant](https://testanything.org/) reporter. -* `console`: Log the JSON object for each reporter event from [`QUnit.on`](https://api.qunitjs.com/callbacks/QUnit.on/). Use this to explore or debug the reporter interface. +* `console`: Log the JSON object for each reporter event from [`QUnit.on`](./api/callbacks/QUnit.on.md). Use this to explore or debug the reporter interface. ### `--require` @@ -75,11 +79,11 @@ QUnit.config.notrycatch = true; global.MyApp = require( './index' ); ``` -See [QUnit.config](https://api.qunitjs.com/config/QUnit.config/) for all available configuration options. +See [QUnit.config](./api/config/index.md) for all available configuration options. ### `--seed` -This option assigns [`QUnit.config.seed`](https://api.qunitjs.com/config/seed/) for you. +This option assigns [`QUnit.config.seed`](./api/config/seed.md) for you. ## Node.js CLI options diff --git a/docsearch.config.json b/docsearch.config.json index 5ec3f82..886d733 100644 --- a/docsearch.config.json +++ b/docsearch.config.json @@ -1,8 +1,7 @@ { "index_name": "qunitjs_com", "start_urls": [ - "https://qunitjs.com", - "https://api.qunitjs.com" + "https://qunitjs.com" ], "stop_content": [ "Ie)HCF*5Sx(?1GQs@VjLJ5&Mh zPW9KQ%J`$PdXuVs*2LB>s(E<6;Uw8|CG1YGP4jmC=~(MRolV0yzt_cv2Ac=>#p_|zy@buNVCe6}nUed<{LQd!AMtNs;23(K3;Cyn2Ft1`y znb=APwO_VF`osg=l7F-fArMj;#*7`EOP$ob`-O9=l<$KaJ`t>K86Q+e>ctI# zgx>#tzwl-^d8YQJi1pP}@_Hys7SglWv#)0Q*NJsU?X?7B;4LoED^ykddDsoBGP8(bSt6x`|3OeJmaVE z)Y^Gd#Wsw&RTtxO3r{$VfJ_p<>>4H~(MEO-#_>;i)$S5=nf~ zsDF|&NY(5oBs$CUJtu^=~KG=Zb zqe%=}`)2XL@3kA}+Kl+_Y-JF;cnYwSCd2j@?F|QaeX5LJZHe>?LGyXWU3WxVQKHI{ zjz8a0K_HqvIw`-lr7LHSO!e|4hGOQZDP^61w<(S^C{FQr<3f-&Hnp{B&1&9sB=stk z@68i7#`^M*y|L~0#xrptO+r@WDf{mk-xuROqD}3jRI0sQ>SiZ`K!)r!T)TFeGlrbg z16~YWWmu5ko*N6!RlcXhFZT$w^AI<}jJJ%d0BGbbx89N&zFxM`f%byIYJdtK2_ zZPZJQ8ABijPAn&1y}ZBn#rjN4RD9)%tX-Y9cl6J_l)oDco{+T2n*0jUWuh_?U|A+*yo5uae z-~Ih!pZR8eeNFmQV>v7cq^v5Z#$f5_OJh|j5^Wp`eo@Ukwm$D&zEQ`^0xJ%Ti4G~e zfOFU=Fp#hPXV5+Cnp6Y{1k#b-rBkhPmfN${;XpjVR)vWH@ugkO?s+3J@hdj_D-UbN z#Izp-f+U3{p;VU3TyUdvm839sRwC!ZqE^C zha)GiZW8E^Xm>Lj)I^7!vpzDW2mPaksK*^1yb({CC&XyW2xBi>Tg#J5kNLFUC>SEp z8N0F?;bQUQF0QkG-i?#*IgL9<|F}%O^9-5QW~Qc4TQXr&3?CHZ4SI1x=;U1B;k7J> z+nR}Ed%_bP((E;l;m^&FTGxC$oa|yIkAL=WBr7_thMOM6QA5@-nTSKX^5dn>IK5&T zzen!JQRemW{N)iAR!R<>%)jzYz4Yi)9a|tw3|eTXLvoS@`DYndn=ioS%?EPhRJ7kd zMbWw}%;O|`qN(6soAlv#TBhwGSs$VN)nOS5536lb+$|sOe)oJ?X0&?T%GzeMJU8~T z2SGDDrOV{SG#v%W@-?Gpjn&x5>}sq@#4X`ij4*V4nNLy7p)Q`0sq*yb(*9{xb%3;c zV09)26^%4K>te55_Sv_GdD}U#O+mtb8!O{5rN&v=+=`^Mh%0MA9RDuN5Zdd+_U8KN z(a%FP`Lv2czL(GaowHdU=a+ntOJ1^|KV_>XLm7y#Drekyo%S{+BFt^B#z~14V?zUM zMBni!F(5ETmf9gD%SLbRH`5jIh>#V9u=e%wp+Jk9%-vT)v?~#qO}In@EQ<=L!9fr8 z{zYZpTry=`6s`QrvZ-Mm|JJQF)g9jVc^e~Ynj3_1{v<)nFGXU8&V6c9X!EPym@r@0 zn{E7R^nqR2`LU!U@oh%SJhh$Y9_=c&|6;Lk_v83<(enCWN%Zx}r9GM*@*HgGinGdm zLsXn@f+}fR6qieym{IV#Y!jxr|6nNsN{{t60iWIH4a3ZSsPf<&pBmB?hN#7(Y^MWW zF}d=RuC@QRB;)67;#qF2rp`)~$1U5-U=TQTIc|*hq-LB|axF(dBkkh-O^Bb6mz(d* z`AE)CQ!ZDc@6%-`!K&lYcsT)3e&1+I`He{#VFUiO;BmN%b4`hv66?Vwc- z-GFf71E)$Lnk#k7Ts7pwO*-RakHPYE2o zZgB=*@!-KS2EJpD=2g-_NVBb8JR#Q=4Fr4u3l?J6)ltxR=O{^R&|3dyngqgg7bfsx z0?%Q?-Kv$~!>Ky?U=q<+aC|%|u5p0a;gMx$LIAY}eGqr>sQ4kj!Z74Tv+{})3rW%0 zrhAvp+oohJF4jkEv8yvbeE|jRG-OV``HZgi{>I8Qe^bV=pa}&34orXmyz`=KM4=G2 z7_exSJ?DceBS;+)t}#uq*XdG>R#Gb*lgs9aQe0AQ+>!tv3+4bbxlj3O?_3p41&L9K z$yQ>(=EEiHFd0vFsX4b5`vb?EuM>EtfQ>1)N@$=dCrNj<+Rkq6@)Sk-0>S{9o=yT+8> zT)f%KOxc@0<()TikfeViN2DR6J=ty|i!-y?VT2K??bpJac}Hq%=`OfJhpIxf;TIRI z6VdDi!}4yNo>FjhGti6dCX>qvH6q2rIs~!2A*(fH< zk-&2VdwphSqJ6L|w$is#Yf6|rG4dA<<06DPkgUw0L;8vqBYv|9OTa-8S%`i5^h%ov zgv#oB^`E120rMN-h1&teWWIC<>)| zGN6QJ*C4wqwLTno@Ab`~Tw-JX2X!Bl`s|cgvZ8O@aNw*R(kC=l3U=QYj(cmAdw;`d`Te&3c|XPwtHrZKihPmWor@z3@v?ds`p$tN|ZQ)huKsDyKYm z&UrLboA9$?JqteR}Km5B7dyq65y7zOO>x)Ugi(!SL4~ZwzoI{ z%X+bWa15@$`>?WqU*r|Nbk|SEAH&aAypQ5y$vIfcc6Q$i>w+ylKx~e7!aYr*I8_&3Ptweoo z=-a?}2UY!9$ClIK*sp07{CD$KjZfVSrawuAe8Ke#HNm^}`i3k2u%*7bFyqhsLSt;@WeNf#8+t`o+xh%{fhZx3{%foG_IP{dtVmQ5;Lq>k;ao?1R4$Nnlx> zlfwvWcYq1_sKHQJGxb+jB6;PbNq8%yQXDf^pA*+y^xKjY{-gPj_{eIfV9l<*e`I6q z{>HLndzF!FzMBlD|0H&`(Ry=o@=FftRT{02ds^R->a&fBRxSsZQAPt)^%pH7)kdun)27f{}Vy2Q5<(kiU6g(3s|X+Q2B@v)cJeOz@|B{+AR>SVWaB_w3^}}!)pVRo5ozBRQW(Db>|ue^HU&FX8hJ{- zA)uN;8+cjE+6S~1Y6o8`M~04&K3CilKy^qzU)da<$iLNZyY!xY1INXnItX;Os*K{* zZ>Sx~f<$3`NF=QqWk+-$mq<_7!I!=VZ#0x%7~;ogA(Qf5??4yhmqwg%BZLAiM!jzx z^%7mFIbT#jZD1ACI+Tf+xbr7$u{I|t$zs)2#O3ts1&07%7rbRSV}afO>5FOp#nmPp zEGz+QKeA$u)rhpvsD^jWn>@_3udt8FG2`zJQOI`w$_8?_cQ?B?>V+dPF5CpiKt9aj zBA(l1eA%P86p@Z+70(_aFp25AA!y>q)!;~t!qUT1#s+592X7H*3$Ral)}#<$j0A#u zsB0OqMi=N^or?I7jgRWQA%-KFhsb4rk2tbFVM#zpPdEQ2dGETxjKY@>mNEiCI?J#I zx|gv0_aII@aV_r+gpZ9ZNFv#*z#jpX?cmDg{6MSlhMrm~A?6L^&5sli@P5@h#v$&= z;@rbPGOLlxg7i7qN)Fs}w5~oSgUOY_HzCvz3kprhMMej839H-X_P6etMcBzen%b{8Uuzs_$!P*|l@YGfUC1S``di^AYBw22QpwwD$fYY0>`H~N#CZ!ZBK z!{p^xRc8Z=s8(>isPl~GvyKky4+loj@cL7?q2I~Plg9copB~XSNo+zB zjH1i?`sxP$!hi1zgp^k?{b3modqhv4G7Z~LCq?7ff1~=m@Q>ZJ*D3d~4|&cH#key_ zAzQ|ouwNYQb5md-PrM}_4|0%YAeQm*DtCN{uQ}K-T4!~eYw|BrR}aT`H> zm`eCxJ%&NSV&dnDE7%dsasz!{=eW#vq0~E$BzY{SMGt%PAF(S4?x(53YzJHak8)H^ za$V3)eGqcfLW^5=rb)QC>zkOaC|E(ztzz=^2VaTZ3qu+jEe(6&BmOynO7;QZ|0rd9 z6=N_xNEJ+gqhrGCf`;$P5QUMKdp<0LKln!9F4WF$NvEHS89gxx8oT9<06DCGGCm3 z2L3`}0p>DD9<%<}t=_gRlB4RCoW1q*MQ4)yqO!C|QM zX+*BlrtMw864!*8K1X83h@%YTmE0S8t%GH8Oi6y>U{nRiIEw z2YOlvh>MCa?O9bk;JTe5i2<`M>Mz`&X3*?gCXx{|6KK$KHoyVcGBuhy!+@!8M>*Sb z&9*QtYxz&z8tvnIS#281EXy92vBt_7uu7hc;fdfUVj0fLc+PaO9-^NC4=E-QQ0krb zOD~=>Ui#%kuh?=sireaGWU3=L<`M6=WASsP=Oz4lFW=3crdFz|TP(7oUwb!%yuJFy zy*xLSSZc|f_^h(D?A}ESXV*9}(BV924{V#9ffv%OLSJEg-sL<=v1SJdizQR1>D|7?s!6@W#$D}B?7z^_BZ`=^?T+D9nLwK zc8XDvTlgpVc-2Z7vTF3j3X~uugv~QN#RF<^esEZ>D^jjGeTINzyIzM;s!10lNEu#F zM`2XkT6C+?zZF4YMaFM4MIK*SbB!r>pWxI|Xvn?&TdDnb{l$P|na=nxQi(*U9DgSc zvp|rRChe`BhWG2LLY|5JW+(dMH5SAXX=nE-fLVML?6WuRXbi7qG)=pxQ;*V$ma@2; z7{c*9&nWUk&$El%?6uIdT?zu3{E}HJnL`R(8bAD`ov+>+CKkAVtfNVAg4iw&XMkP? zB&|NIOr|{N2M^ydm0?!5x_qWtNz_`fBZ%{XD%q_kvLZyF@FQckVS}Z=B35eTDwtnV z9^x~*PEsLWhdvX?Sc3F9a*N8T(7XB)YIinVXEw4Rf3v2HtR*2$pmP}(_j5<&( z<5u^s&*yO{#l~bH|CZgs`t!x>n0&0ok<{~A>4w^fwrn+-*Az=&m^wpN_FAeH5O)X> zLKOS{?hw29?nO7}wNL8@Ykt&Kqgjy#nbqKKG+h?@l?)bbEWa3$c3cGaP+O zZ6d6?Flopz791hX(e#tMaTe^Z4NmcUJfuRO(ecbn1oZM0edZ?`^ceQ%i;ihNk_Dx~ zK1Kaj+0aN+js{Zr!=+Q!fn0@dJYLTQb>P&I6nO_|hq)^`-K`#7VXlq#?|FK#YiXiG z^<9K4$Ut+5F#vE;H5Z#p8Ki|ApCf%G|49eLf_I^@SGy_X4senz_DH9DJ)d=+7}cBW@6XXViT)p% zh8>emDk5b1ahUtf!!`;tD2N~4%n>H-P&#mf!->mD9*GWi?Q5J@F`27_lJMALdBfi@ z#gEDqOOXPC#3$huU=lLV>Y zs__C9I}Piu1+A5^&qo-%HxY?bi`EOF+aY@O_7%2{o2|X;+8T~qnpF(>I!J|%P$`s z=H@UKTBl3v{dmb4BUKd0*LA2@k~ZZGOnz9$te9GpSof^RzxdbV#=fx%!b_|s7 zQ5d~yq`$6TP`{&DiA$c%;)slz1$E=ygY*)8&TT%Q@@Qx6pDL#M9JpBZX*BO@DM+iJ zURk!|Kl|*nIf%=}uvJ{U9n7zLs$#R>n95O@;M9CA63P7A!05-D2mf`6w>b@rh1khF z&b=lJLs~bOUeOQ#Bp!v8QMK8pp^9s2Y{BSwMq_duo6Gl6^;z)E86LI_{vJfMK;f@3 zhQA7{Eb|9Rn7|)6LptbOoUb=d|AD`Fxe`r*W2(PL@<>l>pkj=wa{Jcl^KXg%!6Pr` zxotqqM=-<|s`6&bxtn@lb+ZxhU%ZS`2IH)=Nq1cl%z?;OIP`u$XDE*5-cKu8RAd3dxk74I1UnsS0&UzQMj?O zg|)jWUI#*`PY1)~6RjWAjqe*^MeC&$prH&1TYE>ciuc}LiNzT8`~7@C~gU6RPyWS=fk}-;t*ut)n5LqbSB9!R&8i?Z(I8sDi0oWP#wWfNZG|s+?W>dJu`oXG zfcTglJoMU>)%z5I*`~GjMHXb_8K75#!t$l27ViIBAa};uswjy=Z6Kqqa*wx7x=o?c zzmu5*FKc1t3#hYA|15R$O`pS~E`q}EoSET-B)Sf0ZGAm{vZNq)g8!*kG>yi5hq3~K zOaHc$qx0upqiybF>aLn?%iB&#tHqF^GNzB2iTZgU2U_7tEZVR%dr9r(|I`&VRFON* zk*oXn-~lHWeG;>YU!*(PI(66SumEUp59QPu_*`O(Y#m?4?2n%;i8>v2eiXc(40}TT zK0i1OebM?Xc3LMcGcE_mdNd5CXj%j2s1aO^-Xrk(YUnP>mlHW0(*0fn>g>(U=%sycXb zI`l>@MwRvFIv*>H((R`x(K5Ap)se*B+9GV2d3*XeG;eQ^A+O z?Ci%5rhpxA4Bwvdj`*A}Plxsg??tZ}rn}5;)HKqH_}1k~2r_Sb8ZvMKw} zeEdF-vwC7FN8OOtLA1HQWvIVy?waIRhKfQ;xpOEr`a#B*J~(L0Hf2;6d$j3)(MpPq z(*#54q|l61O5 zg2sV74=0Rx@7@;4vyM??XvNOBt*zGiHj;Ds>F+rDaGAmru2CV_pkrOA(hW%_aVgmp;!5KI|rdHJd`V^sf!u8cqe$^}ZH}!h3$#)oehESyY*P`kEyxwD{RRj#E0i$rx(dqw;nuFxe4DM@;XHY)ePYF^ zMiFE<=Ys|<-AF#s=rE)t`i1k4Cm$;K_I6jq`9ecM-(Z|JO@z)Zx#M|Yt*^7WmY*00 zE!DuI?iI44)b4$H(fXzr94TZBWBW!}hDy$Tbh)d^<3*HzSzMnIYupXe{T7w7mJZ?` zu}i&NM$RtbIrg~5l%oK!tK;i-oq+Lv0wk~5Y|!r6tU0HDNVtL|=>BY_f});9z>0W9=#QQX<*K>(}Cjo4ch4CCsoBsv`NEWYT?Z zH~;L{9WWIiTAUmEKE?e7RI;1=g=vbQ;U^KQyuiXqiIaRkc>B`l8hP97&4lQx)NP6L zkP!ytTOekiS+w$#PX=-6(L14kpBD#K3^k-~rw7|xiW6fR4Y@-XvS}|{#c0~uCBMwN zli{Qzug4bjAEXr#)%h~n|BieP|F~I<>kb+p-`Bpk{Dp0;(j0|&A ze+wI+UZl9oq@c$@Snmn02&Q;oUsdl-Rdi)zWvIKoyy;}j7*>?d;(cn|Wr3&1l>?!v z7x0m1=Y%=Wpal<-)zg%v-1w)j`bxCC-yAueoxCI2XC~_)SNOxa-oHw7el^pWe*RVY zyP6M%B+VgFzn!un5x1)JJADIS8K;mC`wjZ+-PC}JZ$Ma3mwr%)+HK!Zo0 zyDJ~{2hYLDz4cb~D)w3Pk7ml3WgTDD@P6DTto=tTJ@jv1&Z^lRgDgl%`l4mntu$Fl z45a{%daT=d+trB%4eSA=p_z*ap2r=5*o9)^WbY!C8QxxixA?2dJCQuf9i6ER&-$%y zNyq4%o|Rg1&Tq5M62vqj0n_MXn0q7@)0pAw@X*v=2HLrOK1%pshdt~dS`8jkS%^y(Ma!jo4!W8m%kB5<2b0P;kdBMzOJyxHy#M74>}`*p0y zn_X(j4$+Yil@XyCqt8|guUP^1P>?Ik`rOHwE4|W08f!N50YOCnrwF4{P&k5_=f(*E z1}P4VF&NBc&;Jn;{ev{I+m+wq&Hhio$^QQkxR-v1j)4H~dBzSKY=HyV+;S!vX!_s6 z>y%0W#MLA57IPqo2(YW8l@2{_a}bD#u)9U?oS{(!A(t zG*bl3Nsp=C%kz4_Pq~J-Jl+%xA0Gv9K%Q*YR~KR8)BM6`TL-X3K>z(Y;U%Vy0Kh(% z?E!LqyqDo8$=Lgv2TP5NjN9ShAWDx6aN*77xl;X0YM$C|mAJPx66RdDUla#Mc&)|# zHfQ%-u=SpoYz9za^#JyF=>q^7Sqg6Z`Md>AH~wQ86PDqrsyOG@ND_w+0Gfr?I%`|v z37h%~Zmj1+%{fIa`jFf{u@k%JYT(}Kj{*{Xylrx$ zIrPeq6{wx*my(T2n*Z_LU!t9I-rnui%z`Jm*Z*Vp8B&tX`SYy^gnza+9!dLDWnMU> z9{O3-QPPA3FiH;VE^su;oVWsHs_b>taUh+g_hm@Cr)~w33;<`(AKGSjFa0YV;s*9k z744ICH9D?qQ%g_AoGfxTTUdc z=SnVA-st7MlSY&&dtha(hadU_1hm(i*)BwI$CITXut~`J^CRnqqmHJ%fCTRaliWQe zpwHR@o6>9P?XNHhy{PlD%Dudzk^NRd8b5`}p$oJYfK2E0>Dk>Qss8B+K*R>l`+(Kd z#{zbBDqAA)1A>vEZ?gJIPPJPp=NK{yT^qn2oTJf`EpMk6dU*`R2)RUQ`Y-NGUiH^@ z?FE3Btsr)hlwBa)yOc+tf8K0Qv0d-*>a<+Hs+;2~Ch5ouIXvq;A)7n;-lzAEU$lV^ z0$DEsHh0#ZC~P~rb-E&yDAoRdD}T};mfbNI3OA)M0_{oy`%wPx$iyCO*ApmrKVt~0002gdT=c>0DxfpR*oSfydoS& z)8mcrOKt~SO!Kt$vrpwXB7+f1)Mcd>>RD%<8rFKqZP)ZCrRV)Uqo2fw^wC1XJV#l2 zZa-nxuN|vZm2=xBB5(}sJ~3H3zUESG>1J^8EkK?Qi<#HXA(8j?T~ECLz4Sa6Sqz)s zvS*bWaQizEzFDTQimbCf5KB3k*>}MmnT+FQSHI`MkkuQM)sTGG1AKyUFri3v5r|@Z zM{}U!1Qa;}PP~a5qcfFCwmKu#QrYDaAK~$OhxOHqg}i)*7U4J67GaOv%;S0>5m^_ z57sKql}+T`#(HxkZhYUs z{(b}s%OUk0mY0T~GJL=e(X0Zh4S9HjtIJ&>g9VY{g(%ifL~XAHJ}P_YTOKYoleg8I z=IXCh(;8BlX+>Moed4uF{mR=mJQ1PG;K3V?#TnpEsS-D4Sq`nF?Tm_PyP{9q=3t(R z0m4rn6lc&~U!(~SMuk_ysalY9-+(qB#3~r~=kiw~G>yN6Y-xLXMyt?pUr`S%aI7qeqNZsj!?b*%3W$k$I-Utg(KPOqRg8=@KSgxAQ4RDb#nxVB1X zy-V9x&G*rw6ibbV8lq(z-gcJC${8&dQ6D_t>@HTime6eVID26b~L52V%~6J z06P=bIkk~iW`e^-A~J>36R?~;MG$SEhMM`%PKbKf5S(wAy{hgh24+E8=>psaO2f~!HpVfZ~|M+EVyl{OoF7~ zQ4#M+nFgdL7jD}k@r#zdI?Cr*k8c=i=xEw^8Hln*^I!mG!jy?#RQZO*II4HrWLRc& zC)SrNk$`%e7!iVmjDK)j?HDM&1D8;O^XNE<7KiA8FmlvU;lN5Oz{p|Dmy%_lnDEn= z594RvKo$IC?bKhx`6?tE%V;DU1CM-L1sYS20(?up=)d^*X`5h&u6&4J7Cv#n8r3@b zqx(}ci{oN~eefj6LdsjISqdgVn$5uL*?i|&nQX(05-6`mG#tsj7UoTxo zb>gLhqxXyMhhBU*b!(tr8Y*H00Sn3*$c%Yn0#PZg0!+!5QpE9)!Q`QpGKP(Tk8bCZ zPdYA{#(g<_fR)C8&lJ~9*hi4KJlG}L#y4HziU+S$)dl?d(@QDD);@8QKPL+-zN1~9 z##+Z5YzG7NN{~4WXvo6VI$Xhr9kt0Nin*c^D{8vnL*% z_^1BVN&nRU76-6Gg0s@w(+l;Z*hAXS-r@i+I&_`7Q_;(m5P$YKKTG|HQbv$V#5Cs> zjRRU#{p=n684Oza`{AxlHgMu7iDmZcgY3y1ED$9~7Dgdlj8^Z5?7sK>RF)Wd(*aHxVXerVsYcd8x^ST1G>dijIYntwT7L}(L>278+C@NEX3^5_Z=T zqFSCkV_bb7ZA88R-ssj?Z9`5N%~a`;KS?@jc>Rln@HXmB>fTVo zMODj}*Mc7cAV)Y-Tp+z3RW4oU8{yBoRccy_b##*y(_8!}ZC?UWqJTS@MJj%Td_(c? zcW>jF3n9G%U8op=%8wFsmW3DAt~v0c&-~m-qu4Q5oMsUTJMkxl)gO_Q{D6S0 z(@9A|augLVpxRi;lZ_iJeQU#IR!^m(V+~~@&>1~ro6fqgrO?iQ2PsoM{J2!;Ikg3e z!NE~07ls>vWBkQ)Mp4Edep*A29~r)t!FouJ5PR;g{g+?sy#a`}Nxk+TMEY~0U!e74 z((SK_zv;^;y3Ge6!33-R51HQ{B|M0nUlipfIP4JMH6D=hiKcP z9;nwKrgkIhk+s^*`7D|9u|tkGu#yt6gXP7^CO{rrsh5k!*B&ctl5HP3zpezeb=-zM zfg^>ifyIqxr9RHy{Na7;8UJ{fp z3R5SAlNQgIfLQFK?c3KwuW@PYcuYUmo8_c@$sY$R2X3-9ave8oyk}q%>gM|VP*#T0 zRw&_ZL_tht1S@S%UYF6pCxYZU?~!Ij9rG>bo|A19t+Y0L_U zlAKa0ajj4~Wt+(pNn0OdLgSFAY<=1{(L&=tVLNn@_t5jb5Q5{DTP!H7>n`%aMSIb_ zLAt^H*>`=h;BXIRYue5J-wzJQwCv zqqz`8`~xn$ai~0%jb5ez5m{?=JqT5Gk6cxdC|aFj)n>~~?t4+yvr6E3nZk5M10AL~ z0dKg1IloIC$!G}$vX#hLyu8mU5EQ@Kv~eKOgq-9B9Ehqxzyu9I=pNYrF%*B9(-)~! z+Y--kpyI`PpoIWFT)Pv^`tnZ1KKi zvDpQmxanO#OpjDtj(2=RdtCsLF6vsA`etq?dK{z(zZbhc->^UT18H9g8GNHrz8n{f z;{Is?XqE_ZGu0ra^gU|p6qT4Wbq7`w0d~gxaVPTWksbsc`}~QkoRG3W2&hB=6(hPu0Y(Ha4dSpN7f*rEo1ituy?ZoevE=*XjdhXsawim!bZmbL3Xl*Wa5 zAo%67WCm8Q?`Z_;CEA=RsMJIA;pFDF;(I%Jk5;^JETfLg*&5AOkffSeBMvD~^&XhZf8B&rN`+K@!_~+5%#SVLSjcZaCPZiU7EIwu!!i?9 z4rBiV#ebEDK~~sN@E%Pp;}Yc$a%}aF0<5=$9%r0JtqWxEt48_2tySkJ(-nELR=WoZ zrR~V~E=q7WSZyhNXZ6tGeV*ncDzNm5CeCVg?J!!tz?(In}`&++ke&6o_c! zYd|7q9w@DpA#+bTIPM69(DoW;w3p8R=T=^V+Q&;~#GFp?VUZ~|6*Khb5<=5P9-Jl0 zSr(>-xD-m%e2vqJd|>d849bO`Lvvz!eOU?jdt!52_-B4biYz?qUIgz!r)kDn0h#Kx zw0eZDxvMTm`KTO8uwGr81?=#AS&L_{WM26qBQ@TrIo?N&-xMK_u!e&MVbFuww>3Ow z+!G?QhYv982izV&4{Cr+yiaX9FSw6_W;cSSV~Knvq){UDqO=%g z250P`$+xH711~BI0Db0~B}Z|QtNXSsiOGk1;A(=%p`!VE2*Dpx#N7QxIOiGbMYW*x zic}21mS8W=-VUz(j;cd0qt6k9f5))W`{#gW54=Gx;m44wbx+Q=rH#%E@@q0It0QmgrIWs~jwXdWDD>gN7vwRI_oI;miD_w+vBD4o_^JO<< z3qghJ=M>4gf2{UCB4tPc^04M?zF?bEC@5a;t;1Q3D?cLF5LDh?G@}Yi9f|T$u{5$U zJ$q{PB?~K(4ar2x9A~xqnOnLrjo0uvJ#7g+S`csX=>6Gv+oJ$0KDfVD#J<0Ml8>L4 z-o>5jrW1SVTVnSt3)NEdj6j$N4r{h~HvpN5uALAmwQy|p)8<5$ z-$e1u<~+>=B{K=JmTJ*p_uPd{Ix+9SEtR!*? z&4nT1__HcV^SGqf z12@Afn(VcF?F4xruV;V%%Np?7hxcgq8E!HeLbI2a!z~kb*s4(ywshQ{*jG|`d3O4f zqODQL6!Csj>izAZKEC`BH{YUV9}y|6_UQA~wZLtp-&AC%>ZG6H2C$Vq{d#g-MTLpl zEN;MGec4f1FXBX9mC(@gCKh}Z<|`!WLZkdtQwjbctYKlJ{q2ShaA@sD++cUiClFOY zcs+;{2Nf!l=6}r)rW(KV!MI%>EYv9;Z{Gn|X8*O0)Zs6hi39NbTavvk)S(}SehfS& zX55??lqW+;gKL+ellyavz-u$T(7PPaMdl)ywH^!Uxg~GxXvdDo!Fk8KzLG3eRrdzA z;4uB-3T7tFa?0D5aayxLEx%`-=k>G#V|U<#{x?0F%T=xO>)+Ci$L|k@Bh1rD*F&pHT8TB#dYKxqwy%4D(lpy{7O|t9El(U znTsqMIAIS$?Zy4&K=Xhq?cxg6EcNCn)q``psqDLOiL^Jp= z@IuEKiI~7p*BF(uE&4xv?T~$tU0gQ(+I9~&db;>2Uv~f8&|5`V9Eqog)Hvf`RB1bP zyW+uhzJ#W8(l53#=f_Xg;f!ELvt!KT}>Qzbn+`C?6${Lk_t~$OD|=d z@#YH0pV%x(cIogZSTFm$WY!e*+Ec=|jMnsb|Givx52Y90bh*~1!3G_H@?qf-yS(4x z6HV*}Qn;Vv`wkx56r{ehK1_X8M?L-^QF`8WN4M6Bf7;E?m$!B3Flh839F)p#Du540 z_MzVw*)&J)Ke0DX&qOxhS4>MCE4zSaCO+tr+OWsY1XW_bjrAonop0-CWMx+iOdUBe zTc%7;jg$&8?5Dh~A;ViLUH9~qJt-HtO&<0Y)e&p-4`H|0Z^s{xx0`8=adaEK8a3gV zTDGR1v6(GPp(?M%im3u%nBI~j(Lve{(H$;1p{xlJ$dtR$K-%X3x&LCiJKc4DRy4-C zH|m--9AgymAGbj&AC9NX-(`TpTN!zL*8Z*h6ay-i2XzYM1}?6!FZ&MLwMcwGHc&sc zmYQ}02YI18w`Q(twmz!T)bGtT&t`F9)v{&3+eZ*6)di`=`>&C1{|&MM1=_rrX2>u# zU*}p#&p&@T?6Cfu_fZ^ar7FPOaYAAJ6J>gRHNnf%rq;PfO`AM2p5%-FZlBA1I7E(T zY&eQ}PdFy~j6zQ%PB`_2rna7TkI!$(u;crqAu-uq&8)JrTie@?+!z)Uk*Bf&`MuQ& z?WoY#yonPkgS1JBy5+FunXrYRP>#gn{fjQ!n3t&V>_$raHC3Be?ayN_!1?D}dgSfZ z_rK|l>fD~n{oTdU>Uz17wkpy0-J>sWTTkUC^}QyiD#NvIp0v>GsoO-9z?FB%7WW1G z-2n)t46U)pxJfAx5^;@_J)B!TNctiHt3qZTI8-f6zrLo7xdgsWYU0ugVDeER`?nku zhAP8uojJ^weQo_2^0vcTzHRGN3c}q%yg0(^?=FHL6fkXKhc0p_-jc#WTlcyk&r<=0i*8gXOR)TJ?L@M$2xOBzk#Ja{<1boFd$;?HO~dERQvsi4HiKbANDH3GIsDyx^=GGSiNL)| zz^d_aUp5T;I}DV%k3WKykYnGcv!@BaBhD{)H3`nYh(AhrM(ObG`M8&?Y*980{w*VtBVsQnVR$o@Krz7=8df2(yA`TTU#$(D0@le#=!T#qtg~hWt5@{wg zO!rqsFNTk=gPSbW;{I5~M~uG-pXUz$_$AM7t~s~t>mc){tG@zPn6IqP+B?2q`%{J9P}%#r%Y&D+gK^cOgruseN# zSHlf0CB~z$ij;rEW;mhpFoTN9v090H|3Q~|GHo;X9WzvEhHu;w&)3|IYmBy$|5#rK zq8RfcpW=JqS8Qf_PtXE2U-no@^_;x%UPG^#Dw#Z{oV43$e3oJ?2r^d7Sl`kOtUP0{ zZbllZ!39hhfGe9HQ9)y7EkR^v0*}&ev&3M_n*5RJzBC&buVuGqXza`pgcO$P;dlHz zspNzg>p@USJlE6wYrcf+_ea3`5fEgIup{K(hHCoA|8eKy;|-fPNnu@)ra)2qO^5>5pJa*`iq#+jp#|5`+U<7pnib?WT>?-BpFmBAAM(87m$yo*5XRnN zvV?Vc?7$WES`M}RObPG}ni0xTq#J?sr}G(!e9ReY_NoM|cSX{v2m~om-%k_QZXqeQ zO%5X)iU7M4E-wJcpGM_ogHzfkPoV@Xjxw?zzH4@$ zH|oRuI%RYoD6++(=j46<0tmby`La4w&=YGIXZ^~iE=IEsr22@IT$)^UHWop~j;1pm zTORT~a?o;NC+=9uod7G4Lr*n0Yr9{MDsE_Smap5q97vjcm}bQ&HkQFJ`Ko#)Ly?ZQ z1f4P1w4viH(ebMp#(d#q zb~QfVZz4ar3ilnc55D8}7aa#doJOD4^GScPEjDt4KUP@S)pDMM@gurT5HT;YC0VH| zDYG>Gw#d5O{%~A_#q&_8Gx(c^y*kTI{I}l*c%?550qiY#8f#nx2zI0v%kAx zlP&REbHx6yVn+11pEu@%T@VzA{TTHS9-k$3=C;)=)Nv(gzL`OvG>T3Mt^0{kg6kq4 z`yoMbmB^jcpAmO#LD{r4m0Y4c5@foN)IbUP=*IqKu&;n;F&8mH%^K@4NMNAO+-EL@ zYlu|{)(`@MD&oTY?s0+Oe_IRU6y9_XXVYzTCXBV!s=>BEql>ZOT9sR$@xZgv(B$7J zQd4&IEgG^0S8S`IlhGM}!Y2x!X`N7DhVqD_nvb?MS0O*WjcX@UB02X-Lff6`9#P0p zxjWfWk*=6P1-ZXL40rWp%FieNNE6nnQqvA3TY{FHYXT`DB(&;6l*Lbag#=iWsS--G z`}Aj8E6`iH9s1ljqWoiU6xD5MF(#BilU6qU;Va5GJYHcd<)i3$kpU1)f4_`)xEgMd zPCZ}Gt53Q=#iD-Ai3X}z2Q9DXoY3j2rJmP!((^CJVcGVj3zZnsqi6V79ufqR4OzYV zT8V<}I7_CxnDm7n!=41~-w##4rh@{ZMBtP+*mE(+9V+N+mGB%+jsZTy0VQ}}%J+Cy z25}4W_B)>CPP~XF7~hj31t)Vn6zg(L+;_yfu&XQBcSN?3xkA?H5oMGz5bDF-=YX$v zcOk_GN%ht-ZepbRTyX}I$HVplw4XVSG~MYCc<#?c=;wjo>N`SE%tyUXo-Z_!A%+@w zJ2m;_e(+f!yC0@?Zb@rQjvpFPgp4*tkDM5J7E_%`D zzW9I=NA=g%9q*Lxv>zRgA`{YZi@9hHkIs#N6}c2c*5a`Z*at@fL8+BcSHtw1hzUQ* zg0C=@8?JUfOP6=Z!n6wf4+>6lX+c(W79=c_u!SScga@R+?9eleM3Jc!)l=uLwsV*o z;+}{1tw-0Y&FHs00-6eHmdmy|idMmtZGrjrEp!=JGLW!=ndgb0fZ(bZF6)D?GYGoo zE-OmS5p=^1bsi(-9}`*IyVFXad)Y-|I<0+0Hcy&Q`;qATGiUK+i!6_jU7h0byA!4i zgwE+59QPjB<(zB+_-wK7tC}+xQe@l2-=;n}6LXCLF&)jYhD_qMbUd_wRt@V(! z%9}Q3Fsxe&tE<+P)oP-P$vzxm3i%(?!zR$&2Q_EFjXc}y-vHj zJr+5#Eo_b8ufnPa{5-$eC{I78``R;1oN(IDnZxb>LmijL)Y#fN_yR|J zgoQj=UUCYs`OHS4(s?{TDY0OgFo?j-kLfy6rn^X2Y1WbZSgCXNW_TkGT9}}(!71!7 z$wDTX)6D%O@mHsw@Vu8ithU|tV|#UXSxUJ3a_$66CW*%jQ=^g-bCOx4fzD$jY;&E>7JWJ za({~!4v=Q7KO%H326XsCiLKeKd1=j$2shW+ew7G765GBFD2sQOv~XVl&3Nzu4M>Gg zQ_CTmd@0*RFkf>N+8CkyyjeNYTk7T6Oxvrjug>}ZYN;H!8IUM2 zu<%h5?s4N}KJuyLy^@wMA%+^Ozp8wEFQ4fw54V)%{D9-&uNHO83V`LO-D9_`EGG%e&a)y87jQCQuIY0Z z?_oZ{VN+kyo_~vedEpQeAb$Iq25{%waxmj1)CrCO)G>$v5*Ptm6#u{SkpR*N0R)oT VKrds>=(_*3^|Xz&YBU~4{|_QGPW=D? literal 0 HcmV?d00001 diff --git a/img/logo-with-colored-dark-text.svg b/img/logo-with-colored-dark-text.svg new file mode 100644 index 0000000..eb7ae66 --- /dev/null +++ b/img/logo-with-colored-dark-text.svg @@ -0,0 +1 @@ + diff --git a/index.md b/index.md index 19e5f69..afdf81a 100644 --- a/index.md +++ b/index.md @@ -6,7 +6,7 @@ home_primary_btn: href: /intro/ home_secondary_btn: name: View the Docs - href: https://api.qunitjs.com + href: /api/ ---
@@ -17,7 +17,7 @@ home_secondary_btn:

Universal

-

Tests can be run anywhere; Node, your browser, even inside a Web Worker. Test your code where it runs.

+

QUnit can run anywhere; web browsers, Node, SpiderMonkey, even in a Web Worker! Test your code where it runs.

@@ -92,8 +92,7 @@ These are the officially supported [release channels](intro.md#release-channels) To contribute: * [Watch the repository](https://github.com/qunitjs/qunit) to learn about release, new requests, or bug reports. -* The source of this website, is in the [qunitjs.com](https://github.com/qunitjs/qunitjs.com) repository. -* The source of the Documentation site, is in the ["docs/" directory](https://github.com/qunitjs/qunit/tree/master/docs). +* The source of this website, is in the ["docs/" directory](https://github.com/qunitjs/qunit/tree/main/docs). --- diff --git a/intro.md b/intro.md index 57d5452..0989fb1 100644 --- a/intro.md +++ b/intro.md @@ -8,7 +8,13 @@ redirect_from: - "/cookbook/" --- -

The following guide will get you up-and-running with QUnit either in Node.js or [in the Browser](#in-the-browser).

+

+ +The following guide will get you up-and-running with QUnit either in Node.js or [in the Browser](#in-the-browser). + +

+ +QUnit has no dependencies and supports Node.js, SpiderMonkey, and all [major browsers](#browser-support). ## In Node.js @@ -43,7 +49,7 @@ QUnit.test('two numbers', assert => { }); ``` -This defines a test suite for the "add" feature, with a single test case that verifies the result of adding two numbers together. Refer to the [`QUnit.test()` page](https://api.qunitjs.com/QUnit/test/) in our API Documentation for how to organise tests and make other assertions. +This defines a test suite for the "add" feature, with a single test case that verifies the result of adding two numbers together. Refer to the [`QUnit.test()` page](./api/QUnit/test.md) in our API Documentation for how to organise tests and make other assertions. You can now run your first test through the [QUnit CLI](./cli.md). It is recommended that you run the `qunit` command via an npm script, which will automatically find the `qunit` program in your local `node_modules` folder, which is where npm keeps the dependencies you download. In your `package.json` file, specify it like so: @@ -75,7 +81,7 @@ ok 1 add > two numbers Congrats! You just wrote and executed your first QUnit test! -Check out the [API documentation](https://api.qunitjs.com) to learn about QUnit APIs for organising tests and making assertions. +Check out the [API documentation](./api/index.md) to learn about QUnit APIs for organising tests and making assertions. See [Command-line interface](./cli.md) for help with the `qunit` command. @@ -139,7 +145,7 @@ If you open this up in the browser you'll find a detailed report of the tests th Congrats! You just wrote and executed your first QUnit test! -Check out the [API documentation](https://api.qunitjs.com) to learn more about the QUnit APIs for organising tests and making assertions. +Check out the [API documentation](./api/index.md) to learn more about the QUnit APIs for organising tests and making assertions. ### Browser support diff --git a/upgrade-guide-2.x.md b/upgrade-guide-2.x.md index 141c6c6..c2b0196 100644 --- a/upgrade-guide-2.x.md +++ b/upgrade-guide-2.x.md @@ -19,21 +19,21 @@ The old methods are removed in QUnit 2.0 and replaced with placeholder methods t | QUnit 1.x | QUnit 2.x |--|-- -| `module()` | [`QUnit.module()`](https://api.qunitjs.com/QUnit/module/) -| `test()` | [`QUnit.test()`](https://api.qunitjs.com/QUnit/test/) -| `asyncTest()` | [`QUnit.test()`](https://api.qunitjs.com/QUnit/test/) with [`assert.async()`](https://api.qunitjs.com/assert/async/) -| `stop()`/`start()` | [`assert.async()`](https://api.qunitjs.com/assert/async/) -| `expect()` | [`assert.expect()`](https://api.qunitjs.com/assert/expect/) -| `ok()`, `equal()`, `deepEqual()`, … | [`assert`](https://api.qunitjs.com/assert/) -| `setup`/`teardown` options | [`beforeEach` and `afterEach` hooks](https://api.qunitjs.com/QUnit/module/#options-object) +| `module()` | [`QUnit.module()`](./api/QUnit/module.md) +| `test()` | [`QUnit.test()`](./api/QUnit/test.md) +| `asyncTest()` | [`QUnit.test()`](./api/QUnit/test.md) with [`assert.async()`](./api/assert/async.md) +| `stop()`/`start()` | [`assert.async()`](./api/assert/async.md) +| `expect()` | [`assert.expect()`](./api/assert/expect.md) +| `ok()`, `equal()`, `deepEqual()`, … | [`assert`](./api/assert/index.md) +| `setup`/`teardown` options | [`beforeEach` and `afterEach` hooks](./api/QUnit/module.md#options-object) For plugins and other integrations: | QUnit 1.x | QUnit 2.x |--|-- -| `QUnit.log = …`
`QUnit.begin = …`
… | [`QUnit.log(…)`](https://api.qunitjs.com/callbacks/QUnit.log/),
[`QUnit.begin(…)`](https://api.qunitjs.com/callbacks/QUnit.begin/),
… -| `QUnit.push()` | [`assert.pushResult()`](https://api.qunitjs.com/assert/pushResult) -| `QUnit.jsDump.parse()` | [`QUnit.dump.parse()`](https://api.qunitjs.com/config/QUnit.dump.parse/) +| `QUnit.log = …`
`QUnit.begin = …`
… | [`QUnit.log(…)`](./api/callbacks/QUnit.log.md),
[`QUnit.begin(…)`](./api/callbacks/QUnit.begin.md),
… +| `QUnit.push()` | [`assert.pushResult()`](./api/assert/pushResult.md) +| `QUnit.jsDump.parse()` | [`QUnit.dump.parse()`](./api/extension/QUnit.dump.parse.md) ## Changes @@ -52,7 +52,7 @@ For plugins and other integrations: ### Removed global functions -QUnit 2 no longer uses global functions. The main methods are now part of the `QUnit` interface, and the assertion methods are exposed through a new [`assert`](https://api.qunitjs.com/assert/) object that is bound to each test. +QUnit 2 no longer uses global functions. The main methods are now part of the `QUnit` interface, and the assertion methods are exposed through a new [`assert`](./api/assert/index.md) object that is bound to each test. Before: @@ -76,9 +76,9 @@ QUnit.test('add', assert => { ### Introducing `assert.async()` -Use [`assert.async()`](https://api.qunitjs.com/assert/async/) instead of the `stop()` and `start()` functions. Calling `assert.async()` returns a `done` function that should be called once the asynchronous operation is finished. +Use [`assert.async()`](./api/assert/async.md) instead of the `stop()` and `start()` functions. Calling `assert.async()` returns a `done` function that should be called once the asynchronous operation is finished. -Similarly, if you were using `asyncTest()`, use the regular [`QUnit.test()`](https://api.qunitjs.com/QUnit.test/) with [`assert.async()`](https://api.qunitjs.com/async/) instead. +Similarly, if you were using `asyncTest()`, use the regular [`QUnit.test()`](./api/QUnit/test.md) with [`assert.async()`](./api/async.md) instead. Before: @@ -115,7 +115,7 @@ QUnit.test('navigates to new page', assert => { ### Renamed module hooks -The [module hooks](https://api.qunitjs.com/QUnit.module/) `setup` and `teardown` have been renamed to `beforeEach` and `afterEach`. The new names were first introduced in QUnit 1.16, and removed in QUnit 2.0. +The [module hooks](./api/QUnit/module.md) `setup` and `teardown` have been renamed to `beforeEach` and `afterEach`. The new names were first introduced in QUnit 1.16, and removed in QUnit 2.0. Before: @@ -143,7 +143,7 @@ QUnit.module('router', { }); ``` -You can also use a [nested scope](https://api.qunitjs.com/QUnit/module/#nested-scope) as of QUnit 1.20, which makes for simpler sharing of variables and associating of tests with modules. +You can also use a [nested scope](./api/QUnit/module.md#nested-scope) as of QUnit 1.20, which makes for simpler sharing of variables and associating of tests with modules. Example: @@ -168,7 +168,11 @@ QUnit.module('router', hooks => { Early alpha releases of QUnit 0.x required property assignments to register callback events. In QUnit 1.0, these were deprecated in favour of more modern event registration methods. The ability to use assignments as way to register callbacks was removed in QUnit 2.0. -

See also [`QUnit.on()`](https://api.qunitjs.com/callbacks/QUnit.on/), which implements the [js-reporters spec](https://github.com/js-reporters/js-reporters) since QUnit 2.2.

+

+ +See also [`QUnit.on()`](./api/callbacks/QUnit.on.md), which implements the [js-reporters spec](https://github.com/js-reporters/js-reporters) since QUnit 2.2. + +

Before: @@ -186,11 +190,11 @@ QUnit.log(function (results) { }); ``` -This applies to all reporting callbacks, specifically: [`begin`](https://api.qunitjs.com/callbacks/QUnit.begin/), [`done`](https://api.qunitjs.com/callbacks/QUnit.done/), [`log`](https://api.qunitjs.com/QUnit.log/), [`moduleDone`](https://api.qunitjs.com/callbacks/QUnit.moduleDone/), [`moduleStart`](https://api.qunitjs.com/callbacks/QUnit.moduleStart/), [`testDone`](https://api.qunitjs.com/callbacks/QUnit.testDone/), and [`testStart`](https://api.qunitjs.com/callbacks/QUnit.testStart/). +This applies to all reporting callbacks, specifically: [`begin`](./api/callbacks/QUnit.begin.md), [`done`](./api/callbacks/QUnit.done.md), [`log`](./api/callbacks/QUnit.log.md), [`moduleDone`](./api/callbacks/QUnit.moduleDone.md), [`moduleStart`](./api/callbacks/QUnit.moduleStart.md), [`testDone`](./api/callbacks/QUnit.testDone.md), and [`testStart`](./api/callbacks/QUnit.testStart.md). ### Replace `QUnit.push()` with `assert.pushResult()` -To implement custom assertions, assign functions to [`QUnit.assert`](https://api.qunitjs.com/config/QUnit.assert), and inside use [`this.pushResult()`](https://api.qunitjs.com/assert/pushResult) instead of `QUnit.push`. This allows assertions to be directly associated with its test context, preventing asynchronous tests from leaking into other tests. +To implement custom assertions, assign functions to [`QUnit.assert`](./api/extension/QUnit.assert.md), and inside use [`this.pushResult()`](./api/assert/pushResult.md) instead of `QUnit.push`. This allows assertions to be directly associated with its test context, preventing asynchronous tests from leaking into other tests. Before: @@ -250,7 +254,7 @@ QUnit.test('currentPage after replaceState', assert => { ### Renamed `QUnit.jsDump` to `QUnit.dump` -Originally `jsDump` was a standalone library imported into QUnit. It has since evolved further within the library. To reflect that, the property was renamed to [`QUnit.dump.parse`](https://api.qunitjs.com/config/QUnit.dump.parse/). This should only affect custom reporter code, not regular testsuites. +Originally `jsDump` was a standalone library imported into QUnit. It has since evolved further within the library. To reflect that, the property was renamed to [`QUnit.dump.parse`](./api/extension/QUnit.dump.parse.md). This should only affect custom reporter code, not regular testsuites. Before: @@ -295,7 +299,7 @@ QUnit.test('addition', assert => { ### Replace `assert.throws(Function, string, message)` signature -The signature of [`assert.throws()`](https://api.qunitjs.com/assert/throws/) that accepted an error string as second parameter has been removed. This avoids ambiguity with the assertion message, as both parameters were optional. +The signature of [`assert.throws()`](./api/assert/throws.md) that accepted an error string as second parameter has been removed. This avoids ambiguity with the assertion message, as both parameters were optional. It is recommended to use a regular expression or error object as the expected value instead. @@ -337,6 +341,6 @@ QUnit.test('add', assert => { }); ``` -See [`assert.throws()`](https://api.qunitjs.com/assert/throws/) for an overview of the supported signatures. +See [`assert.throws()`](./api/assert/throws.md) for an overview of the supported signatures. Note that in the two-argument signature `assert.throws(Function, string)` has always been interpreted as asserting _anything_ is thrown, with the string argument being the assertion message. This continues to be supported.