Skip to content

Commit d918b3f

Browse files
committed
fix(render): Default to HTMLElement in returned container
1 parent 2a3be2c commit d918b3f

File tree

2 files changed

+50
-9
lines changed

2 files changed

+50
-9
lines changed

types/index.d.ts

+17-7
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ import {
66
BoundFunction,
77
prettyFormat,
88
} from '@testing-library/dom'
9+
import {Renderer} from 'react-dom'
910
import {act as reactAct} from 'react-dom/test-utils'
1011

1112
export * from '@testing-library/dom'
1213

13-
export type RenderResult<Q extends Queries = typeof queries> = {
14-
container: Element
14+
export type RenderResult<
15+
Q extends Queries = typeof queries,
16+
Container extends Element | DocumentFragment = HTMLElement
17+
> = {
18+
container: Container
1519
baseElement: Element
1620
debug: (
1721
baseElement?:
@@ -26,8 +30,11 @@ export type RenderResult<Q extends Queries = typeof queries> = {
2630
asFragment: () => DocumentFragment
2731
} & {[P in keyof Q]: BoundFunction<Q[P]>}
2832

29-
export interface RenderOptions<Q extends Queries = typeof queries> {
30-
container?: Element
33+
export interface RenderOptions<
34+
Q extends Queries = typeof queries,
35+
Container extends Element | DocumentFragment = HTMLElement
36+
> {
37+
container?: Container
3138
baseElement?: Element
3239
hydrate?: boolean
3340
queries?: Q
@@ -43,10 +50,13 @@ export function render(
4350
ui: React.ReactElement,
4451
options?: Omit<RenderOptions, 'queries'>,
4552
): RenderResult
46-
export function render<Q extends Queries>(
53+
export function render<
54+
Q extends Queries,
55+
Container extends Element | DocumentFragment = HTMLElement
56+
>(
4757
ui: React.ReactElement,
48-
options: RenderOptions<Q>,
49-
): RenderResult<Q>
58+
options: RenderOptions<Q, Container>,
59+
): RenderResult<Q, Container>
5060

5161
/**
5262
* Unmounts React trees that were mounted with render.

types/test.tsx

+33-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export async function testRender() {
1717

1818
// helpers
1919
const {container, rerender, debug} = page
20+
expectType<HTMLElement, typeof container>(container)
2021
return {container, rerender, debug}
2122
}
2223

@@ -35,13 +36,18 @@ export async function testPureRender() {
3536

3637
// helpers
3738
const {container, rerender, debug} = page
39+
expectType<HTMLElement, typeof container>(container)
3840
return {container, rerender, debug}
3941
}
4042

4143
export function testRenderOptions() {
4244
const container = document.createElement('div')
4345
const options = {container}
44-
render(<div />, options)
46+
const {container: returnedContainer} = render(<div />, options)
47+
// Unclear why TypeScript infers `HTMLElement` here when the the input `container` is `HTMLDivElement`
48+
// Hopefully this breaks someday and we can switch to
49+
// expectType<HTMLDivElement, typeof returnedContainer>(returnedContainer)
50+
expectType<HTMLElement, typeof returnedContainer>(returnedContainer)
4551
}
4652

4753
export function testSVGRenderOptions() {
@@ -50,7 +56,8 @@ export function testSVGRenderOptions() {
5056
'svg',
5157
)
5258
const options = {container}
53-
render(<svg />, options)
59+
const {container: returnedContainer} = render(<svg />, options)
60+
expectType<SVGSVGElement, typeof returnedContainer>(returnedContainer)
5461
}
5562

5663
export function testFireEvent() {
@@ -87,3 +94,27 @@ eslint
8794
testing-library/no-debug: "off",
8895
testing-library/prefer-screen-queries: "off"
8996
*/
97+
98+
// https://stackoverflow.com/questions/53807517/how-to-test-if-two-types-are-exactly-the-same
99+
type IfEquals<T, U, Yes = unknown, No = never> = (<G>() => G extends T
100+
? 1
101+
: 2) extends <G>() => G extends U ? 1 : 2
102+
? Yes
103+
: No
104+
105+
/**
106+
* Issues a type error if `Expected` is not identical to `Actual`.
107+
*
108+
* `Expected` should be declared when invoking `expectType`.
109+
* `Actual` should almost always we be a `typeof value` statement.
110+
*
111+
* Source: https://github.com/mui-org/material-ui/blob/6221876a4b468a3330ffaafa8472de7613933b87/packages/material-ui-types/index.d.ts#L73-L84
112+
*
113+
* @example `expectType<number | string, typeof value>(value)`
114+
* TypeScript issues a type error since `value is not assignable to never`.
115+
* This means `typeof value` is not identical to `number | string`
116+
* @param actual
117+
*/
118+
declare function expectType<Expected, Actual>(
119+
actual: IfEquals<Actual, Expected, Actual>,
120+
): void

0 commit comments

Comments
 (0)