jest-dom
jest-dom copied to clipboard
TS error when using vitest globals and @testing-library/jest-dom
@testing-library/jest-domversion: 5.16.1nodeversion: 14.17.0yarnversion: 1.22.17@testing-library/reactversion: 12.1.2
I am using vitest instead of jest for testing a React component lib in a monorepo. I am using @testing-library/jest-dom and @testing-library/react and both happily work with vitest.
However, when type checking my code I am seeing a clash between @types/jest and vitest. The @types/jest dependency seems to have been pulled in by @testing-library/jest-dom.
../node_modules/@types/jest/index.d.ts:34:1 - error TS6200: Definitions of the following identifiers conflict with those in another file: test, describe, it, expect, beforeAll, afterAll, beforeEach, afterEach
34 declare var beforeAll: jest.Lifecycle;
~~~~~~~
../node_modules/vitest/global.d.ts:1:1
1 declare global {
~~~~~~~
Conflicts are in this file.
../node_modules/vitest/global.d.ts:1:1 - error TS6200: Definitions of the following identifiers conflict with those in another file: test, describe, it, expect, beforeAll, afterAll, beforeEach, afterEach
1 declare global {
~~~~~~~
../node_modules/@types/jest/index.d.ts:34:1
34 declare var beforeAll: jest.Lifecycle;
~~~~~~~
Conflicts are in this file.
Found 2 errors.
It would be great to have a workaround for this issue so the jest types can be removed or ignored.
To reproduce this issue clone the following project (notice it uses a branch called yarn):
https://github.com/robcaldecott/pnpm-vite-monorepo-example/tree/yarn
To see the error:
yarn install
cd components
yarn build
Related:
- https://github.com/vitest-dev/vitest/issues/517
Simply removing the following line from the types definition solves it and doesn't impact the functionality from my brief testing.
/// <reference types="jest" />
If anyone wants a hacky and crude solution in the meantime:
package.json
"type": "module",
"scripts": {
"build": "node patchJestDom.js && %previous_build_commands%",
}
patchJestDom.js
import fs from 'fs';
import path from 'path';
const typesPath = path.resolve('node_modules', '@types', 'testing-library__jest-dom', 'index.d.ts');
fs.readFile(typesPath, 'utf8', (err, data) => {
if (err) throw err;
let lines = data.split('\n');
if (lines[8] === '/// <reference types="jest" />') {
lines = lines.slice(0, 8).concat(lines.slice(9));
}
fs.writeFile(typesPath, lines.join('\n'), 'utf8', function(err) {
if (err) throw err;
});
});
@dancras thanks for the hack, currently using it too but added to postinstall lifecycle script to have it run on both https://docs.npmjs.com/cli/v8/using-npm/scripts#npm-ci and https://docs.npmjs.com/cli/v8/using-npm/scripts#npm-install
"scripts": {
...existing scripts here,
"postinstall": "node scripts/patchJestDom.js",
}
Thanks @dancras, your solution worked! I had to modify it because my reference to jest was on line 10, so I ended up making it to recognize any line:
const fs = require("fs");
const path = require("path");
const typesPath = path.resolve(
"node_modules",
"@types",
"testing-library__jest-dom",
"index.d.ts"
);
fs.readFile(typesPath, "utf8", (err, data) => {
if (err) throw err;
let lines = data.split("\n");
jestTypesIndex = lines.findIndex((line) =>
line.includes('reference types="jest"')
);
if (lines[jestTypesIndex] === '/// <reference types="jest" />') {
lines = lines
.slice(0, jestTypesIndex)
.concat(lines.slice(jestTypesIndex + 1));
}
fs.writeFile(typesPath, lines.join("\n"), "utf8", function (err) {
if (err) throw err;
});
});
FWIW this works just fine too. (I tend not to care about throwing errors because they are of limited value in a build step like this)
import fs from 'fs'
import path from 'path'
const typesFile = path.resolve('node_modules/@types/testing-library__jest-dom/index.d.ts')
const encoding = 'utf8'
const strings = {
search: '/// <reference types="jest" />',
replace: '// See https://github.com/testing-library/jest-dom/issues/427 for reference',
}
const result = fs
.readFileSync(typesFile, encoding)
.replace(strings.search, strings.replace)
fs.writeFileSync(typesFile, result, encoding)
EDIT: my vite.config.js and tsconfig.json for reference. @laruiss ☝️
@larsenwork Would you be so kind as to share your tsconfig.*.json?
I am still struggling with this 😕
@gnapse is there any possibility to remove /// <reference types="jest" /> from library types and make @types/jest a peerDependency?
Anyway, I found a slightly less hacky workaround 💯 :
setupTests.ts (file which goes to test.setupFiles in vite.config.ts)
import '@testing-library/jest-dom/extend-expect';
global.d.ts (file placed in root or inside src directory)
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { TestingLibraryMatchers } from '@testing-library/jest-dom/matchers';
declare global {
namespace jest {
type Matchers<R = void, T = {}> = TestingLibraryMatchers<
typeof expect.stringContaining,
R
>;
}
}
tsconfig.json
{
"compilerOptions": {
...
"types": ["node", "vite/client", "vitest/globals"],
},
}
Tried the workaround above, but had to tweak one thing. In global.d.ts I had to write this instead:
import type { TestingLibraryMatchers } from "@testing-library/jest-dom/matchers";
declare global {
namespace jest {
interface Matchers<R = void>
extends TestingLibraryMatchers<typeof expect.stringContaining, R> {}
}
}
Not sure if this is the correct solution but adding "skipLibCheck": true, in my tsconfig.json fixed it.
for me, using [email protected] and @testing-library/[email protected] only the solution bellow worked
// global.d.ts
import type { TestingLibraryMatchers } from '@testing-library/jest-dom/matchers';
type CustomMatchers<R = unknown> = TestingLibraryMatchers<typeof expect.stringContaining, R>;
declare global {
namespace Vi {
interface Assertion extends CustomMatchers {}
interface AsymmetricMatchersContaining extends CustomMatchers {}
}
}
and using pnpm the code bellow need to be placed in the .npmrc to be able to use the TestingLibraryMatchers types outside node_modules
shamefully-hoist = true
Combining @BPreisner and @airjp73's solutions, I made a new template to save myself (and others) future time: https://github.com/jsjoeio/react-ts-vitest-template
@BPreisner Out of curiosity, would this have worked on its own?
"compilerOptions": {
"typeRoots": [],
"types": [],
}
I know it doesn't fix the root problem of them bundling the types.
But am wondering ( can't test it right now) if augmenting the jest namespace would be necessary at all if types settings are set to not include by default.
I made a simple solution to fix this issue: https://github.com/zoontek/types-testing-library-vitest-dom
Installation (yarn)
"resolutions": {
"@types/testing-library__jest-dom": "github:zoontek/types-testing-library-vitest-dom"
}
Installation (npm)
"overrides": {
"@types/testing-library__jest-dom": "github:zoontek/types-testing-library-vitest-dom"
}
Test setup file
import matchers from "@testing-library/jest-dom/matchers";
import { expect } from "vitest";
expect.extend(matchers);
Hi.
How about providing @testing-library/vitest-dom ?
Since the release of Vitest, the number of downloads of Vitest has increased.
I think the official support for using Testing Library with Vitest makes us very happy and we don't have to add ad-hoc solutions.
I don't think making a new package is worth the cognitive/maintenance overhead when we could just make this package more framework agnostic for now.
However, I vaguely remember another maintainer planning on refactoring matchers out of this package. Please let me know if there's an update on that. I couldn't find the source, but I don't want to step on any toes accidentally.
Would the workaround here work for this? https://github.com/testing-library/jest-dom/issues/439
Are you suggesting we document this as a temporary workaround for Vitest users, or expose a Vitest entry point permanently?
Are you suggesting we document this as a temporary workaround for Vitest users, or expose a Vitest entry point permanently?
Currently a workaround, I can see why the requirement of types: [] would be a pain, I was already using that to get around Cypress conflicts, but not an ideal solution. Maybe the inverse would be a cleaner solution, where we provide jest.d.ts and vitest.d.ts and the matchers are just exported from the index, so you have to do something like /// <reference types="@testing-library/jest-dom/vitest" /> in your jest-setup.ts script instead of excluding the index with types: []?
Maybe the inverse would be a cleaner solution, where we provide
jest.d.tsandvitest.d.ts
I like this approach as well.
For now, I am using a workaround with pnpm patch, creating this diff to empty the definitions in @types/testing-library__jest-dom/index.d.ts:
diff --git a/index.d.ts b/index.d.ts
index 43ba6b7fe458e77d152fe0b2f7afeac05d8fc563..dfa540aa7d1ab8c78c31104d3210eee3c33a94c0 100755
--- a/index.d.ts
+++ b/index.d.ts
@@ -7,12 +7,9 @@
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// Minimum TypeScript Version: 4.3
-/// <reference types="jest" />
+// NOTE: these definitions are left blank, to support using Testing Library Jest matchers with
+// Vitest, without having to use Jest's global types (for `expect`, etc).
-import { TestingLibraryMatchers } from './matchers';
+// Vitest's `expect` type is instead extended in `src/setupTests.ts`.
-declare global {
- namespace jest {
- interface Matchers<R = void, T = {}> extends TestingLibraryMatchers<typeof expect.stringContaining, R> {}
- }
-}
+// For more context, see https://github.com/testing-library/jest-dom/issues/427
In patches/@[email protected], with this in package.json:
"pnpm": {
"patchedDependencies": {
"@types/[email protected]": "patches/@[email protected]"
}
}
Paired with this in my Vitest setup file: https://github.com/testing-library/jest-dom/issues/439#issuecomment-1087504347
import matchers, {
TestingLibraryMatchers,
} from '@testing-library/jest-dom/matchers';
declare global {
namespace Vi {
interface JestAssertion<T = any>
extends jest.Matchers<void, T>,
TestingLibraryMatchers<T, void> {}
}
}
expect.extend(matchers);
The workaround (hack 😆) that worked for me was to copy globals.d.ts from vitest to my global.d.ts file
declare global {
const suite: typeof import('vitest')['suite']
const test: typeof import('vitest')['test']
const describe: typeof import('vitest')['describe']
const it: typeof import('vitest')['it']
const expect: typeof import('vitest')['expect']
const assert: typeof import('vitest')['assert']
const vitest: typeof import('vitest')['vitest']
const vi: typeof import('vitest')['vitest']
const beforeAll: typeof import('vitest')['beforeAll']
const afterAll: typeof import('vitest')['afterAll']
const beforeEach: typeof import('vitest')['beforeEach']
const afterEach: typeof import('vitest')['afterEach']
}
And adding the following to compilerOptions in my tsconfig:
"paths": {
"@jest/*": ["NOT_FOUND"],
"jest/*": ["NOT_FOUND"],
}
Solution for pnpm users:
Add this to your package.json:
"pnpm": {
"patchedDependencies": {
"@types/[email protected]": "patches/@[email protected]"
}
}
Now, create a file patches/@[email protected] with the following contents:
diff --git a/index.d.ts b/index.d.ts
index 43ba6b7fe458e77d152fe0b2f7afeac05d8fc563..3bf91587abf21f15ac166bcae1f5a59b23884b87 100755
--- a/index.d.ts
+++ b/index.d.ts
@@ -7,7 +7,7 @@
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// Minimum TypeScript Version: 4.3
-/// <reference types="jest" />
+// See https://github.com/testing-library/jest-dom/issues/427 for reference
import { TestingLibraryMatchers } from './matchers';
Save both and run pnpm i.
Do you have any updates on the status of this issue? For now, the approach I've followed is the one mentioned into another issue (which targets the latest version of Vite)
Another solution if you don't mind to install a new package is to install vitest-dom. no hack or workaround and it works straightforward with pnpm too. It is not part of testing-library though.
I'm also seeing this issue on a brand new project
https://github.com/merlinstardust/coverage-zero-vitest-example/actions/runs/5384546869/jobs/9772635711#step:7:20
:tada: This issue has been resolved in version 6.0.0 :tada:
The release is available on:
Your semantic-release bot :package::rocket: