Skip to content

Commit bdb34f1

Browse files
kryopsjgoz
andauthored
fix: matchers type is making the global expect unsafe (#513)
* fix: matchers type is making the global expect unsafe * Add test file for TypeScript typings * Remove jest specifics from matchers.d.ts * Type tests for all test environments * Fix all AsymmetricMatcher interfaces * Ignore type tests from eslint --------- Co-authored-by: Michael Manzinger <[email protected]> Co-authored-by: John Gozde <[email protected]>
1 parent 4b764b9 commit bdb34f1

15 files changed

+694
-8
lines changed

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"setup": "npm install && npm run validate -s",
1717
"test": "kcd-scripts test",
1818
"test:update": "npm test -- --updateSnapshot --coverage",
19-
"validate": "kcd-scripts validate"
19+
"test:types": "tsc -p types/__tests__/jest && tsc -p types/__tests__/jest-globals && tsc -p types/__tests__/vitest",
20+
"validate": "kcd-scripts validate && npm run test:types"
2021
},
2122
"files": [
2223
"dist",
@@ -110,7 +111,8 @@
110111
"eslintIgnore": [
111112
"node_modules",
112113
"coverage",
113-
"dist"
114+
"dist",
115+
"types/__tests__"
114116
],
115117
"repository": {
116118
"type": "git",

tsconfig.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
{
22
"compilerOptions": {
3+
"noEmit": true,
34
"strict": true,
45
"skipLibCheck": true
56
},
6-
"include": ["*.d.ts", "types"]
7+
"include": ["*.d.ts", "types"],
8+
"exclude": ["types/__tests__"]
79
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* File that tests whether the TypeScript typings work as expected.
3+
*/
4+
5+
/* eslint-disable @typescript-eslint/no-unsafe-call */
6+
/* eslint-disable @typescript-eslint/no-floating-promises */
7+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
8+
9+
import {expect} from '@jest/globals'
10+
import * as matchers from '../../matchers'
11+
12+
expect.extend(matchers)
13+
14+
const element: HTMLElement = document.body
15+
16+
function customExpect(
17+
_actual: HTMLElement,
18+
):
19+
| matchers.TestingLibraryMatchers<unknown, void>
20+
| matchers.TestingLibraryMatchers<unknown, Promise<void>> {
21+
throw new Error('Method not implemented.')
22+
}
23+
24+
customExpect(element).toBeInTheDOM()
25+
customExpect(element).toBeInTheDOM(document.body)
26+
customExpect(element).toBeInTheDocument()
27+
customExpect(element).toBeVisible()
28+
customExpect(element).toBeEmpty()
29+
customExpect(element).toBeDisabled()
30+
customExpect(element).toBeEnabled()
31+
customExpect(element).toBeInvalid()
32+
customExpect(element).toBeRequired()
33+
customExpect(element).toBeValid()
34+
customExpect(element).toContainElement(document.body)
35+
customExpect(element).toContainElement(null)
36+
customExpect(element).toContainHTML('body')
37+
customExpect(element).toHaveAttribute('attr')
38+
customExpect(element).toHaveAttribute('attr', true)
39+
customExpect(element).toHaveAttribute('attr', 'yes')
40+
customExpect(element).toHaveClass()
41+
customExpect(element).toHaveClass('cls1')
42+
customExpect(element).toHaveClass('cls1', 'cls2', 'cls3', 'cls4')
43+
customExpect(element).toHaveClass('cls1', {exact: true})
44+
customExpect(element).toHaveDisplayValue('str')
45+
customExpect(element).toHaveDisplayValue(['str1', 'str2'])
46+
customExpect(element).toHaveDisplayValue(/str/)
47+
customExpect(element).toHaveDisplayValue([/str1/, 'str2'])
48+
customExpect(element).toHaveFocus()
49+
customExpect(element).toHaveFormValues({foo: 'bar', baz: 1})
50+
customExpect(element).toHaveStyle('display: block')
51+
customExpect(element).toHaveStyle({display: 'block', width: 100})
52+
customExpect(element).toHaveTextContent('Text')
53+
customExpect(element).toHaveTextContent(/Text/)
54+
customExpect(element).toHaveTextContent('Text', {normalizeWhitespace: true})
55+
customExpect(element).toHaveTextContent(/Text/, {normalizeWhitespace: true})
56+
customExpect(element).toHaveValue()
57+
customExpect(element).toHaveValue('str')
58+
customExpect(element).toHaveValue(['str1', 'str2'])
59+
customExpect(element).toHaveValue(1)
60+
customExpect(element).toHaveValue(null)
61+
customExpect(element).toBeChecked()
62+
customExpect(element).toHaveDescription('some description')
63+
customExpect(element).toHaveDescription(/some description/)
64+
customExpect(element).toHaveDescription(expect.stringContaining('partial'))
65+
customExpect(element).toHaveDescription()
66+
customExpect(element).toHaveAccessibleDescription('some description')
67+
customExpect(element).toHaveAccessibleDescription(/some description/)
68+
customExpect(element).toHaveAccessibleDescription(
69+
expect.stringContaining('partial'),
70+
)
71+
customExpect(element).toHaveAccessibleDescription()
72+
73+
customExpect(element).toHaveAccessibleErrorMessage()
74+
customExpect(element).toHaveAccessibleErrorMessage(
75+
'Invalid time: the time must be between 9:00 AM and 5:00 PM',
76+
)
77+
customExpect(element).toHaveAccessibleErrorMessage(/invalid time/i)
78+
customExpect(element).toHaveAccessibleErrorMessage(
79+
expect.stringContaining('Invalid time'),
80+
)
81+
82+
customExpect(element).toHaveAccessibleName('a label')
83+
customExpect(element).toHaveAccessibleName(/a label/)
84+
customExpect(element).toHaveAccessibleName(
85+
expect.stringContaining('partial label'),
86+
)
87+
customExpect(element).toHaveAccessibleName()
88+
customExpect(element).toHaveErrorMessage(
89+
'Invalid time: the time must be between 9:00 AM and 5:00 PM',
90+
)
91+
customExpect(element).toHaveErrorMessage(/invalid time/i)
92+
customExpect(element).toHaveErrorMessage(
93+
expect.stringContaining('Invalid time'),
94+
)
95+
96+
// @ts-expect-error The types accidentally allowed any property by falling back to "any"
97+
customExpect(element).nonExistentProperty()
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* File that tests whether the TypeScript typings for @types/jest work as expected.
3+
*/
4+
5+
/* eslint-disable @typescript-eslint/no-unsafe-call */
6+
/* eslint-disable @typescript-eslint/no-floating-promises */
7+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
8+
9+
import {expect} from '@jest/globals'
10+
import '../../jest-globals'
11+
12+
const element: HTMLElement = document.body
13+
14+
expect(element).toBeInTheDOM()
15+
expect(element).toBeInTheDOM(document.body)
16+
expect(element).toBeInTheDocument()
17+
expect(element).toBeVisible()
18+
expect(element).toBeEmpty()
19+
expect(element).toBeDisabled()
20+
expect(element).toBeEnabled()
21+
expect(element).toBeInvalid()
22+
expect(element).toBeRequired()
23+
expect(element).toBeValid()
24+
expect(element).toContainElement(document.body)
25+
expect(element).toContainElement(null)
26+
expect(element).toContainHTML('body')
27+
expect(element).toHaveAttribute('attr')
28+
expect(element).toHaveAttribute('attr', true)
29+
expect(element).toHaveAttribute('attr', 'yes')
30+
expect(element).toHaveClass()
31+
expect(element).toHaveClass('cls1')
32+
expect(element).toHaveClass('cls1', 'cls2', 'cls3', 'cls4')
33+
expect(element).toHaveClass('cls1', {exact: true})
34+
expect(element).toHaveDisplayValue('str')
35+
expect(element).toHaveDisplayValue(['str1', 'str2'])
36+
expect(element).toHaveDisplayValue(/str/)
37+
expect(element).toHaveDisplayValue([/str1/, 'str2'])
38+
expect(element).toHaveFocus()
39+
expect(element).toHaveFormValues({foo: 'bar', baz: 1})
40+
expect(element).toHaveStyle('display: block')
41+
expect(element).toHaveStyle({display: 'block', width: 100})
42+
expect(element).toHaveTextContent('Text')
43+
expect(element).toHaveTextContent(/Text/)
44+
expect(element).toHaveTextContent('Text', {normalizeWhitespace: true})
45+
expect(element).toHaveTextContent(/Text/, {normalizeWhitespace: true})
46+
expect(element).toHaveValue()
47+
expect(element).toHaveValue('str')
48+
expect(element).toHaveValue(['str1', 'str2'])
49+
expect(element).toHaveValue(1)
50+
expect(element).toHaveValue(null)
51+
expect(element).toBeChecked()
52+
expect(element).toHaveDescription('some description')
53+
expect(element).toHaveDescription(/some description/)
54+
expect(element).toHaveDescription(expect.stringContaining('partial'))
55+
expect(element).toHaveDescription()
56+
expect(element).toHaveAccessibleDescription('some description')
57+
expect(element).toHaveAccessibleDescription(/some description/)
58+
expect(element).toHaveAccessibleDescription(expect.stringContaining('partial'))
59+
expect(element).toHaveAccessibleDescription()
60+
expect(element).toHaveAccessibleName('a label')
61+
expect(element).toHaveAccessibleName(/a label/)
62+
expect(element).toHaveAccessibleName(expect.stringContaining('partial label'))
63+
expect(element).toHaveAccessibleName()
64+
expect(element).toHaveErrorMessage(
65+
'Invalid time: the time must be between 9:00 AM and 5:00 PM',
66+
)
67+
expect(element).toHaveErrorMessage(/invalid time/i)
68+
expect(element).toHaveErrorMessage(expect.stringContaining('Invalid time'))
69+
70+
expect(element).not.toBeInTheDOM()
71+
expect(element).not.toBeInTheDOM(document.body)
72+
expect(element).not.toBeInTheDocument()
73+
expect(element).not.toBeVisible()
74+
expect(element).not.toBeEmpty()
75+
expect(element).not.toBeEmptyDOMElement()
76+
expect(element).not.toBeDisabled()
77+
expect(element).not.toBeEnabled()
78+
expect(element).not.toBeInvalid()
79+
expect(element).not.toBeRequired()
80+
expect(element).not.toBeValid()
81+
expect(element).not.toContainElement(document.body)
82+
expect(element).not.toContainElement(null)
83+
expect(element).not.toContainHTML('body')
84+
expect(element).not.toHaveAttribute('attr')
85+
expect(element).not.toHaveAttribute('attr', true)
86+
expect(element).not.toHaveAttribute('attr', 'yes')
87+
expect(element).not.toHaveClass()
88+
expect(element).not.toHaveClass('cls1')
89+
expect(element).not.toHaveClass('cls1', 'cls2', 'cls3', 'cls4')
90+
expect(element).not.toHaveClass('cls1', {exact: true})
91+
expect(element).not.toHaveDisplayValue('str')
92+
expect(element).not.toHaveDisplayValue(['str1', 'str2'])
93+
expect(element).not.toHaveDisplayValue(/str/)
94+
expect(element).not.toHaveDisplayValue([/str1/, 'str2'])
95+
expect(element).not.toHaveFocus()
96+
expect(element).not.toHaveFormValues({foo: 'bar', baz: 1})
97+
expect(element).not.toHaveStyle('display: block')
98+
expect(element).not.toHaveTextContent('Text')
99+
expect(element).not.toHaveTextContent(/Text/)
100+
expect(element).not.toHaveTextContent('Text', {normalizeWhitespace: true})
101+
expect(element).not.toHaveTextContent(/Text/, {normalizeWhitespace: true})
102+
expect(element).not.toHaveValue()
103+
expect(element).not.toHaveValue('str')
104+
expect(element).not.toHaveValue(['str1', 'str2'])
105+
expect(element).not.toHaveValue(1)
106+
expect(element).not.toBeChecked()
107+
expect(element).not.toHaveDescription('some description')
108+
expect(element).not.toHaveDescription()
109+
expect(element).not.toHaveAccessibleDescription('some description')
110+
expect(element).not.toHaveAccessibleDescription()
111+
expect(element).not.toHaveAccessibleName('a label')
112+
expect(element).not.toHaveAccessibleName()
113+
expect(element).not.toBePartiallyChecked()
114+
expect(element).not.toHaveErrorMessage()
115+
expect(element).not.toHaveErrorMessage('Pikachu!')
116+
117+
// @ts-expect-error The types accidentally allowed any property by falling back to "any"
118+
expect(element).nonExistentProperty()
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"compilerOptions": {
3+
"noEmit": true,
4+
"strict": true,
5+
"skipLibCheck": true,
6+
"types": []
7+
},
8+
"include": ["*.ts"]
9+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* File that tests whether the TypeScript typings work as expected.
3+
*/
4+
5+
/* eslint-disable @typescript-eslint/no-unsafe-call */
6+
/* eslint-disable @typescript-eslint/no-floating-promises */
7+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
8+
9+
import * as matchers from '../../matchers'
10+
11+
expect.extend(matchers)
12+
13+
const element: HTMLElement = document.body
14+
15+
function customExpect(
16+
_actual: HTMLElement,
17+
):
18+
| matchers.TestingLibraryMatchers<unknown, void>
19+
| matchers.TestingLibraryMatchers<unknown, Promise<void>> {
20+
throw new Error('Method not implemented.')
21+
}
22+
23+
customExpect(element).toBeInTheDOM()
24+
customExpect(element).toBeInTheDOM(document.body)
25+
customExpect(element).toBeInTheDocument()
26+
customExpect(element).toBeVisible()
27+
customExpect(element).toBeEmpty()
28+
customExpect(element).toBeDisabled()
29+
customExpect(element).toBeEnabled()
30+
customExpect(element).toBeInvalid()
31+
customExpect(element).toBeRequired()
32+
customExpect(element).toBeValid()
33+
customExpect(element).toContainElement(document.body)
34+
customExpect(element).toContainElement(null)
35+
customExpect(element).toContainHTML('body')
36+
customExpect(element).toHaveAttribute('attr')
37+
customExpect(element).toHaveAttribute('attr', true)
38+
customExpect(element).toHaveAttribute('attr', 'yes')
39+
customExpect(element).toHaveClass()
40+
customExpect(element).toHaveClass('cls1')
41+
customExpect(element).toHaveClass('cls1', 'cls2', 'cls3', 'cls4')
42+
customExpect(element).toHaveClass('cls1', {exact: true})
43+
customExpect(element).toHaveDisplayValue('str')
44+
customExpect(element).toHaveDisplayValue(['str1', 'str2'])
45+
customExpect(element).toHaveDisplayValue(/str/)
46+
customExpect(element).toHaveDisplayValue([/str1/, 'str2'])
47+
customExpect(element).toHaveFocus()
48+
customExpect(element).toHaveFormValues({foo: 'bar', baz: 1})
49+
customExpect(element).toHaveStyle('display: block')
50+
customExpect(element).toHaveStyle({display: 'block', width: 100})
51+
customExpect(element).toHaveTextContent('Text')
52+
customExpect(element).toHaveTextContent(/Text/)
53+
customExpect(element).toHaveTextContent('Text', {normalizeWhitespace: true})
54+
customExpect(element).toHaveTextContent(/Text/, {normalizeWhitespace: true})
55+
customExpect(element).toHaveValue()
56+
customExpect(element).toHaveValue('str')
57+
customExpect(element).toHaveValue(['str1', 'str2'])
58+
customExpect(element).toHaveValue(1)
59+
customExpect(element).toHaveValue(null)
60+
customExpect(element).toBeChecked()
61+
customExpect(element).toHaveDescription('some description')
62+
customExpect(element).toHaveDescription(/some description/)
63+
customExpect(element).toHaveDescription(expect.stringContaining('partial'))
64+
customExpect(element).toHaveDescription()
65+
customExpect(element).toHaveAccessibleDescription('some description')
66+
customExpect(element).toHaveAccessibleDescription(/some description/)
67+
customExpect(element).toHaveAccessibleDescription(
68+
expect.stringContaining('partial'),
69+
)
70+
customExpect(element).toHaveAccessibleDescription()
71+
72+
customExpect(element).toHaveAccessibleErrorMessage()
73+
customExpect(element).toHaveAccessibleErrorMessage(
74+
'Invalid time: the time must be between 9:00 AM and 5:00 PM',
75+
)
76+
customExpect(element).toHaveAccessibleErrorMessage(/invalid time/i)
77+
customExpect(element).toHaveAccessibleErrorMessage(
78+
expect.stringContaining('Invalid time'),
79+
)
80+
81+
customExpect(element).toHaveAccessibleName('a label')
82+
customExpect(element).toHaveAccessibleName(/a label/)
83+
customExpect(element).toHaveAccessibleName(
84+
expect.stringContaining('partial label'),
85+
)
86+
customExpect(element).toHaveAccessibleName()
87+
customExpect(element).toHaveErrorMessage(
88+
'Invalid time: the time must be between 9:00 AM and 5:00 PM',
89+
)
90+
customExpect(element).toHaveErrorMessage(/invalid time/i)
91+
customExpect(element).toHaveErrorMessage(
92+
expect.stringContaining('Invalid time'),
93+
)
94+
95+
// @ts-expect-error The types accidentally allowed any property by falling back to "any"
96+
customExpect(element).nonExistentProperty()

0 commit comments

Comments
 (0)