Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: replace mlh-tsd with tsd-lite #41

Merged
merged 19 commits into from Feb 4, 2022
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions .eslintignore
@@ -0,0 +1,4 @@
.yarn
build
coverage
e2e/__fixtures__/errors-syntax
21 changes: 21 additions & 0 deletions LICENSE.md
@@ -0,0 +1,21 @@
MIT License

Copyright (c) Jest Community

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
102 changes: 54 additions & 48 deletions README.md
@@ -1,84 +1,90 @@
# `jest-runner-tsd`
# jest-runner-tsd
mrazauskas marked this conversation as resolved.
Show resolved Hide resolved

A Jest runner that tests typescript typings using [tsd](https://github.com/SamVerschueren/tsd) under the hood.
> Run your TypeScript type tests using Jest.

## Install
[![version](https://img.shields.io/npm/v/jest-runner-tsd.svg)](https://npmjs.com/package/jest-runner-tsd)
[![license](https://img.shields.io/github/license/jest-community/jest-runner-tsd.svg)](https://github.com/jest-community/jest-runner-tsd/blob/main/LICENSE.md)

Install `jest-runner-tsd`
**Note:** the `jest-runner-tsd` is using [`tsd-lite`](https://github.com/mrazauskas/tsd-lite) instead of [`tsd`](https://github.com/SamVerschueren/tsd). Both of them have the same type testing logic, but `tsd-light` makes it easier to test projects written in TypeScript (or types generated by your library).

```bash
yarn add --dev jest-runner-tsd
Most important differences (for the full list see [`tsd-lite` repo](https://github.com/mrazauskas/tsd-lite)):

- `tsd-lite` has no additional [rules](https://github.com/SamVerschueren/tsd/issues/32) or checks;
- `jest.config` is used to discover test files;
- and `tsconfig.json` provides configuration for TS compiler. For details see [Configuration](#configuration) section.

# or with NPM
## Install

npm install --save-dev jest-runner-tsd
```bash
yarn add -D jest-runner-tsd @tsd/typescript
mrazauskas marked this conversation as resolved.
Show resolved Hide resolved
SimenB marked this conversation as resolved.
Show resolved Hide resolved
```

### Adding to Jest Config
Remember to install `@tsd/typescript` package. It is a required peer dependency.
mrazauskas marked this conversation as resolved.
Show resolved Hide resolved

## Configuration

Create a `jest.config.types.js` file and have the runner property set to `jest-runner-tsd` as shown below:
### Jest

First of all, you should [configure Jest](https://jestjs.io/docs/configuration) to discover your test files. For example, if the files are suffix with `.test.ts` and live inside `__typetests__` directory, set up `jest.config.tsd.js` like this:
mrazauskas marked this conversation as resolved.
Show resolved Hide resolved

```js
module.exports = {
displayName: {
color: 'blue',
name: 'types',
},
runner: 'jest-runner-tsd',
testMatch: ['**/__typetests__/*.test.ts'],
};
```

In the project `package.json` file, modify the scripts block to use the configuration file as show below:
### TS Compiler

```json
...
"scripts": {
...
"type-tests": "yarn jest --config jest.config.types.js"
}
...
```
Your test files will be compiled using the [TypeScript compiler](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API) (similarly to `tsc`), but, instead of emitting JS code, `tsd-lite` will analyze types and diagnostics returned by the compiler.
mrazauskas marked this conversation as resolved.
Show resolved Hide resolved

### Run
To compile each test file, `tsd-lite` will read the nearest [`tsconfig.json`](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) and will pass the configuration to the compiler. Hence, you may have a configuration for the whole project, or a group of test files, or just a particular test file.

To start the test, just execute the following command
For example, if your project already includes a `tsconfig.json` in the root directory, but you prefer to have different configuration for testing, simply add another `tsconfig.json` to a directory with the test files. It may override or extend your root configuration.

```bash
yarn test-types
```

## Writing tests
**Hint:** run `yarn tsc -p path/to/__typetests__ --showConfig` to print the configuration which applies to the test files.

> This runner uses TSD. To see the available assertions, checkout it's [documentation](https://github.com/SamVerschueren/tsd)
**Note:** if `tsconfig.json` is not found, the compiler will fall back to the default configuration.

### For JavaScript Projects
### Optionally Strict

There are multiple ways you can pass a type definition file.
Just like TypeScript, `tsd-lite` is [optionally strict](https://www.typescriptlang.org/docs/handbook/2/basic-types.html#strictness). In contrary, the vanilla `tsd` is strict by default. If you are migrating your test suite, remember to set `"strict": true` in `tsconfig.json`.

#### Default
```ts
import { expectType } from 'tsd-lite';

The type definitions should be in a file named `index.d.ts` in the root directory of the project by default.
declare const loggedInUsername: string;

#### `types` property in package.json
const users = [
{ name: 'Oby', age: 12 },
{ name: 'Heera', age: 32 },
];

You can also set your `types` property in package.json. The runner will automatically pick the type defintion file from there.
const loggedInUser = users.find(u => u.name === loggedInUsername);

```json
{
...
"types": "path/to/types.d.ts"
}
expectType<number>(loggedInUser.age);
```

#### Docblocks
The assertion in this example fails with `"strict": true`, but passes with `"strict": false`.

If the type definition file is located somewhere else then specify its path in the top of respective test file using the `@type` inside a docblock.
## Running Tests

```ts
/**
* @type ../../custom/path/to/types.d.ts
**/
If all is set, simply run `yarn jest -c jest.config.tsd.js` command. Or better include a script in `package.json`:

```json
"scripts": {
"test:types": "jest -c jest.config.tsd.js"
}
```

### For TypeScript Projects
## Learn More

To learn more about `tsd` and its assertions see the [documentation](https://github.com/SamVerschueren/tsd).

> **Note:** This is only a workaround. A stable solution may be introduced in future.
## License

Due to [limitations in TSD](https://github.com/SamVerschueren/tsd/issues/32), the only solution now for testing types in TypeScript projects
would be to have a empty type definition file and specify it's path using one of the many methods explained above.
[MIT](https://github.com/jest-community/jest-runner-tsd/blob/main/LICENSE.md) © Jest Community
7 changes: 0 additions & 7 deletions e2e/__fixtures__/docblock/concat.d.ts

This file was deleted.

1 change: 0 additions & 1 deletion e2e/__fixtures__/docblock/concat.js

This file was deleted.

9 changes: 0 additions & 9 deletions e2e/__fixtures__/docblock/concat.test.ts

This file was deleted.

7 changes: 7 additions & 0 deletions e2e/__fixtures__/errors-syntax/index.d.ts
@@ -0,0 +1,7 @@
/* eslint-disable no-unused-vars */
declare const one: {
(foo: string, bar: string): string;
(foo: number, bar: number): number;
};

export default one;
3 changes: 3 additions & 0 deletions e2e/__fixtures__/errors-syntax/index.js
@@ -0,0 +1,3 @@
module.exports.default = (foo, bar) => {
return foo + bar;
};
5 changes: 5 additions & 0 deletions e2e/__fixtures__/errors-syntax/index.test.ts
@@ -0,0 +1,5 @@
import { expectError } from 'tsd-lite';
import one from '.';

expectError(one('foo', 'bar');
expectError(one('foo' 'bar'));
8 changes: 8 additions & 0 deletions e2e/__fixtures__/errors-syntax/package.json
@@ -0,0 +1,8 @@
{
"jest": {
"runner": "../../../src/",
"testMatch": [
"**/*.test.ts"
]
}
}
3 changes: 3 additions & 0 deletions e2e/__fixtures__/errors-tsconfig/index.test.ts
@@ -0,0 +1,3 @@
import { expectType } from 'tsd-lite';

expectType<string>('dummy test');
8 changes: 8 additions & 0 deletions e2e/__fixtures__/errors-tsconfig/package.json
@@ -0,0 +1,8 @@
{
"jest": {
"runner": "../../../src/",
"testMatch": [
"**/*.test.ts"
]
}
}
9 changes: 9 additions & 0 deletions e2e/__fixtures__/errors-tsconfig/tsconfig.json
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2019",
"strict": "yes",
"noEmitOnError": true
},
"include": ["./**/*"]
}
@@ -1,4 +1,4 @@
import { expectType } from 'mlh-tsd';
import { expectType } from 'tsd-lite';
import concat from '.';

expectType<string>(concat('pre', 'fix'));
Expand Down
@@ -1,4 +1,4 @@
import { expectType } from 'mlh-tsd';
import { expectType } from 'tsd-lite';
import concat from '.';

expectType<string>(concat('pre', 'fix'));
Expand Down
14 changes: 14 additions & 0 deletions e2e/__fixtures__/override-tsconfig/index.test.ts
@@ -0,0 +1,14 @@
// Adapted from: https://www.typescriptlang.org/tsconfig#strictNullChecks

import { expectType } from 'tsd-lite';

declare const loggedInUsername: string;

const users = [
{ name: 'Oby', age: 12 },
{ name: 'Heera', age: 32 },
];

const loggedInUser = users.find(u => u.name === loggedInUsername);

expectType<number>(loggedInUser.age);
7 changes: 7 additions & 0 deletions e2e/__fixtures__/override-tsconfig/tsconfig.json
@@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"strict": false
},
"include": ["**/*"]
}
7 changes: 0 additions & 7 deletions e2e/__fixtures__/pkg-types/types.d.ts

This file was deleted.

5 changes: 0 additions & 5 deletions e2e/__fixtures__/pkg-types/types.test.ts

This file was deleted.

8 changes: 8 additions & 0 deletions e2e/__fixtures__/ts-failing/index.test.ts
@@ -0,0 +1,8 @@
import { expectError, expectType } from 'tsd-lite';
import { makeDate } from '.';

expectType<Date>(makeDate(12345678));
expectType<string>(makeDate(5, 5, 5));

expectError(makeDate(1, 3, 6));
expectError(makeDate(1, 3));
13 changes: 13 additions & 0 deletions e2e/__fixtures__/ts-failing/index.ts
@@ -0,0 +1,13 @@
/* eslint-disable no-unused-vars */

function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
if (d !== undefined && y !== undefined) {
return new Date(y, mOrTimestamp, d);
} else {
return new Date(mOrTimestamp);
}
}

export { makeDate };
@@ -1,5 +1,4 @@
{
"types": "./types.d.ts",
"jest": {
"runner": "../../../src/",
"testMatch": ["**/*.test.ts"]
Expand Down
12 changes: 12 additions & 0 deletions e2e/__fixtures__/ts-passing/index.test.ts
@@ -0,0 +1,12 @@
import { expectError, expectType } from 'tsd-lite';

interface SomeType {
readonly prop: string;
}

function doSomething(obj: SomeType) {
expectError((obj.prop = 'hello'));
return obj.prop;
}

expectType<string>(doSomething({ prop: 'val' }));
6 changes: 6 additions & 0 deletions e2e/__fixtures__/ts-passing/package.json
@@ -0,0 +1,6 @@
{
"jest": {
"runner": "../../../src/",
"testMatch": ["**/*.test.ts"]
}
}
12 changes: 0 additions & 12 deletions e2e/__snapshots__/docblock.test.ts.snap

This file was deleted.

40 changes: 40 additions & 0 deletions e2e/__snapshots__/errors.test.ts.snap
@@ -0,0 +1,40 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`throws if nearest tsconfig.json is not valid 1`] = `
"FAIL e2e/__fixtures__/errors-tsconfig/index.test.ts
● Test suite failed to run
ConfigError: Compiler option 'strict' requires a value of type boolean.
3 | \\"module\\": \\"commonjs\\",
4 | \\"target\\": \\"es2019\\",
> 5 | \\"strict\\": \\"yes\\",
| ^
6 | \\"noEmitOnError\\": true
7 | },
8 | \\"include\\": [\\"./**/*\\"]
at tsconfig.json:5:15
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time:
Ran all test suites.
"
`;

exports[`throws if syntax error is encountered 1`] = `
"FAIL e2e/__fixtures__/errors-syntax/index.test.ts
● Test suite failed to run
SyntaxError: ')' expected.
2 | import one from '.';
3 |
> 4 | expectError(one('foo', 'bar');
| ^
5 | expectError(one('foo' 'bar'));
6 |
at index.test.ts:4:30
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time:
Ran all test suites.
"
`;