Skip to content

Commit 5675b86

Browse files
authored
fix: Standalone types for "./matchers" export and add Bun support (#566)
1 parent 1fb156c commit 5675b86

File tree

6 files changed

+275
-3
lines changed

6 files changed

+275
-3
lines changed

package.json

+9-3
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@
2727
},
2828
"./matchers": {
2929
"require": {
30-
"types": "./types/matchers.d.ts",
30+
"types": "./types/matchers-standalone.d.ts",
3131
"default": "./dist/matchers.js"
3232
},
3333
"import": {
34-
"types": "./types/matchers.d.ts",
34+
"types": "./types/matchers-standalone.d.ts",
3535
"default": "./dist/matchers.mjs"
3636
}
3737
},
@@ -60,7 +60,7 @@
6060
"setup": "npm install && npm run validate -s",
6161
"test": "kcd-scripts test",
6262
"test:update": "npm test -- --updateSnapshot --coverage",
63-
"test:types": "tsc -p types/__tests__/jest && tsc -p types/__tests__/jest-globals && tsc -p types/__tests__/vitest",
63+
"test:types": "tsc -p types/__tests__/jest && tsc -p types/__tests__/jest-globals && tsc -p types/__tests__/vitest && tsc -p types/__tests__/bun",
6464
"validate": "kcd-scripts validate && npm run test:types"
6565
},
6666
"files": [
@@ -92,6 +92,8 @@
9292
"devDependencies": {
9393
"@jest/globals": "^29.6.2",
9494
"@rollup/plugin-commonjs": "^25.0.4",
95+
"@types/bun": "latest",
96+
"@types/web": "latest",
9597
"expect": "^29.6.2",
9698
"jest-environment-jsdom-sixteen": "^1.0.3",
9799
"jest-watch-select-projects": "^2.0.0",
@@ -105,6 +107,7 @@
105107
},
106108
"peerDependencies": {
107109
"@jest/globals": ">= 28",
110+
"@types/bun": "latest",
108111
"@types/jest": ">= 28",
109112
"jest": ">= 28",
110113
"vitest": ">= 0.32"
@@ -113,6 +116,9 @@
113116
"@jest/globals": {
114117
"optional": true
115118
},
119+
"@types/bun": {
120+
"optional": true
121+
},
116122
"@types/jest": {
117123
"optional": true
118124
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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 'bun:test'
10+
import * as matchersStandalone from '../../matchers-standalone'
11+
import * as originalMatchers from '../../matchers'
12+
13+
expect.extend(matchersStandalone)
14+
15+
const element: HTMLElement = document.body
16+
17+
function customExpect(
18+
_actual: HTMLElement,
19+
):
20+
| originalMatchers.TestingLibraryMatchers<unknown, void>
21+
| originalMatchers.TestingLibraryMatchers<unknown, Promise<void>> {
22+
throw new Error('Method not implemented.')
23+
}
24+
25+
customExpect(element).toBeInTheDOM()
26+
customExpect(element).toBeInTheDOM(document.body)
27+
customExpect(element).toBeInTheDocument()
28+
customExpect(element).toBeVisible()
29+
customExpect(element).toBeEmpty()
30+
customExpect(element).toBeDisabled()
31+
customExpect(element).toBeEnabled()
32+
customExpect(element).toBeInvalid()
33+
customExpect(element).toBeRequired()
34+
customExpect(element).toBeValid()
35+
customExpect(element).toContainElement(document.body)
36+
customExpect(element).toContainElement(null)
37+
customExpect(element).toContainHTML('body')
38+
customExpect(element).toHaveAttribute('attr')
39+
customExpect(element).toHaveAttribute('attr', true)
40+
customExpect(element).toHaveAttribute('attr', 'yes')
41+
customExpect(element).toHaveClass()
42+
customExpect(element).toHaveClass('cls1')
43+
customExpect(element).toHaveClass('cls1', 'cls2', 'cls3', 'cls4')
44+
customExpect(element).toHaveClass('cls1', {exact: true})
45+
customExpect(element).toHaveDisplayValue('str')
46+
customExpect(element).toHaveDisplayValue(['str1', 'str2'])
47+
customExpect(element).toHaveDisplayValue(/str/)
48+
customExpect(element).toHaveDisplayValue([/str1/, 'str2'])
49+
customExpect(element).toHaveFocus()
50+
customExpect(element).toHaveFormValues({foo: 'bar', baz: 1})
51+
customExpect(element).toHaveStyle('display: block')
52+
customExpect(element).toHaveStyle({display: 'block', width: 100})
53+
customExpect(element).toHaveTextContent('Text')
54+
customExpect(element).toHaveTextContent(/Text/)
55+
customExpect(element).toHaveTextContent('Text', {normalizeWhitespace: true})
56+
customExpect(element).toHaveTextContent(/Text/, {normalizeWhitespace: true})
57+
customExpect(element).toHaveValue()
58+
customExpect(element).toHaveValue('str')
59+
customExpect(element).toHaveValue(['str1', 'str2'])
60+
customExpect(element).toHaveValue(1)
61+
customExpect(element).toHaveValue(null)
62+
customExpect(element).toBeChecked()
63+
customExpect(element).toHaveDescription('some description')
64+
customExpect(element).toHaveDescription(/some description/)
65+
customExpect(element).toHaveDescription(expect.stringContaining('partial'))
66+
customExpect(element).toHaveDescription()
67+
customExpect(element).toHaveAccessibleDescription('some description')
68+
customExpect(element).toHaveAccessibleDescription(/some description/)
69+
customExpect(element).toHaveAccessibleDescription(
70+
expect.stringContaining('partial'),
71+
)
72+
customExpect(element).toHaveAccessibleDescription()
73+
74+
customExpect(element).toHaveAccessibleErrorMessage()
75+
customExpect(element).toHaveAccessibleErrorMessage(
76+
'Invalid time: the time must be between 9:00 AM and 5:00 PM',
77+
)
78+
customExpect(element).toHaveAccessibleErrorMessage(/invalid time/i)
79+
customExpect(element).toHaveAccessibleErrorMessage(
80+
expect.stringContaining('Invalid time'),
81+
)
82+
83+
customExpect(element).toHaveAccessibleName('a label')
84+
customExpect(element).toHaveAccessibleName(/a label/)
85+
customExpect(element).toHaveAccessibleName(
86+
expect.stringContaining('partial label'),
87+
)
88+
customExpect(element).toHaveAccessibleName()
89+
customExpect(element).toHaveErrorMessage(
90+
'Invalid time: the time must be between 9:00 AM and 5:00 PM',
91+
)
92+
customExpect(element).toHaveErrorMessage(/invalid time/i)
93+
customExpect(element).toHaveErrorMessage(
94+
expect.stringContaining('Invalid time'),
95+
)
96+
97+
// @ts-expect-error The types accidentally allowed any property by falling back to "any"
98+
customExpect(element).nonExistentProperty()

types/__tests__/bun/bun-types.test.ts

+118
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 'bun:test'
10+
import '../../bun'
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()

types/__tests__/bun/tsconfig.json

+9
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": ["bun", "web"]
7+
},
8+
"include": ["*.ts"]
9+
}

types/bun.d.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {type expect} from 'bun:test'
2+
import {type TestingLibraryMatchers} from './matchers'
3+
4+
export {}
5+
declare module 'bun:test' {
6+
interface Matchers<T = any>
7+
extends TestingLibraryMatchers<
8+
ReturnType<typeof expect.stringContaining>,
9+
T
10+
> {}
11+
}

types/matchers-standalone.d.ts

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {type TestingLibraryMatchers} from './matchers'
2+
3+
type TLM = TestingLibraryMatchers<any, void>
4+
5+
interface MatcherReturnType {
6+
pass: boolean
7+
message: () => string
8+
}
9+
10+
interface OverloadedMatchers {
11+
toHaveClass: (expected: any, ...rest: string[]) => MatcherReturnType
12+
toHaveClass: (
13+
expected: any,
14+
className: string,
15+
options?: {exact: boolean},
16+
) => MatcherReturnType
17+
}
18+
19+
declare namespace matchersStandalone {
20+
type MatchersStandalone = {
21+
[T in keyof TLM]: (
22+
expected: any,
23+
...rest: Parameters<TLM[T]>
24+
) => MatcherReturnType
25+
} & OverloadedMatchers
26+
}
27+
28+
declare const matchersStandalone: matchersStandalone.MatchersStandalone &
29+
Record<string, any>
30+
export = matchersStandalone

0 commit comments

Comments
 (0)