Skip to content

Commit 797287a

Browse files
Improve test result accessibility
* Explain diff gutter symbols. Fixes #1558 * Add test result labels. Color covers figure and label, and indicates passed-as-expected vs. not (expected/unexpected fail or unexpected pass). Fixes #2919 * Bold the source line of errors in code excerpts * TAP reporter: Strip ANSI control sequences from error labels * Print assertion error in italics, to better differentiate from the test title. Co-authored-by: Mark Wubben <[email protected]>
1 parent cc10b0c commit 797287a

23 files changed

+169
-152
lines changed

lib/assert.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import {SnapshotError, VersionMismatchError} from './snapshot-manager.js';
88

99
function formatDescriptorDiff(actualDescriptor, expectedDescriptor, options) {
1010
options = {...options, ...concordanceOptions};
11+
const {diffGutters} = options.theme;
12+
const {insertLine, deleteLine} = options.theme.string.diff;
1113
return {
12-
label: 'Difference:',
14+
label: `Difference (${diffGutters.actual}${deleteLine.open}actual${deleteLine.close}, ${diffGutters.expected}${insertLine.open}expected${insertLine.close}):`,
1315
formatted: concordance.diffDescriptors(actualDescriptor, expectedDescriptor, options),
1416
};
1517
}

lib/code-excerpt.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default function exceptCode(source, options = {}) {
4343
const coloredLineNumber = isErrorSource ? lineNumber : chalk.grey(lineNumber);
4444
const result = ` ${coloredLineNumber} ${item.value.padEnd(extendedWidth)}`;
4545

46-
return isErrorSource ? chalk.bgRed(result) : result;
46+
return isErrorSource ? chalk.bgRed.bold(result) : result;
4747
})
4848
.join('\n');
4949
}

lib/concordance-options.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ const colorTheme = {
8585
undefined: ansiStyles.yellow,
8686
};
8787

88-
const plainTheme = JSON.parse(JSON.stringify(colorTheme), value => typeof value === 'string' ? stripAnsi(value) : value);
88+
const plainTheme = JSON.parse(JSON.stringify(colorTheme), (_name, value) => typeof value === 'string' ? stripAnsi(value) : value);
8989

9090
const theme = chalk.level > 0 ? colorTheme : plainTheme;
9191

