Skip to content

Commit 7fbb7a0

Browse files
Use ava's diff comparison view to report assertion mismatches during tests (#114)
1 parent f1a2057 commit 7fbb7a0

File tree

4 files changed

+108
-46
lines changed

4 files changed

+108
-46
lines changed

source/lib/rules/files-property.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ export default (context: Context): Diagnostic[] => {
2424
return [];
2525
}
2626

27-
const content = fs.readFileSync(path.join(context.cwd, 'package.json'), 'utf8');
27+
const packageJsonFullPath = path.join(context.cwd, 'package.json');
28+
const content = fs.readFileSync(packageJsonFullPath, 'utf8');
2829

2930
return [
3031
{
31-
fileName: 'package.json',
32+
fileName: packageJsonFullPath,
3233
message: `TypeScript type definition \`${normalizedTypingsFile}\` is not part of the \`files\` list.`,
3334
severity: 'error',
3435
...getJSONPropertyPosition(content, 'files')

source/lib/rules/types-property.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ export default (context: Context): Diagnostic[] => {
1313
const {pkg} = context;
1414

1515
if (!pkg.types && pkg.typings) {
16-
const content = fs.readFileSync(path.join(context.cwd, 'package.json'), 'utf8');
16+
const packageJsonFullPath = path.join(context.cwd, 'package.json');
17+
const content = fs.readFileSync(packageJsonFullPath, 'utf8');
1718

1819
return [
1920
{
20-
fileName: 'package.json',
21+
fileName: packageJsonFullPath,
2122
message: 'Use property `types` instead of `typings`.',
2223
severity: 'error',
2324
...getJSONPropertyPosition(content, 'typings')

source/test/fixtures/utils.ts

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
1+
import * as path from 'path';
12
import {ExecutionContext} from 'ava';
23
import {Diagnostic} from '../../lib/interfaces';
34

4-
type Expectation = [number, number, 'error' | 'warning', string, (string | RegExp)?];
5+
type Expectation = [
6+
line: number,
7+
column: number,
8+
severity: 'error' | 'warning',
9+
message: string,
10+
];
11+
12+
type ExpectationWithFilename = [
13+
line: number,
14+
column: number,
15+
severity: 'error' | 'warning',
16+
message: string,
17+
fileName: string,
18+
];
519

620
/**
721
* Verify a list of diagnostics.
@@ -11,20 +25,52 @@ type Expectation = [number, number, 'error' | 'warning', string, (string | RegEx
1125
* @param expectations - Expected diagnostics.
1226
*/
1327
export const verify = (t: ExecutionContext, diagnostics: Diagnostic[], expectations: Expectation[]) => {
14-
t.deepEqual(diagnostics.length, expectations.length, 'Received different count of diagnostics than expected!');
15-
16-
for (const [index, diagnostic] of diagnostics.entries()) {
17-
t.is(diagnostic.line, expectations[index][0], `"line" for diagnostic ${index} doesn't match!`);
18-
t.is(diagnostic.column, expectations[index][1], `"column" for diagnostic ${index} doesn't match!`);
19-
t.is(diagnostic.severity, expectations[index][2], `"severity" for diagnostic ${index} doesn't match!`);
20-
t.is(diagnostic.message, expectations[index][3], `"message" for diagnostic ${index} doesn't match!`);
21-
22-
const filename = expectations[index][4];
23-
24-
if (typeof filename === 'string') {
25-
t.is(diagnostic.fileName, filename, `"fileName" for diagnostic ${index} doesn't match!`);
26-
} else if (typeof filename === 'object') {
27-
t.regex(diagnostic.fileName, filename, `"fileName" for diagnostic ${index} doesn't match!`);
28-
}
29-
}
28+
const diagnosticObjs = diagnostics.map(({line, column, severity, message}) => ({
29+
line,
30+
column,
31+
severity,
32+
message,
33+
}));
34+
35+
const expectationObjs = expectations.map(([line, column, severity, message]) => ({
36+
line,
37+
column,
38+
severity,
39+
message,
40+
}));
41+
42+
t.deepEqual(diagnosticObjs, expectationObjs, 'Received diagnostics that are different from expectations!');
43+
};
44+
45+
/**
46+
* Verify a list of diagnostics including file paths.
47+
*
48+
* @param t - The AVA execution context.
49+
* @param cwd - The working directory as passed to `tsd`.
50+
* @param diagnostics - List of diagnostics to verify.
51+
* @param expectations - Expected diagnostics.
52+
*/
53+
export const verifyWithFileName = (
54+
t: ExecutionContext,
55+
cwd: string,
56+
diagnostics: Diagnostic[],
57+
expectations: ExpectationWithFilename[]
58+
) => {
59+
const diagnosticObjs = diagnostics.map(({line, column, severity, message, fileName}) => ({
60+
line,
61+
column,
62+
severity,
63+
message,
64+
fileName: path.relative(cwd, fileName),
65+
}));
66+
67+
const expectationObjs = expectations.map(([line, column, severity, message, fileName]) => ({
68+
line,
69+
column,
70+
severity,
71+
message,
72+
fileName,
73+
}));
74+
75+
t.deepEqual(diagnosticObjs, expectationObjs, 'Received diagnostics that are different from expectations!');
3076
};

source/test/test.ts

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as path from 'path';
22
import test from 'ava';
3-
import {verify} from './fixtures/utils';
3+
import {verify, verifyWithFileName} from './fixtures/utils';
44
import tsd from '..';
55
import {Diagnostic} from '../lib/interfaces';
66

@@ -21,18 +21,20 @@ test('return diagnostics', async t => {
2121
});
2222

2323
test('return diagnostics from imported files as well', async t => {
24-
const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/failure-nested')});
24+
const cwd = path.join(__dirname, 'fixtures/failure-nested');
25+
const diagnostics = await tsd({cwd});
2526

26-
verify(t, diagnostics, [
27-
[5, 19, 'error', 'Argument of type \'number\' is not assignable to parameter of type \'string\'.', /child.test-d.ts$/],
28-
[6, 19, 'error', 'Argument of type \'number\' is not assignable to parameter of type \'string\'.', /index.test-d.ts$/]
27+
verifyWithFileName(t, cwd, diagnostics, [
28+
[5, 19, 'error', 'Argument of type \'number\' is not assignable to parameter of type \'string\'.', 'child.test-d.ts'],
29+
[6, 19, 'error', 'Argument of type \'number\' is not assignable to parameter of type \'string\'.', 'index.test-d.ts'],
2930
]);
3031
});
3132

3233
test('fail if typings file is not part of `files` list', async t => {
33-
const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/no-files')});
34+
const cwd = path.join(__dirname, 'fixtures/no-files');
35+
const diagnostics = await tsd({cwd});
3436

35-
verify(t, diagnostics, [
37+
verifyWithFileName(t, cwd, diagnostics, [
3638
[3, 1, 'error', 'TypeScript type definition `index.d.ts` is not part of the `files` list.', 'package.json'],
3739
]);
3840
});
@@ -50,39 +52,51 @@ test('allow specifying glob patterns containing typings file in `files` list', a
5052
});
5153

5254
test('fail if `typings` property is used instead of `types`', async t => {
53-
const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/types-property/typings')});
55+
const cwd = path.join(__dirname, 'fixtures/types-property/typings');
56+
const diagnostics = await tsd({cwd});
5457

55-
verify(t, diagnostics, [
58+
verifyWithFileName(t, cwd, diagnostics, [
5659
[3, 1, 'error', 'Use property `types` instead of `typings`.', 'package.json'],
5760
]);
5861
});
5962

6063
test('fail if tests don\'t pass in strict mode', async t => {
61-
const diagnostics = await tsd({
62-
cwd: path.join(__dirname, 'fixtures/failure-strict-null-checks')
63-
});
64-
65-
verify(t, diagnostics, [
66-
[4, 19, 'error', 'Argument of type \'number | null\' is not assignable to parameter of type \'number\'.\n Type \'null\' is not assignable to type \'number\'.', /failure-strict-null-checks\/index.test-d.ts$/],
64+
const cwd = path.join(__dirname, 'fixtures/failure-strict-null-checks');
65+
const diagnostics = await tsd({cwd});
66+
67+
verifyWithFileName(t, cwd, diagnostics, [
68+
[
69+
4,
70+
19,
71+
'error',
72+
'Argument of type \'number | null\' is not assignable to parameter of type \'number\'.\n Type \'null\' is not assignable to type \'number\'.',
73+
'index.test-d.ts',
74+
],
6775
]);
6876
});
6977

7078
test('overridden config defaults to `strict` if `strict` is not explicitly overridden', async t => {
71-
const diagnostics = await tsd({
72-
cwd: path.join(__dirname, 'fixtures/strict-null-checks-as-default-config-value')
73-
});
74-
75-
verify(t, diagnostics, [
76-
[4, 19, 'error', 'Argument of type \'number | null\' is not assignable to parameter of type \'number\'.\n Type \'null\' is not assignable to type \'number\'.', /strict-null-checks-as-default-config-value\/index.test-d.ts$/],
79+
const cwd = path.join(__dirname, 'fixtures/strict-null-checks-as-default-config-value');
80+
const diagnostics = await tsd({cwd});
81+
82+
verifyWithFileName(t, cwd, diagnostics, [
83+
[
84+
4,
85+
19,
86+
'error',
87+
'Argument of type \'number | null\' is not assignable to parameter of type \'number\'.\n Type \'null\' is not assignable to type \'number\'.',
88+
'index.test-d.ts',
89+
],
7790
]);
7891
});
7992

8093
test('fail if types are used from a lib that was not explicitly specified', async t => {
81-
const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/lib-config/failure-missing-lib')});
94+
const cwd = path.join(__dirname, 'fixtures/lib-config/failure-missing-lib');
95+
const diagnostics = await tsd({cwd});
8296

83-
verify(t, diagnostics, [
84-
[1, 22, 'error', 'Cannot find name \'Window\'.', /failure-missing-lib\/index.d.ts$/],
85-
[4, 11, 'error', 'Cannot find name \'Window\'.', /failure-missing-lib\/index.test-d.ts$/]
97+
verifyWithFileName(t, cwd, diagnostics, [
98+
[1, 22, 'error', 'Cannot find name \'Window\'.', 'index.d.ts'],
99+
[4, 11, 'error', 'Cannot find name \'Window\'.', 'index.test-d.ts'],
86100
]);
87101
});
88102

0 commit comments

Comments
 (0)