Skip to content

Commit

Permalink
fix(render): Default to HTMLElement in returned container (#868)
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon committed Feb 2, 2021
1 parent 2a3be2c commit 81c2de9
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 12 deletions.
26 changes: 18 additions & 8 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ import {
BoundFunction,
prettyFormat,
} from '@testing-library/dom'
import {Renderer} from 'react-dom'
import {act as reactAct} from 'react-dom/test-utils'

export * from '@testing-library/dom'

export type RenderResult<Q extends Queries = typeof queries> = {
container: Element
export type RenderResult<
Q extends Queries = typeof queries,
Container extends Element | DocumentFragment = HTMLElement
> = {
container: Container
baseElement: Element
debug: (
baseElement?:
Expand All @@ -26,8 +30,11 @@ export type RenderResult<Q extends Queries = typeof queries> = {
asFragment: () => DocumentFragment
} & {[P in keyof Q]: BoundFunction<Q[P]>}

export interface RenderOptions<Q extends Queries = typeof queries> {
container?: Element
export interface RenderOptions<
Q extends Queries = typeof queries,
Container extends Element | DocumentFragment = HTMLElement
> {
container?: Container
baseElement?: Element
hydrate?: boolean
queries?: Q
Expand All @@ -39,14 +46,17 @@ type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
/**
* Render into a container which is appended to document.body. It should be used with cleanup.
*/
export function render<
Q extends Queries,
Container extends Element | DocumentFragment = HTMLElement
>(
ui: React.ReactElement,
options: RenderOptions<Q, Container>,
): RenderResult<Q, Container>
export function render(
ui: React.ReactElement,
options?: Omit<RenderOptions, 'queries'>,
): RenderResult
export function render<Q extends Queries>(
ui: React.ReactElement,
options: RenderOptions<Q>,
): RenderResult<Q>

/**
* Unmounts React trees that were mounted with render.
Expand Down
36 changes: 32 additions & 4 deletions types/test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {render, fireEvent, screen, waitFor} from '.'
import * as pure from './pure'

export async function testRender() {
const page = render(<div />)
const page = render(<button />)

// single queries
page.getByText('foo')
Expand All @@ -17,11 +17,12 @@ export async function testRender() {

// helpers
const {container, rerender, debug} = page
expectType<HTMLElement, typeof container>(container)
return {container, rerender, debug}
}

export async function testPureRender() {
const page = pure.render(<div />)
const page = pure.render(<button />)

// single queries
page.getByText('foo')
Expand All @@ -35,13 +36,15 @@ export async function testPureRender() {

// helpers
const {container, rerender, debug} = page
expectType<HTMLElement, typeof container>(container)
return {container, rerender, debug}
}

export function testRenderOptions() {
const container = document.createElement('div')
const options = {container}
render(<div />, options)
const {container: returnedContainer} = render(<button />, options)
expectType<HTMLDivElement, typeof returnedContainer>(returnedContainer)
}

export function testSVGRenderOptions() {
Expand All @@ -50,7 +53,8 @@ export function testSVGRenderOptions() {
'svg',
)
const options = {container}
render(<svg />, options)
const {container: returnedContainer} = render(<path />, options)
expectType<SVGSVGElement, typeof returnedContainer>(returnedContainer)
}

export function testFireEvent() {
Expand Down Expand Up @@ -87,3 +91,27 @@ eslint
testing-library/no-debug: "off",
testing-library/prefer-screen-queries: "off"
*/

// https://stackoverflow.com/questions/53807517/how-to-test-if-two-types-are-exactly-the-same
type IfEquals<T, U, Yes = unknown, No = never> = (<G>() => G extends T
? 1
: 2) extends <G>() => G extends U ? 1 : 2
? Yes
: No

/**
* Issues a type error if `Expected` is not identical to `Actual`.
*
* `Expected` should be declared when invoking `expectType`.
* `Actual` should almost always we be a `typeof value` statement.
*
* Source: https://github.com/mui-org/material-ui/blob/6221876a4b468a3330ffaafa8472de7613933b87/packages/material-ui-types/index.d.ts#L73-L84
*
* @example `expectType<number | string, typeof value>(value)`
* TypeScript issues a type error since `value is not assignable to never`.
* This means `typeof value` is not identical to `number | string`
* @param actual
*/
declare function expectType<Expected, Actual>(
actual: IfEquals<Actual, Expected, Actual>,
): void

0 comments on commit 81c2de9

Please sign in to comment.