Skip to content

Commit 5360170

Browse files
committed
Work on tests
1 parent f31a9ab commit 5360170

30 files changed

+817
-206
lines changed

.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
node_modules/**
33
dist/**
44
build/**
5+
coverage/**
56

67
**/.yarn
78
**/.pnp.*

.eslintrc.cjs

+4-8
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ module.exports = {
7575
},
7676
{
7777
extends: ["plugin:jest/recommended", "plugin:jest/style"],
78-
files: ["**/__tests__/**"],
78+
files: ["matchers/**", "**/__tests__/**"],
7979
rules: {
80+
"import/no-extraneous-dependencies": "off",
8081
"@typescript-eslint/ban-ts-comment": "off",
8182
"@typescript-eslint/explicit-module-boundary-types": "off",
8283
"@typescript-eslint/no-empty-function": "off",
@@ -99,12 +100,6 @@ module.exports = {
99100
"no-console": "off",
100101
},
101102
},
102-
{
103-
files: ["rollup/**"],
104-
parserOptions: {
105-
project: "rollup/tsconfig.json",
106-
},
107-
},
108103
],
109104
parser: "@typescript-eslint/parser",
110105
parserOptions: {
@@ -121,6 +116,7 @@ module.exports = {
121116
devDependencies: [
122117
"**/__tests__/**",
123118
"build-plugins/**",
119+
"matchers/**",
124120
"scripts/**",
125121
".eslintrc.cjs",
126122
"jest.config.mjs",
@@ -183,7 +179,7 @@ module.exports = {
183179
"prefer-rest-params": "warn",
184180
"prefer-spread": "warn",
185181
"prefer-template": "error",
186-
quotes: ["error", "double", { allowTemplateLiterals: false, avoidEscape: false }],
182+
quotes: ["error", "double", { allowTemplateLiterals: true, avoidEscape: false }],
187183
radix: "warn",
188184
"sort-imports": ["error", { ignoreDeclarationSort: true }],
189185
},

.gitattributes

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
* text=auto eol=lf
2-
api-extractor.json linguist-language=JSON-with-Comments
2+
3+
.vscode/**/*.json linguist-language=JSON-with-Comments

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
/node_modules/
1010
/dist/
1111
/build/
12+
/coverage/
1213

1314
# Logs.
1415
npm-debug.log*

.prettierignore

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
/node_modules/
1010
/dist/
1111
/build/
12+
/coverage/
1213

1314
# Yarn.
1415
**/.yarn

.vscode/extensions.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"recommendations": [
3+
"dbaeumer.vscode-eslint",
4+
"redhat.vscode-yaml",
5+
"esbenp.prettier-vscode",
6+
"Orta.vscode-jest"
7+
]
8+
}

.vscode/settings.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"editor.codeActionsOnSave": {
3+
"source.fixAll.eslint": true
4+
},
5+
"javascript.validate.enable": false,
6+
"jest.jestCommandLine": "yarn jest",
7+
"typescript.tsdk": "node_modules/typescript/lib"
8+
}

jest.config.mjs

+36-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,46 @@
1+
import { createRequire } from "module";
2+
13
/**
2-
* @type {import("ts-jest").JestConfigWithTsJest}
4+
* @typedef {import("jest").Config} JestConfig
5+
* @typedef {import("ts-jest").TsJestGlobalOptions} TsJestOptions
6+
*/
7+
8+
const require = createRequire(import.meta.url);
9+
10+
/**
11+
* @type {JestConfig}
312
*/
413
export default {
5-
testPathIgnorePatterns: ["/node_modules/", "/build/", "/dist/", "/scripts/", "/build/"],
14+
collectCoverageFrom: ["src/**/*.ts", "!**/__tests__/**", "!build/**", "!dist/**"],
15+
modulePathIgnorePatterns: ["build/", "dist/"],
16+
roots: ["<rootDir>/src/"],
17+
setupFilesAfterEnv: ["<rootDir>/matchers/index.ts"],
18+
snapshotFormat: {
19+
printBasicPrototype: true,
20+
printFunctionName: true,
21+
},
22+
testPathIgnorePatterns: [
23+
"/node_modules/",
24+
"/build/",
25+
"/dist/",
26+
"/scripts/",
27+
"/build-plugins/",
28+
"\\.snap$",
29+
],
630
transform: {
31+
/**
32+
* @type {[string, TsJestOptions]}
33+
*/
734
"\\.ts$": [
8-
"ts-jest",
35+
require.resolve("ts-jest"),
936
{
10-
// isolatedModules: true,
37+
isolatedModules: true,
1138
},
1239
],
1340
},
41+
watchPathIgnorePatterns: ["coverage"],
42+
watchPlugins: [
43+
require.resolve("jest-watch-typeahead/filename"),
44+
require.resolve("jest-watch-typeahead/testname"),
45+
],
1446
};

matchers/index.ts

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { expect } from "@jest/globals";
2+
import {
3+
EXPECTED_COLOR,
4+
RECEIVED_COLOR,
5+
ensureNoExpected,
6+
matcherErrorMessage,
7+
matcherHint,
8+
printReceived,
9+
} from "jest-matcher-utils";
10+
11+
import { BigDecimal } from "../src/BigDecimal";
12+
13+
import { printWithType } from "./print";
14+
15+
import type { DecimalValue } from "../src/BigDecimal";
16+
import type { MatcherHintOptions } from "jest-matcher-utils";
17+
18+
expect.extend({
19+
toEqualDecimal(received: unknown, expected: DecimalValue) {
20+
const matcherName = "toEqualDecimal";
21+
const options: MatcherHintOptions = {
22+
isNot: this.isNot,
23+
promise: this.promise,
24+
};
25+
expected = new BigDecimal(expected);
26+
27+
if (!(received instanceof BigDecimal)) {
28+
return {
29+
pass: false,
30+
message: () =>
31+
matcherErrorMessage(
32+
matcherHint(matcherName, undefined, undefined, options),
33+
`${RECEIVED_COLOR("received")} value must be a BigDecimal`,
34+
printWithType("Received", received, printReceived),
35+
),
36+
};
37+
}
38+
39+
const pass = received.eq(expected);
40+
const message = pass
41+
? () =>
42+
// eslint-disable-next-line prefer-template
43+
matcherHint(matcherName, undefined, undefined, options) +
44+
"\n\n" +
45+
`Expected: ${EXPECTED_COLOR(expected)}`
46+
: () =>
47+
// eslint-disable-next-line prefer-template
48+
matcherHint(matcherName, undefined, undefined, options) +
49+
"\n\n" +
50+
`Expected: ${EXPECTED_COLOR(expected)}\n` +
51+
`Received: ${RECEIVED_COLOR(received)}`;
52+
53+
return { pass, message };
54+
},
55+
56+
toBeEmpty(received: unknown, expected: void) {
57+
const matcherName = "toBeEmpty";
58+
const options: MatcherHintOptions = {
59+
isNot: this.isNot,
60+
promise: this.promise,
61+
};
62+
ensureNoExpected(expected, matcherName, options);
63+
64+
if (
65+
received === null ||
66+
received === undefined ||
67+
typeof (received as never)[Symbol.iterator] !== "function"
68+
) {
69+
return {
70+
pass: false,
71+
message: () =>
72+
matcherErrorMessage(
73+
matcherHint(matcherName, undefined, undefined, options),
74+
`${RECEIVED_COLOR("received")} value must be an iterable`,
75+
printWithType("Received", received, printReceived),
76+
),
77+
};
78+
}
79+
80+
const pass = (received as Iterable<unknown>)[Symbol.iterator]().next().done === true;
81+
const message = pass
82+
? () =>
83+
// eslint-disable-next-line prefer-template
84+
matcherHint(matcherName, undefined, undefined, options) +
85+
"\n\n" +
86+
`Expected ${RECEIVED_COLOR("received")} to not be empty`
87+
: () =>
88+
// eslint-disable-next-line prefer-template
89+
matcherHint(matcherName, undefined, undefined, options) +
90+
"\n\n" +
91+
`Received: ${printReceived(received)}`;
92+
93+
return { pass, message };
94+
},
95+
});

matchers/print.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function printWithType<T>(name: string, value: T, print: (value: T) => string): string {
2+
const type = typeof value;
3+
const hasType =
4+
value !== null && value !== undefined
5+
? `${name} has type: ${type === "object" ? value.constructor?.name ?? type : type}\n`
6+
: "";
7+
const hasValue = `${name} has value: ${print(value)}`;
8+
return hasType + hasValue;
9+
}

matchers/types.d.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { DecimalValue } from "../src/BigDecimal";
2+
3+
interface CustomMatchers<R = unknown> {
4+
/**
5+
* Used to check that an iterable is empty.
6+
*/
7+
toBeEmpty(): R;
8+
/**
9+
* Used to check that a `BigDecimal` has the expected value.
10+
*/
11+
toEqualDecimal(expected: DecimalValue): R;
12+
}
13+
14+
declare global {
15+
/* eslint-disable @typescript-eslint/no-empty-interface */
16+
namespace jest {
17+
interface Expect extends CustomMatchers {}
18+
interface Matchers<R> extends CustomMatchers<R> {}
19+
interface InverseAsymmetricMatchers extends CustomMatchers {}
20+
}
21+
/* eslint-enable @typescript-eslint/no-empty-interface */
22+
}

package.json

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"name": "@juici/bigmath",
2+
"name": "@juici/math",
33
"version": "0.1.0",
4-
"description": "A big math utility library",
4+
"description": "A mathematics utility library",
55
"license": "(MIT OR Apache-2.0)",
66
"files": [
77
"LICENSE-APACHE",
@@ -11,7 +11,7 @@
1111
],
1212
"repository": {
1313
"type": "git",
14-
"url": "https://github.com/Juici/bigmath.git"
14+
"url": "https://github.com/Juici/math.js.git"
1515
},
1616
"types": "dist/index.d.ts",
1717
"main": "dist/cjs/index.js",
@@ -31,13 +31,14 @@
3131
"./package.json": "./package.json"
3232
},
3333
"scripts": {
34-
"lint": "eslint . --cache --ext cjs,mjs,ts,md",
35-
"test": "yarn lint && jest",
3634
"build": "rimraf dist && yarn build:ts && yarn build:types",
3735
"build:ts": "rollup --config rollup.config.mjs",
3836
"build:types": "node ./scripts/build-types.mjs",
37+
"coverage": "jest --coverage",
38+
"lint": "eslint . --cache --ext cjs,mjs,ts,md",
3939
"prepare": "yarn build",
40-
"prepublishOnly": "yarn test"
40+
"prepublishOnly": "yarn test",
41+
"test": "yarn lint && jest"
4142
},
4243
"devDependencies": {
4344
"@microsoft/api-extractor": "^7.31.0",
@@ -63,6 +64,8 @@
6364
"execa": "^6.1.0",
6465
"graceful-fs": "^4.2.10",
6566
"jest": "^29.0.2",
67+
"jest-matcher-utils": "^29.0.3",
68+
"jest-watch-typeahead": "^2.2.0",
6669
"pkg-dir": "^7.0.0",
6770
"prettier": "^2.7.1",
6871
"pretty-ms": "^8.0.0",

scripts/build-types.mjs

+6-30
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,8 @@ const prettierConfig = await prettier.resolveConfig(__filename);
3838

3939
// Compile TypeScript definition files.
4040
{
41-
const compilerOptions = await parseCompilerOptions(tsconfig);
42-
4341
const program = ts.createProgram([inputTsFile], {
44-
...compilerOptions,
42+
...tsconfig.options,
4543
noEmit: false,
4644
declaration: true,
4745
emitDeclarationOnly: true,
@@ -80,11 +78,7 @@ const prettierConfig = await prettier.resolveConfig(__filename);
8078
bundledPackages: [],
8179
newlineKind: "lf",
8280
compiler: {
83-
overrideTsconfig: deepmerge(tsconfig, {
84-
compilerOptions: {
85-
skipLibCheck: true,
86-
},
87-
}),
81+
tsconfigFilePath: path.resolve(root, "tsconfig.api.json"),
8882
},
8983
dtsRollup: {
9084
enabled: true,
@@ -170,35 +164,17 @@ async function removeBuildDir() {
170164

171165
/**
172166
* @param {string} file
167+
* @return {ts.ParsedCommandLine}
173168
*/
174169
async function readTsConfig(file) {
175-
const configJson = await fs.promises.readFile(file, "utf-8");
176-
177-
const { config, error } = ts.parseConfigFileTextToJson(file, configJson);
178-
if (error && emitDiagnostics(error)) {
170+
const configJson = await ts.readJsonConfigFile(file, (path) => fs.readFileSync(path, "utf-8"));
171+
const config = ts.parseJsonSourceFileConfigFileContent(configJson, ts.sys, root);
172+
if (emitDiagnostics(config.errors)) {
179173
process.exit(1);
180174
}
181-
182175
return config;
183176
}
184177

185-
/**
186-
* @param {string} file
187-
* @return {ts.CompilerOptions}
188-
*/
189-
async function parseCompilerOptions(config, file = tsconfigFile) {
190-
const { options, errors } = ts.convertCompilerOptionsFromJson(
191-
config?.compilerOptions ?? {},
192-
root,
193-
file,
194-
);
195-
if (emitDiagnostics(errors)) {
196-
process.exit(1);
197-
}
198-
199-
return options;
200-
}
201-
202178
/**
203179
* @param {ts.Diagnostic | Array<ts.Diagnostic>} diagnostics
204180
* @returns {boolean}n

0 commit comments

Comments
 (0)