Skip to content

Commit 81c2de9

Browse files
authored
fix(render): Default to HTMLElement in returned container (#868)
1 parent 2a3be2c commit 81c2de9

File tree

2 files changed

+50
-12
lines changed

2 files changed

+50
-12
lines changed

types/index.d.ts

+18-8
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
@@ -39,14 +46,17 @@ type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
3946
/**
4047
* Render into a container which is appended to document.body. It should be used with cleanup.
4148
*/
49+
export function render<
50+
Q extends Queries,
51+
Container extends Element | DocumentFragment = HTMLElement
52+
>(
53+
ui: React.ReactElement,
54+
options: RenderOptions<Q, Container>,
55+
): RenderResult<Q, Container>
4256
export function render(
4357
ui: React.ReactElement,
4458
options?: Omit<RenderOptions, 'queries'>,
4559
): RenderResult
46-
export function render<Q extends Queries>(
47-
ui: React.ReactElement,
48-
options: RenderOptions<Q>,
49-
): RenderResult<Q>
5060

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

types/test.tsx

+32-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {render, fireEvent, screen, waitFor} from '.'
33
import * as pure from './pure'
44

55
export async function testRender() {
6-
const page = render(<div />)
6+
const page = render(<button />)
77

88
// single queries
99
page.getByText('foo')
@@ -17,11 +17,12 @@ 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

2324
export async function testPureRender() {
24-
const page = pure.render(<div />)
25+
const page = pure.render(<button />)
2526

2627
// single queries
2728
page.getByText('foo')
@@ -35,13 +36,15 @@ 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(<button />, options)
47+
expectType<HTMLDivElement, typeof returnedContainer>(returnedContainer)
4548
}
4649

4750
export function testSVGRenderOptions() {
@@ -50,7 +53,8 @@ export function testSVGRenderOptions() {
5053
'svg',
5154
)
5255
const options = {container}
53-
render(<svg />, options)
56+
const {container: returnedContainer} = render(<path />, options)
57+
expectType<SVGSVGElement, typeof returnedContainer>(returnedContainer)
5458
}
5559

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

0 commit comments

Comments
 (0)