lib/reporters/default.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,9 @@ export default class Reporter {
254254

255255
case 'selected-test': {
256256
if (event.skip) {
257-
this.lineWriter.writeLine(colors.skip(`- ${this.prefixTitle(event.testFile, event.title)}`));
257+
this.lineWriter.writeLine(colors.skip(`- [skip] ${this.prefixTitle(event.testFile, event.title)}`));
258258
} else if (event.todo) {
259-
this.lineWriter.writeLine(colors.todo(`- ${this.prefixTitle(event.testFile, event.title)}`));
259+
this.lineWriter.writeLine(colors.todo(`- [todo] ${this.prefixTitle(event.testFile, event.title)}`));
260260
}
261261

262262
break;
@@ -514,15 +514,30 @@ export default class Reporter {
514514
}
515515

516516
writeTestSummary(event) {
517+
// Prefix icon indicates matched expectations vs. not.
518+
// Prefix color indicates passed-as-expected vs. not (fail or unexpected pass).
519+
// This yields four possibilities, which in the standard configuration render as:
520+
// * normal test, pass: <green>✔</green>
521+
// * normal test, fail: <red>✘ [fail]</red>
522+
// * fail-expected test, fail: <red>✔ [expected fail]</red>
523+
// * fail-expected test, pass: <red>✘ [unexpected pass]</red>
524+
let prefix;
525+
let suffix;
517526
if (event.type === 'hook-failed' || event.type === 'test-failed') {
518-
this.write(`${colors.error(figures.cross)} ${this.prefixTitle(event.testFile, event.title)} ${colors.error(event.err.message)}`);
527+
const type = event.knownFailing ? '[unexpected pass]' : '[fail]';
528+
prefix = colors.error(`${figures.cross} ${type}:`);
529+
suffix = chalk.italic(colors.error(event.err.message));
519530
} else if (event.knownFailing) {
520-
this.write(`${colors.error(figures.tick)} ${colors.error(this.prefixTitle(event.testFile, event.title))}`);
531+
prefix = colors.error(figures.tick + ' [expected fail]');
521532
} else {
522-
const duration = event.duration > this.durationThreshold ? colors.duration(' (' + prettyMs(event.duration) + ')') : '';
523-
this.write(`${colors.pass(figures.tick)} ${this.prefixTitle(event.testFile, event.title)}${duration}`);
533+
prefix = colors.pass(figures.tick);
534+
if (event.duration > this.durationThreshold) {
535+
suffix = colors.duration(`(${prettyMs(event.duration)})`);
536+
}
524537
}
525538

539+
const label = this.prefixTitle(event.testFile, event.title);
540+
this.write(`${prefix} ${label}${suffix ? ' ' + suffix : ''}`);
526541
this.writeLogs(event);
527542
}
528543

lib/reporters/tap.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function dumpError(error) {
2929
}
3030

3131
if (error.values.length > 0) {
32-
object.values = Object.fromEntries(error.values.map(({label, formatted}) => [label, stripAnsi(formatted)]));
32+
object.values = Object.fromEntries(error.values.map(({label, formatted}) => [stripAnsi(label), stripAnsi(formatted)]));
3333
}
3434
}
3535

test-tap/assert.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ function assertFailure(t, subset) {
5656
if (subset.values) {
5757
t.equal(lastFailure.values.length, subset.values.length);
5858
for (const [i, s] of lastFailure.values.entries()) {
59-
t.equal(s.label, subset.values[i].label);
59+
t.equal(stripAnsi(s.label), subset.values[i].label);
6060
t.match(stripAnsi(s.formatted), subset.values[i].formatted);
6161
}
6262
} else {
@@ -279,7 +279,7 @@ test('.is()', t => {
279279
message: '',
280280
raw: {actual: 'foo', expected: 'bar'},
281281
values: [
282-
{label: 'Difference:', formatted: /- 'foo'\n\+ 'bar'/},
282+
{label: 'Difference (- actual, + expected):', formatted: /- 'foo'\n\+ 'bar'/},
283283
],
284284
});
285285

@@ -289,31 +289,31 @@ test('.is()', t => {
289289
expected: 42,
290290
message: '',
291291
values: [
292-
{label: 'Difference:', formatted: /- 'foo'\n\+ 42/},
292+
{label: 'Difference (- actual, + expected):', formatted: /- 'foo'\n\+ 42/},
293293
],
294294
});
295295

296296
failsWith(t, () => assertions.is('foo', 42, 'my message'), {
297297
assertion: 'is',
298298
message: 'my message',
299299
values: [
300-
{label: 'Difference:', formatted: /- 'foo'\n\+ 42/},
300+
{label: 'Difference (- actual, + expected):', formatted: /- 'foo'\n\+ 42/},
301301
],
302302
});
303303

304304
failsWith(t, () => assertions.is(0, -0, 'my message'), {
305305
assertion: 'is',
306306
message: 'my message',
307307
values: [
308-
{label: 'Difference:', formatted: /- 0\n\+ -0/},
308+
{label: 'Difference (- actual, + expected):', formatted: /- 0\n\+ -0/},
309309
],
310310
});
311311

312312
failsWith(t, () => assertions.is(-0, 0, 'my message'), {
313313
assertion: 'is',
314314
message: 'my message',
315315
values: [
316-
{label: 'Difference:', formatted: /- -0\n\+ 0/},
316+
{label: 'Difference (- actual, + expected):', formatted: /- -0\n\+ 0/},
317317
],
318318
});
319319

@@ -535,20 +535,20 @@ test('.deepEqual()', t => {
535535
assertion: 'deepEqual',
536536
message: '',
537537
raw: {actual: 'foo', expected: 'bar'},
538-
values: [{label: 'Difference:', formatted: /- 'foo'\n\+ 'bar'/}],
538+
values: [{label: 'Difference (- actual, + expected):', formatted: /- 'foo'\n\+ 'bar'/}],
539539
});
540540

541541
failsWith(t, () => assertions.deepEqual('foo', 42), {
542542
assertion: 'deepEqual',
543543
message: '',
544544
raw: {actual: 'foo', expected: 42},
545-
values: [{label: 'Difference:', formatted: /- 'foo'\n\+ 42/}],
545+
values: [{label: 'Difference (- actual, + expected):', formatted: /- 'foo'\n\+ 42/}],
546546
});
547547

548548
failsWith(t, () => assertions.deepEqual('foo', 42, 'my message'), {
549549
assertion: 'deepEqual',
550550
message: 'my message',
551-
values: [{label: 'Difference:', formatted: /- 'foo'\n\+ 42/}],
551+
values: [{label: 'Difference (- actual, + expected):', formatted: /- 'foo'\n\+ 42/}],
552552
});
553553

554554
failsWith(t, () => assertions.deepEqual({}, {}, null), {
@@ -758,7 +758,7 @@ test('.like()', t => {
758758
failsWith(t, () => assertions.like({a: 'foo', b: 'irrelevant'}, {a: 'bar'}), {
759759
assertion: 'like',
760760
message: '',
761-
values: [{label: 'Difference:', formatted: /{\n-\s*a: 'foo',\n\+\s*a: 'bar',\n\s*}/}],
761+
values: [{label: 'Difference (- actual, + expected):', formatted: /{\n-\s*a: 'foo',\n\+\s*a: 'bar',\n\s*}/}],
762762
});
763763

764764
t.end();
@@ -1429,7 +1429,7 @@ test('.snapshot()', async t => {
14291429
failsWith(t, () => assertions.snapshot({foo: 'not bar'}), {
14301430
assertion: 'snapshot',
14311431
message: 'Did not match snapshot',
1432-
values: [{label: 'Difference:', formatted: ' {\n- foo: \'not bar\',\n+ foo: \'bar\',\n }'}],
1432+
values: [{label: 'Difference (- actual, + expected):', formatted: ' {\n- foo: \'not bar\',\n+ foo: \'bar\',\n }'}],
14331433
});
14341434
}
14351435

@@ -1442,7 +1442,7 @@ test('.snapshot()', async t => {
14421442
failsWith(t, () => assertions.snapshot({foo: 'not bar'}, 'my message'), {
14431443
assertion: 'snapshot',
14441444
message: 'my message',
1445-
values: [{label: 'Difference:', formatted: ' {\n- foo: \'not bar\',\n+ foo: \'bar\',\n }'}],
1445+
values: [{label: 'Difference (- actual, + expected):', formatted: ' {\n- foo: \'not bar\',\n+ foo: \'bar\',\n }'}],
14461446
});
14471447
}
14481448

test-tap/code-excerpt.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ test('read code excerpt', t => {
2222
const excerpt = codeExcerpt({file, line: 2, isWithinProject: true, isDependency: false});
2323
const expected = [
2424
` ${chalk.grey('1:')} function a() {`,
25-
chalk.bgRed(' 2: alert(); '),
25+
chalk.bgRed.bold(' 2: alert(); '),
2626
` ${chalk.grey('3:')} } `,
2727
].join('\n');
2828

@@ -40,7 +40,7 @@ test('truncate lines', t => {
4040
const excerpt = codeExcerpt({file, line: 2, isWithinProject: true, isDependency: false}, {maxWidth: 14});
4141
const expected = [
4242
` ${chalk.grey('1:')} functio…`,
43-
chalk.bgRed(' 2: alert…'),
43+
chalk.bgRed.bold(' 2: alert…'),
4444
` ${chalk.grey('3:')} } `,
4545
].join('\n');
4646

@@ -66,7 +66,7 @@ test('format line numbers', t => {
6666
const excerpt = codeExcerpt({file, line: 10, isWithinProject: true, isDependency: false});
6767
const expected = [
6868
` ${chalk.grey(' 9:')} function a() {`,
69-
chalk.bgRed(' 10: alert(); '),
69+
chalk.bgRed.bold(' 10: alert(); '),
7070
` ${chalk.grey('11:')} } `,
7171
].join('\n');
7272

test-tap/reporters/default.edgecases.v14.log

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import-and-use-test-member.cjs:3
2121

2222
2:
23-
 3: test('pass', t => t.pass());
23+
 3: test('pass', t => t.pass());
2424
4:
2525

2626
TypeError: test is not a function
@@ -39,7 +39,7 @@
3939

4040
throws.cjs:1
4141

42-
 1: throw new Error('throws');
42+
 1: throw new Error('throws');
4343
2:
4444

4545
Error: throws

test-tap/reporters/default.edgecases.v16.log

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import-and-use-test-member.cjs:3
2121

2222
2:
23-
 3: test('pass', t => t.pass());
23+
 3: test('pass', t => t.pass());
2424
4:
2525

2626
TypeError: test is not a function
@@ -39,7 +39,7 @@
3939

4040
throws.cjs:1
4141

42-
 1: throw new Error('throws');
42+
 1: throw new Error('throws');
4343
2:
4444

4545
Error: throws

test-tap/reporters/default.edgecases.v18.log

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import-and-use-test-member.cjs:3
2525

2626
2:
27-
 3: test('pass', t => t.pass());
27+
 3: test('pass', t => t.pass());
2828
4:
2929

3030
TypeError: test is not a function
@@ -43,7 +43,7 @@
4343

4444
throws.cjs:1
4545

46-
 1: throw new Error('throws');
46+
 1: throw new Error('throws');
4747
2:
4848

4949
Error: throws

0 commit comments

Comments
 (0)