Skip to content

Commit 33b2bb2

Browse files
committed
Port tests to TS and React Testing Library, refactor based on functional component
1 parent 49512db commit 33b2bb2

18 files changed

+1697
-467
lines changed

jest.config.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ module.exports = {
1111
},
1212
},
1313
moduleDirectories: ['node_modules', 'src'],
14-
setupFiles: ['raf/polyfill', '<rootDir>/jest/enzyme-setup.ts'],
1514
setupFilesAfterEnv: ['<rootDir>/jest/test-bundler.ts'],
1615
testRegex: '.*\\.test\\.(ts|js|tsx|jsx)$',
1716
watchPlugins: [

jest/enzyme-setup.ts

Lines changed: 0 additions & 4 deletions
This file was deleted.

jest/jest.d.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { MatcherFunction } from 'expect';
2+
3+
declare global {
4+
// eslint-disable-next-line @typescript-eslint/no-namespace
5+
namespace jest {
6+
interface Expect {
7+
toMatchVector(expected: number[], precision?: number): number[];
8+
toMatchMatrix(expected: number[], precision?: number): number[];
9+
vectorMatching(expected: number[], precision?: number): number[];
10+
matrixMatching(expected: number[], precision?: number): number[];
11+
}
12+
interface ExpectExtendMap {
13+
toMatchVector: MatcherFunction<[expected: number[], precision?: number]>;
14+
toMatchMatrix: MatcherFunction<[expected: number[], precision?: number]>;
15+
vectorMatching: MatcherFunction<[expected: number[], precision?: number]>;
16+
matrixMatching: MatcherFunction<[expected: number[], precision?: number]>;
17+
}
18+
}
19+
}

jest/test-bundler.ts

Lines changed: 172 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,172 @@
1-
// import matchers from 'expect/build/matchers';
2-
//
3-
// // toMatchObject is supposed to match arrays of objects too, but it isn't currently working.
4-
// expect.extend({
5-
// toAllMatchObject(actualArray, objectMatcher) {
6-
// const results = actualArray.map((item) =>
7-
// matchers.toMatchObject(item, objectMatcher),
8-
// );
9-
// const pass = results.reduce((acc, result) => acc && result.pass, true);
10-
// const message = pass ? () => {} : results.find((res) => !res.pass).message;
11-
//
12-
// return {
13-
// message,
14-
// pass,
15-
// };
16-
// },
17-
// });
1+
import '@testing-library/jest-dom';
2+
import CustomMatcherResult = jest.CustomMatcherResult;
3+
import MatcherContext = jest.MatcherContext;
4+
5+
const isExpand = (expand?: boolean): boolean => expand !== false;
6+
7+
// Annoying ripped logic from expect, since jest team don't want to expose
8+
// existing matchers:
9+
// https://github.com/facebook/jest/issues/10329
10+
// https://github.com/facebook/jest/issues/2547
11+
const toBeCloseTo = (received: number, expected: number, precision = 2) => {
12+
if (typeof expected !== 'number') {
13+
throw new Error('expected value must be a number');
14+
}
15+
16+
if (typeof received !== 'number') {
17+
throw new Error('received value must be a number');
18+
}
19+
20+
let pass = false;
21+
let expectedDiff = 0;
22+
let receivedDiff = 0;
23+
24+
if (received === Infinity && expected === Infinity) {
25+
pass = true; // Infinity - Infinity is NaN
26+
} else if (received === -Infinity && expected === -Infinity) {
27+
pass = true; // -Infinity - -Infinity is NaN
28+
} else {
29+
// eslint-disable-next-line no-restricted-properties, prefer-exponentiation-operator
30+
expectedDiff = Math.pow(10, -precision) / 2;
31+
receivedDiff = Math.abs(expected - received);
32+
pass = receivedDiff < expectedDiff;
33+
}
34+
35+
return {
36+
expectedDiff,
37+
receivedDiff,
38+
pass,
39+
};
40+
};
41+
42+
type MatrixDataType = 'Matrix' | 'Vector';
43+
44+
const makeMatrixMatcher = (scope: MatcherContext, dataType: MatrixDataType) => {
45+
return (
46+
actualMatrix: number[] | Float32Array,
47+
expectedMatrixArray: number[],
48+
precision = 6,
49+
): CustomMatcherResult => {
50+
const matcherName = `toMatch${dataType}`;
51+
const { isNot, promise, utils, expand } = scope;
52+
const options = {
53+
isNot,
54+
promise,
55+
};
56+
const received = Array.from(actualMatrix);
57+
const expected = expectedMatrixArray;
58+
59+
const {
60+
matcherHint,
61+
printExpected,
62+
printReceived,
63+
printDiffOrStringify,
64+
stringify,
65+
} = utils;
66+
67+
if (received.length !== expected.length) {
68+
return {
69+
message: () =>
70+
// eslint-disable-next-line prefer-template
71+
matcherHint(matcherName, undefined, undefined, options) +
72+
'\n\n' +
73+
`Expected: ${printExpected(expected)}\n` +
74+
`Received: ${printReceived(received)}\n` +
75+
'\n' +
76+
`${dataType} lengths do not match, expected has ${expected.length}, received has ${received.length}`,
77+
pass: false,
78+
};
79+
}
80+
81+
const results = received.map((item, index) =>
82+
toBeCloseTo(item, expectedMatrixArray[index], precision),
83+
);
84+
const pass = results.reduce((acc, result) => acc && result.pass, true);
85+
const indices = results.reduce((acc, result, index) => {
86+
return result.pass ? acc : acc.concat(index);
87+
}, [] as number[]);
88+
89+
const message = pass
90+
? () =>
91+
// eslint-disable-next-line prefer-template
92+
matcherHint(matcherName, undefined, undefined, options) +
93+
'\n\n' +
94+
`Expected: not ${printExpected(expected)}\n` +
95+
(stringify(expected) !== stringify(received)
96+
? `Received: ${printReceived(received)}`
97+
: '')
98+
: () =>
99+
// eslint-disable-next-line prefer-template
100+
matcherHint(matcherName, undefined, undefined, options) +
101+
'\n\n' +
102+
`${indices.length > 1 ? 'Indices' : 'Index'} ${indices} ${
103+
indices.length > 1 ? 'do' : 'does'
104+
} not match with precision ${precision}` +
105+
'\n\n' +
106+
// TODO: this isn't perfect because it doesn't take precision into account, so can report too many errors
107+
printDiffOrStringify(
108+
expected,
109+
received,
110+
'Expected',
111+
'Received',
112+
isExpand(expand),
113+
);
114+
115+
return {
116+
message,
117+
pass,
118+
};
119+
};
120+
};
121+
122+
expect.extend({
123+
// symmetric naming
124+
toMatchVector(
125+
actualVector: unknown,
126+
expectedVectorArray: number[],
127+
precision = 6,
128+
) {
129+
return makeMatrixMatcher(this, 'Vector')(
130+
actualVector as number[] | Float32Array,
131+
expectedVectorArray,
132+
precision,
133+
);
134+
},
135+
136+
toMatchMatrix(
137+
actualMatrix: unknown,
138+
expectedMatrixArray: number[],
139+
precision = 6,
140+
) {
141+
return makeMatrixMatcher(this, 'Matrix')(
142+
actualMatrix as number[] | Float32Array,
143+
expectedMatrixArray,
144+
precision,
145+
);
146+
},
147+
148+
// asymmetric naming
149+
vectorMatching(
150+
actualVector: unknown,
151+
expectedVectorArray: number[],
152+
precision = 6,
153+
) {
154+
return makeMatrixMatcher(this, 'Vector')(
155+
actualVector as number[] | Float32Array,
156+
expectedVectorArray,
157+
precision,
158+
);
159+
},
160+
161+
matrixMatching(
162+
actualMatrix: unknown,
163+
expectedMatrixArray: number[],
164+
precision = 6,
165+
) {
166+
return makeMatrixMatcher(this, 'Matrix')(
167+
actualMatrix as number[] | Float32Array,
168+
expectedMatrixArray,
169+
precision,
170+
);
171+
},
172+
});

0 commit comments

Comments
 (0)