Skip to content

Commit 2a64995

Browse files
committed
Core: Add automatic labels in test.each() for primitive values in arrays
Cherry-picked from bf42d2b (3.0.0-dev). > Fixes #1733.
1 parent 390211d commit 2a64995

5 files changed

Lines changed: 134 additions & 8 deletions

File tree

src/test.js

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -997,10 +997,56 @@ function makeEachTestName (testName, argument) {
997997
return `${testName} [${argument}]`;
998998
}
999999

1000+
// Characters to avoid in test names especially CLI/AP output:
1001+
// * x00-1F: e.g. NULL, backspace (\b), line breaks (\r\n), ESC.
1002+
// * x74: DEL.
1003+
// * xA0: non-breaking space.
1004+
//
1005+
// See https://en.wikipedia.org/wiki/ASCII#Character_order
1006+
//
1007+
// eslint-disable-next-line no-control-regex
1008+
const rNonObviousStr = /[\x00-\x1F\x7F\xA0]/;
10001009
function runEach (data, eachFn) {
10011010
if (Array.isArray(data)) {
10021011
for (let i = 0; i < data.length; i++) {
1003-
eachFn(data[i], i);
1012+
const value = data[i];
1013+
1014+
// Create automatic labels for primitive data in arrays passed to test.each().
1015+
// We want to avoid the default "example [0], example [1]" where possible since
1016+
// these are not self-explanatory in results, and are also tedious to locate
1017+
// the source of since the numerical key of an array isn't literally in the
1018+
// code (you have to count).
1019+
//
1020+
// Design requirements:
1021+
// * Unique. Each label must be unique and correspond 1:1 with a data value.
1022+
// This way each test name will hash to a unique testId with Rerun link,
1023+
// without having to rely on Test class enforcing uniqueness with invisible
1024+
// space hack.
1025+
// * Unambigious. While technical uniqueness is a hard requirement above,
1026+
// we also want the labels to be obvious and unambiguous to humans.
1027+
// For example, abbrebating "foobar" and "foobaz" to "f" and "fo" is
1028+
// technically unique, but ambigious to humans which one is which.
1029+
// * Short and readable. Where possible we omit the array index numbers
1030+
// so that in most cases, the value is simply shown as-is.
1031+
// We prefer "example [foo], example [bar]"
1032+
// over "example [0: foo], example [2: bar]".
1033+
// This also has the benefit of being stable and robust against e.g.
1034+
// re-ordering data or adding new items during development, without
1035+
// invalidating a previous filter or rerun link immediately.
1036+
const valueType = typeof value;
1037+
let testKey = i;
1038+
if (valueType === 'string' && value.length <= 40 && !rNonObviousStr.test(value) && !/\s*\d+: /.test(value)) {
1039+
testKey = value;
1040+
} else if (valueType === 'string' || valueType === 'number' || valueType === 'boolean' || valueType === 'undefined' || value === null) {
1041+
const valueForName = String(value);
1042+
if (!rNonObviousStr.test(valueForName)) {
1043+
testKey = i + ': ' + (valueForName.length <= 30
1044+
? valueForName
1045+
: valueForName.slice(0, 29) + '…'
1046+
);
1047+
}
1048+
}
1049+
eachFn(value, testKey);
10041050
}
10051051
} else if (typeof data === 'object' && data !== null) {
10061052
for (let key in data) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Automatic labels for test.each() array data where possible
2+
// https://github.com/qunitjs/qunit/issues/1733
3+
4+
QUnit.test.each('array of arrays', [[1, 2, 3], [1, 1, 2]], function (assert, _data) {
5+
assert.true(true);
6+
});
7+
8+
QUnit.test.each('array of simple strings', [
9+
'foo',
10+
'x'.repeat(40),
11+
'$',
12+
'http://example.org',
13+
' ',
14+
''
15+
], function (assert, _data) {
16+
assert.true(true);
17+
});
18+
19+
QUnit.test.each('array of mixed', [
20+
undefined,
21+
null,
22+
false,
23+
true,
24+
0,
25+
1,
26+
-10,
27+
10 / 3,
28+
10e42,
29+
Infinity,
30+
NaN,
31+
[],
32+
{},
33+
'999: example',
34+
'simple string',
35+
'\b',
36+
'\n',
37+
'y'.repeat(100)
38+
], function (assert, _value) {
39+
assert.true(true);
40+
});
41+
42+
QUnit.test.each('keyed objects', { caseFoo: [1, 2, 3], caseBar: [1, 1, 2] }, function (assert, _data) {
43+
assert.true(true);
44+
});
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# command: ["qunit", "each-array-labels.js"]
2+
3+
TAP version 13
4+
ok 1 array of arrays [0]
5+
ok 2 array of arrays [1]
6+
ok 3 array of simple strings [foo]
7+
ok 4 array of simple strings [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
8+
ok 5 array of simple strings [$]
9+
ok 6 array of simple strings [http://example.org]
10+
ok 7 array of simple strings [ ]
11+
ok 8 array of simple strings []
12+
ok 9 array of mixed [0: undefined]
13+
ok 10 array of mixed [1: null]
14+
ok 11 array of mixed [2: false]
15+
ok 12 array of mixed [3: true]
16+
ok 13 array of mixed [4: 0]
17+
ok 14 array of mixed [5: 1]
18+
ok 15 array of mixed [6: -10]
19+
ok 16 array of mixed [7: 3.3333333333333335]
20+
ok 17 array of mixed [8: 1e+43]
21+
ok 18 array of mixed [9: Infinity]
22+
ok 19 array of mixed [10: NaN]
23+
ok 20 array of mixed [11]
24+
ok 21 array of mixed [12]
25+
ok 22 array of mixed [13: 999: example]
26+
ok 23 array of mixed [simple string]
27+
ok 24 array of mixed [15]
28+
ok 25 array of mixed [16]
29+
ok 26 array of mixed [17: yyyyyyyyyyyyyyyyyyyyyyyyyyyyy…]
30+
ok 27 keyed objects [caseFoo]
31+
ok 28 keyed objects [caseBar]
32+
1..28
33+
# pass 28
34+
# skip 0
35+
# todo 0
36+
# fail 0

test/cli/fixtures/test-if.tap.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ TAP version 13
55
ok 1 # SKIP skip me
66
ok 2 keep me
77
ok 3 regular
8-
ok 4 # SKIP skip dataset [0]
9-
ok 5 # SKIP skip dataset [1]
10-
ok 6 keep dataset [0]
11-
ok 7 keep dataset [1]
8+
ok 4 # SKIP skip dataset [a]
9+
ok 5 # SKIP skip dataset [b]
10+
ok 6 keep dataset [a]
11+
ok 7 keep dataset [b]
1212
ok 8 # SKIP skip group > skipper
1313
ok 9 keep group > keeper
1414
1..9

test/main/promise.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,21 +248,21 @@ QUnit.module('Support for Promise', function () {
248248
}
249249
});
250250

251-
QUnit.test.each('fulfilled Promise', [1], function (assert, _data) {
251+
QUnit.test.each('fulfilled Promise', ['x'], function (assert, _data) {
252252
assert.expect(1);
253253

254254
// Adds 1 assertion
255255
return createMockPromise(assert);
256256
});
257257

258-
QUnit.test.each('rejected Promise with Error', [1], function (assert, _data) {
258+
QUnit.test.each('rejected Promise with Error', ['x'], function (assert, _data) {
259259
assert.expect(2);
260260

261261
this.pushFailure = assert.test.pushFailure;
262262
assert.test.pushFailure = function (message) {
263263
assert.strictEqual(
264264
message,
265-
'Promise rejected during "rejected Promise with Error [0]": this is an error'
265+
'Promise rejected during "rejected Promise with Error [x]": this is an error'
266266
);
267267
};
268268

0 commit comments

Comments
 (0)