diff --git a/docs/api/QUnit/module.md b/docs/api/QUnit/module.md
index 041659682..99eac0867 100644
--- a/docs/api/QUnit/module.md
+++ b/docs/api/QUnit/module.md
@@ -28,7 +28,12 @@ All tests inside a module will be grouped under that module. Tests can be added
Modules can be nested inside other modules via a [module scope](#module-scope). In the output, tests are generally prefixed by the names of all parent modules. E.g. "Grandparent > Parent > Child > my test".
-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.
+`QUnit.module.only( name, … )`
+`QUnit.module.todo( name, … )`
+`QUnit.module.skip( name, … )`
+`QUnit.module.if( name, condition, … )`
+
+These methods are aliases for `QUnit.module()` that apply the behaviour of [`QUnit.test.only()`](./test.only.md), [`QUnit.test.todo()`](./test.todo.md), [`QUnit.test.skip()`](./test.skip.md) or [`QUnit.test.if()`](./test.if.md) to all a module's tests at once.
@@ -119,10 +124,11 @@ Example: [§ Hooks via module options](#hooks-via-module-options).
## 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 `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.
+| UNRELEASED | Added `QUnit.module.if()` alias.
+| [QUnit 2.4](https://github.com/qunitjs/qunit/releases/tag/2.4.0) | Added `QUnit.module.only()`, `QUnit.module.skip()`, and `QUnit.module.todo()` aliases.
+| [QUnit 2.0](https://github.com/qunitjs/qunit/releases/tag/2.0.0) | Added `before` and `after` options.
+| [QUnit 1.20](https://github.com/qunitjs/qunit/releases/tag/1.20.0) | Introduce `scope` feature.
+| [QUnit 1.16](https://github.com/qunitjs/qunit/releases/tag/1.16.0) | Added `beforeEach` and `afterEach` options.
The `setup` and `teardown` options were deprecated in QUnit 1.16 and removed in QUnit 2.0.
## Examples
diff --git a/docs/api/QUnit/test.each.md b/docs/api/QUnit/test.each.md
index 07821922b..e342cc057 100644
--- a/docs/api/QUnit/test.each.md
+++ b/docs/api/QUnit/test.each.md
@@ -11,8 +11,9 @@ version_added: "2.16.0"
`QUnit.test.each( name, dataset, callback )`
`QUnit.test.only.each( name, dataset, callback )`
+`QUnit.test.todo.each( name, dataset, callback )`
`QUnit.test.skip.each( name, dataset, callback )`
-`QUnit.test.todo.each( name, dataset, callback )`
+`QUnit.test.if.each( name, condition, dataset, callback )`
Add tests using a data provider.
@@ -35,7 +36,7 @@ Use this method to add multiple tests that are similar, but with different data
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.
+The [`only`](./test.only.md), [`todo`](./test.todo.md), [`skip`](./test.skip.md), and [`if`](./test.if.md) variants are also available, as `QUnit.test.only.each`, `QUnit.test.todo.each`, `QUnit.test.skip.each`, and `QUnit.test.if.each` respectively.
## Examples
diff --git a/docs/api/QUnit/test.if.md b/docs/api/QUnit/test.if.md
new file mode 100644
index 000000000..42cad5ce5
--- /dev/null
+++ b/docs/api/QUnit/test.if.md
@@ -0,0 +1,45 @@
+---
+layout: page-api
+title: QUnit.test.if()
+excerpt: Add a test that may be skipped.
+groups:
+ - main
+redirect_from:
+ - "/QUnit/test.if/"
+version_added: "unreleased"
+---
+
+`QUnit.test.if( name, condition, callback )`
+
+Add a test that only runs if a condition is true.
+
+| parameter | description |
+|-----------|-------------|
+| `name` (string) | Title of unit being tested |
+| `condition` (string) | Expression to decide if the test should be run |
+| `callback` (function) | Function that performs the test |
+
+If the condition is true, this is equivalent to calling [`QUnit.test()`](./test.md).
+
+If the conditional is false, this is equivalent to calling [`QUnit.test.skip()`](./test.skip.md), and test will not run. Instead, it will be listed in the results as a "skipped" test.
+
+As a codebase becomes bigger, you may need to conditionally skip an entire group of tests. You can use [`QUnit.module.if()`](./module.md) to recursively skip all tests in a module based on a given condition.
+
+## Examples
+
+```js
+QUnit.module('MyApp');
+
+// Skip if executed without a DOM
+QUnit.test.if('render', typeof document !== 'undefined', function (assert) {
+ assert.strictEqual(MyApp.render(), '
Hello world!
');
+});
+```
+
+```js
+QUnit.module.if('MyApp', typeof document !== 'undefined');
+
+QUnit.test('render', function (assert) {
+ assert.strictEqual(MyApp.render(), 'Hello world!
');
+});
+```
diff --git a/docs/api/QUnit/test.skip.md b/docs/api/QUnit/test.skip.md
index 61da001ab..a6ef9a1cc 100644
--- a/docs/api/QUnit/test.skip.md
+++ b/docs/api/QUnit/test.skip.md
@@ -27,6 +27,10 @@ This test will be listed in the results as a "skipped" test. The callback and th
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.
+## See also
+
+* [`QUnit.test.if( name, condition, callback )`](./test.if.md)
+
## 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.
diff --git a/src/module.js b/src/module.js
index d197e5f01..e0fc1a22c 100644
--- a/src/module.js
+++ b/src/module.js
@@ -171,6 +171,14 @@ module.skip = function (name, options, scope) {
processModule(name, options, scope, { skip: true });
};
+module.if = function (name, condition, options, scope) {
+ if (focused) {
+ return;
+ }
+
+ processModule(name, options, scope, { skip: !condition });
+};
+
module.todo = function (name, options, scope) {
if (focused) {
return;
diff --git a/src/test.js b/src/test.js
index dce4743e9..1ac006f1a 100644
--- a/src/test.js
+++ b/src/test.js
@@ -105,7 +105,7 @@ export default function Test (settings) {
});
if (this.skip) {
- // Skipped tests will fully ignore any sent callback
+ // Skipped tests will fully ignore (and dereference for garbage collect) any sent callback
this.callback = function () {};
this.async = false;
this.expected = 0;
@@ -970,6 +970,9 @@ extend(test, {
skip: function (testName) {
addTest({ testName, skip: true });
},
+ if: function (testName, condition, callback) {
+ addTest({ testName, callback, skip: !condition });
+ },
only: function (testName, callback) {
addOnlyTest({ testName, callback });
},
@@ -1007,7 +1010,18 @@ test.skip.each = function (testName, dataset) {
});
});
};
-
+test.if.each = function (testName, condition, dataset, callback) {
+ runEach(dataset, (data, testKey) => {
+ addTest({
+ testName: makeEachTestName(testName, testKey),
+ callback,
+ withData: true,
+ stackOffset: 5,
+ skip: !condition,
+ data: condition ? data : undefined
+ });
+ });
+};
test.only.each = function (testName, dataset, callback) {
runEach(dataset, (data, testKey) => {
addOnlyTest({
diff --git a/test/cli/fixtures/test-if.js b/test/cli/fixtures/test-if.js
new file mode 100644
index 000000000..dad46a95a
--- /dev/null
+++ b/test/cli/fixtures/test-if.js
@@ -0,0 +1,37 @@
+QUnit.test.if('skip me', false, function (assert) {
+ assert.true(false);
+});
+
+QUnit.test.if('keep me', true, function (assert) {
+ assert.true(true);
+});
+
+QUnit.test('regular', function (assert) {
+ assert.true(true);
+});
+
+QUnit.test.if.each('skip dataset', false, ['a', 'b'], function (assert, _data) {
+ assert.true(false);
+});
+
+QUnit.test.if.each('keep dataset', true, ['a', 'b'], function (assert, data) {
+ assert.true(true);
+ assert.equal(typeof data, 'string');
+});
+
+QUnit.module.if('skip group', false, function () {
+ QUnit.test('skipper', function (assert) {
+ assert.true(false);
+ });
+});
+
+QUnit.module.if('keep group', true, function (hooks) {
+ let list = [];
+ hooks.beforeEach(function () {
+ list.push('x');
+ });
+ QUnit.test('keeper', function (assert) {
+ assert.true(true);
+ assert.deepEqual(list, ['x']);
+ });
+});
diff --git a/test/cli/fixtures/test-if.tap.txt b/test/cli/fixtures/test-if.tap.txt
new file mode 100644
index 000000000..fc4ead929
--- /dev/null
+++ b/test/cli/fixtures/test-if.tap.txt
@@ -0,0 +1,18 @@
+# name: no tests
+# command: ["qunit", "test-if.js"]
+
+TAP version 13
+ok 1 # SKIP skip me
+ok 2 keep me
+ok 3 regular
+ok 4 # SKIP skip dataset [0]
+ok 5 # SKIP skip dataset [1]
+ok 6 keep dataset [0]
+ok 7 keep dataset [1]
+ok 8 # SKIP skip group > skipper
+ok 9 keep group > keeper
+1..9
+# pass 5
+# skip 4
+# todo 0
+# fail 0
diff --git a/test/main/deepEqual.js b/test/main/deepEqual.js
index 8b14a6385..5cf50451d 100644
--- a/test/main/deepEqual.js
+++ b/test/main/deepEqual.js
@@ -1825,7 +1825,7 @@ var hasES6Map = (function () {
}
}());
-QUnit[hasES6Set ? 'test' : 'skip']('Sets', function (assert) {
+QUnit.test.if('Sets', hasES6Set, function (assert) {
var s1, s2, s3, s4, o1, o2, o3, o4, m1, m2, m3;
// Empty sets
@@ -1898,7 +1898,7 @@ QUnit[hasES6Set ? 'test' : 'skip']('Sets', function (assert) {
assert.equal(QUnit.equiv(s1, s2), true, 'Sets with different insertion orders');
});
-QUnit[hasES6Map ? 'test' : 'skip']('Maps', function (assert) {
+QUnit.test.if('Maps', hasES6Map, function (assert) {
var m1, m2, m3, m4, o1, o2, o3, o4, s1, s2, s3;
// Empty maps
@@ -2016,7 +2016,7 @@ var hasES6Symbol = (function () {
return typeof Symbol === 'function';
}());
-QUnit[hasES6Symbol ? 'test' : 'skip']('Symbols', function (assert) {
+QUnit.test.if('Symbols', hasES6Symbol, function (assert) {
var a = Symbol(1);
var b = Symbol(1);
diff --git a/test/main/stacktrace.js b/test/main/stacktrace.js
index 6767f29b7..b3b779cd9 100644
--- a/test/main/stacktrace.js
+++ b/test/main/stacktrace.js
@@ -1,5 +1,5 @@
// Skip in environments without Error#stack support
-(QUnit.stack() ? QUnit.module : QUnit.module.skip)('stacktrace', function () {
+QUnit.module.if('stacktrace', !!QUnit.stack(), function () {
function fooCurrent () {
return QUnit.stack();
}
@@ -65,7 +65,7 @@
// We do that for failed assertions, but for passing tests we omit
// source details in these older browsers.
var supportsUnthrownStack = !!(new Error().stack);
- (supportsUnthrownStack ? QUnit.module : QUnit.module.skip)('source details', function () {
+ QUnit.module.if('source details', supportsUnthrownStack, function () {
QUnit.test('QUnit.test()', function (assert) {
var stack = norm(QUnit.config.current.stack);
var line = stack.split('\n')[0];
diff --git a/test/main/test.js b/test/main/test.js
index 257e33788..e752378a6 100644
--- a/test/main/test.js
+++ b/test/main/test.js
@@ -8,7 +8,7 @@ QUnit.module('test', function () {
assert.true(true);
});
- (typeof document !== 'undefined' ? QUnit.module : QUnit.module.skip)('fixture management', function (hooks) {
+ QUnit.module.if('fixture management', typeof document !== 'undefined', function (hooks) {
/* global document */
var failure = false;
var values = [
diff --git a/test/reorderError1.js b/test/reorderError1.js
index f9020d282..8d7e51e58 100644
--- a/test/reorderError1.js
+++ b/test/reorderError1.js
@@ -1,7 +1,8 @@
/* eslint-env browser */
QUnit.module('Test call count - first case');
-QUnit[window.sessionStorage ? 'test' : 'skip'](
+QUnit.test.if(
'does not skip tests after reordering',
+ !!window.sessionStorage,
function (assert) {
assert.equal(window.totalCount, 3);
}
diff --git a/test/reorderError2.js b/test/reorderError2.js
index d56a85c0c..953a6e6c6 100644
--- a/test/reorderError2.js
+++ b/test/reorderError2.js
@@ -1,7 +1,8 @@
/* eslint-env browser */
QUnit.module('Test call count - second case');
-QUnit[window.sessionStorage ? 'test' : 'skip'](
+QUnit.test.if(
'does not skip tests after reordering',
+ !!window.sessionStorage,
function (assert) {
assert.equal(window.totalCount, 2);
}