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

TS error when using vitest globals and @testing-library/jest-dom #427

Closed
robcaldecott opened this issue Jan 13, 2022 · 25 comments · Fixed by #511
Closed

TS error when using vitest globals and @testing-library/jest-dom #427

robcaldecott opened this issue Jan 13, 2022 · 25 comments · Fixed by #511
Labels
BREAKING CHANGE This change will require a major version bump bug Something isn't working released

Comments

@robcaldecott
Copy link

  • @testing-library/jest-dom version: 5.16.1
  • node version: 14.17.0
  • yarn version: 1.22.17
  • @testing-library/react version: 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
@sheremet-va
Copy link

sheremet-va commented Jan 14, 2022

@dancras
Copy link

dancras commented Feb 11, 2022

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;
    });
});

@larsenwork
Copy link

@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",
  }

@alamo42
Copy link

alamo42 commented Mar 12, 2022

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;
  });
});

@larsenwork
Copy link

larsenwork commented Mar 14, 2022

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 ☝️

@laruiss
Copy link

laruiss commented Mar 17, 2022

@larsenwork Would you be so kind as to share your tsconfig.*.json?
I am still struggling with this 😕

@BPreisner
Copy link

@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"],
  },
}

@airjp73
Copy link

airjp73 commented May 5, 2022

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> {}
  }
}

@cmacdonnacha
Copy link

Not sure if this is the correct solution but adding "skipLibCheck": true, in my tsconfig.json fixed it.

@edumudu
Copy link

edumudu commented Jun 29, 2022

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

@jsjoeio
Copy link

jsjoeio commented Jul 4, 2022

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

@seivan
Copy link

seivan commented Jul 6, 2022

@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.

@zoontek
Copy link

zoontek commented Jul 7, 2022

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);

@kaorun343
Copy link

kaorun343 commented Aug 16, 2022

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.

@nickserv
Copy link
Member

nickserv commented Sep 19, 2022

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.

@nickserv nickserv added bug Something isn't working BREAKING CHANGE This change will require a major version bump labels Sep 19, 2022
@AndrewLeedham
Copy link

Would the workaround here work for this? #439

@nickserv
Copy link
Member

Are you suggesting we document this as a temporary workaround for Vitest users, or expose a Vitest entry point permanently?

@AndrewLeedham
Copy link

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: []?

@elliottsj
Copy link

Maybe the inverse would be a cleaner solution, where we provide jest.d.ts and vitest.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: #439 (comment)

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);

@haikyuu
Copy link

haikyuu commented Oct 21, 2022

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"],
}

@typeofweb
Copy link

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.

@edufarre
Copy link

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)

@madmadi
Copy link

madmadi commented May 28, 2023

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.

@merlinstardust
Copy link

@github-actions
Copy link

🎉 This issue has been resolved in version 6.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BREAKING CHANGE This change will require a major version bump bug Something isn't working released
Projects
None yet