Skip to content

Commit

Permalink
feat(TS): move types from DefinitelyTyped (#530)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lagily authored May 7, 2020
1 parent b31c0b9 commit 678f4da
Show file tree
Hide file tree
Showing 20 changed files with 581 additions and 3 deletions.
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "0.0.0-semantically-released",
"description": "Simple and complete DOM testing utilities that encourage good testing practices.",
"main": "dist/index.js",
"types": "types/index.d.ts",
"module": "dist/@testing-library/dom.esm.js",
"umd:main": "dist/@testing-library/dom.umd.js",
"source": "src/index.js",
Expand All @@ -29,19 +30,21 @@
"test": "kcd-scripts test",
"test:debug": "node --inspect-brk ./node_modules/.bin/jest --watch --runInBand",
"test:update": "npm test -- --updateSnapshot --coverage",
"validate": "kcd-scripts validate"
"validate": "kcd-scripts validate",
"typecheck": "dtslint ./types/"
},
"files": [
"dist"
"dist",
"types"
],
"dependencies": {
"@babel/runtime": "^7.9.6",
"@types/testing-library__dom": "^7.0.2",
"aria-query": "^4.0.2",
"dom-accessibility-api": "^0.4.3",
"pretty-format": "^26.0.1"
},
"devDependencies": {
"dtslint": "^3.4.2",
"@testing-library/jest-dom": "^5.5.0",
"jest-in-case": "^1.0.2",
"jest-serializer-ansi": "^1.0.3",
Expand Down
122 changes: 122 additions & 0 deletions types/__tests__/type-tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {
fireEvent,
isInaccessible,
queries,
screen,
waitFor,
waitForElementToBeRemoved,
} from '../index'

const {
getByText,
queryByText,
findByText,
getAllByText,
queryAllByText,
findAllByText,
queryAllByRole,
queryByRole,
findByRole,
} = queries

async function testQueries() {
// element queries
const element = document.createElement('div')
getByText(element, 'foo')
queryByText(element, 'foo')
await findByText(element, 'foo')
await findByText(element, 'foo', undefined, {timeout: 10})
getAllByText(element, 'bar')
queryAllByText(element, 'bar')
await findAllByText(element, 'bar')
await findAllByText(element, 'bar', undefined, {timeout: 10})

// screen queries
screen.getByText('foo')
screen.queryByText('foo')
await screen.findByText('foo')
await screen.findByText('foo', undefined, {timeout: 10})
screen.debug(screen.getAllByText('bar'))
screen.queryAllByText('bar')
await screen.findAllByText('bar')
await screen.findAllByText('bar', undefined, {timeout: 10})
}

async function testByRole() {
const element = document.createElement('button')
element.setAttribute('aria-hidden', 'true')

console.assert(queryByRole(element, 'button') === null)
console.assert(queryByRole(element, 'button', {hidden: true}) !== null)

console.assert(screen.queryByRole('button') === null)
console.assert(screen.queryByRole('button', {hidden: true}) !== null)

console.assert(
(await findByRole(element, 'button', undefined, {timeout: 10})) === null,
)
console.assert(
(await findByRole(element, 'button', {hidden: true}, {timeout: 10})) !==
null,
)

console.assert(
queryAllByRole(document.body, 'progressbar', {queryFallbacks: true})
.length === 1,
)

// `name` option
console.assert(queryByRole(element, 'button', {name: 'Logout'}) === null)
console.assert(queryByRole(element, 'button', {name: /^Log/}) === null)
console.assert(
queryByRole(element, 'button', {
name: (name, element) =>
name === 'Login' && element.hasAttribute('disabled'),
}) === null,
)
}

function testA11yHelper() {
const element = document.createElement('svg')
console.assert(!isInaccessible(element))
}

function eventTest() {
fireEvent.popState(window, {
location: 'http://www.example.com/?page=1',
state: {page: 1},
})

// HTMLElement
const element = document.createElement('div')
fireEvent.click(getByText(element, 'foo'))

// ChildNode
const child = document.createElement('div')
element.appendChild(child)
if (!element.firstChild) {
// Narrow Type
throw new Error(`Can't find firstChild`)
}
fireEvent.click(element.firstChild)
}

async function testWaitFors() {
const element = document.createElement('div')

await waitFor(() => getByText(element, 'apple'))
await waitFor(() => getAllByText(element, 'apple'))
const result: HTMLSpanElement = await waitFor(() =>
getByText(element, 'apple'),
)
if (!result) {
// Use value
throw new Error(`Can't find result`)
}

element.innerHTML = '<span>apple</span>'

await waitForElementToBeRemoved(() => getByText(element, 'apple'), {interval: 3000, container: element, timeout: 5000})
await waitForElementToBeRemoved(getByText(element, 'apple'))
await waitForElementToBeRemoved(getAllByText(element, 'apple'))
}
12 changes: 12 additions & 0 deletions types/config.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export interface Config {
testIdAttribute: string;
asyncWrapper(cb: (...args: any[]) => any): Promise<any>;
asyncUtilTimeout: number;
defaultHidden: boolean;
}

export interface ConfigFn {
(existingConfig: Config): Partial<Config>;
}

export function configure(configDelta: Partial<Config> | ConfigFn): void;
95 changes: 95 additions & 0 deletions types/events.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
export type EventType =
| 'copy'
| 'cut'
| 'paste'
| 'compositionEnd'
| 'compositionStart'
| 'compositionUpdate'
| 'keyDown'
| 'keyPress'
| 'keyUp'
| 'focus'
| 'blur'
| 'focusIn'
| 'focusOut'
| 'change'
| 'input'
| 'invalid'
| 'submit'
| 'reset'
| 'click'
| 'contextMenu'
| 'dblClick'
| 'drag'
| 'dragEnd'
| 'dragEnter'
| 'dragExit'
| 'dragLeave'
| 'dragOver'
| 'dragStart'
| 'drop'
| 'mouseDown'
| 'mouseEnter'
| 'mouseLeave'
| 'mouseMove'
| 'mouseOut'
| 'mouseOver'
| 'mouseUp'
| 'popState'
| 'select'
| 'touchCancel'
| 'touchEnd'
| 'touchMove'
| 'touchStart'
| 'scroll'
| 'wheel'
| 'abort'
| 'canPlay'
| 'canPlayThrough'
| 'durationChange'
| 'emptied'
| 'encrypted'
| 'ended'
| 'loadedData'
| 'loadedMetadata'
| 'loadStart'
| 'pause'
| 'play'
| 'playing'
| 'progress'
| 'rateChange'
| 'seeked'
| 'seeking'
| 'stalled'
| 'suspend'
| 'timeUpdate'
| 'volumeChange'
| 'waiting'
| 'load'
| 'error'
| 'animationStart'
| 'animationEnd'
| 'animationIteration'
| 'transitionEnd'
| 'doubleClick'
| 'pointerOver'
| 'pointerEnter'
| 'pointerDown'
| 'pointerMove'
| 'pointerUp'
| 'pointerCancel'
| 'pointerOut'
| 'pointerLeave'
| 'gotPointerCapture'
| 'lostPointerCapture';

export type FireFunction = (element: Document | Element | Window | Node, event: Event) => boolean;
export type FireObject = {
[K in EventType]: (element: Document | Element | Window | Node, options?: {}) => boolean;
};
export type CreateObject = {
[K in EventType]: (element: Document | Element | Window | Node, options?: {}) => Event;
};

export const createEvent: CreateObject;
export const fireEvent: FireFunction & FireObject;
1 change: 1 addition & 0 deletions types/get-node-text.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export function getNodeText(node: HTMLElement): string;
30 changes: 30 additions & 0 deletions types/get-queries-for-element.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Matcher } from './matches';
import * as queries from './queries';

export type BoundFunction<T> = T extends (
attribute: string,
element: HTMLElement,
text: infer P,
options: infer Q,
) => infer R
? (text: P, options?: Q) => R
: T extends (a1: any, text: infer P, options: infer Q, waitForElementOptions: infer W) => infer R
? (text: P, options?: Q, waitForElementOptions?: W) => R
: T extends (a1: any, text: infer P, options: infer Q) => infer R
? (text: P, options?: Q) => R
: never;
export type BoundFunctions<T> = { [P in keyof T]: BoundFunction<T[P]> };

export type Query = (
container: HTMLElement,
...args: any[]
) => Error | Promise<HTMLElement[]> | Promise<HTMLElement> | HTMLElement[] | HTMLElement | null;

export interface Queries {
[T: string]: Query;
}

export function getQueriesForElement<T extends Queries = typeof queries>(
element: HTMLElement,
queriesToBind?: T,
): BoundFunctions<T>;
24 changes: 24 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// TypeScript Version: 3.8

import { getQueriesForElement } from './get-queries-for-element';
import * as queries from './queries';
import * as queryHelpers from './query-helpers';

declare const within: typeof getQueriesForElement;
export { queries, queryHelpers, within };

export * from './queries';
export * from './query-helpers';
export * from './screen';
export * from './wait';
export * from './wait-for';
export * from './wait-for-dom-change';
export * from './wait-for-element';
export * from './wait-for-element-to-be-removed';
export * from './matches';
export * from './get-node-text';
export * from './events';
export * from './get-queries-for-element';
export * from './pretty-dom';
export * from './role-helpers';
export * from './config';
29 changes: 29 additions & 0 deletions types/matches.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export type MatcherFunction = (content: string, element: HTMLElement) => boolean;
export type Matcher = string | RegExp | MatcherFunction;

export type NormalizerFn = (text: string) => string;

export interface MatcherOptions {
exact?: boolean;
/** Use normalizer with getDefaultNormalizer instead */
trim?: boolean;
/** Use normalizer with getDefaultNormalizer instead */
collapseWhitespace?: boolean;
normalizer?: NormalizerFn;
}

export type Match = (
textToMatch: string,
node: HTMLElement | null,
matcher: Matcher,
options?: MatcherOptions,
) => boolean;

export interface DefaultNormalizerOptions {
trim?: boolean;
collapseWhitespace?: boolean;
}

export function getDefaultNormalizer(options?: DefaultNormalizerOptions): NormalizerFn;

// N.B. Don't expose fuzzyMatches + matches here: they're not public API
4 changes: 4 additions & 0 deletions types/pretty-dom.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { OptionsReceived } from 'pretty-format';

export function prettyDOM(dom?: Element | HTMLDocument, maxLength?: number, options?: OptionsReceived): string | false;
export function logDOM(dom?: Element | HTMLDocument, maxLength?: number, options?: OptionsReceived): void;
Loading

0 comments on commit 678f4da

Please sign in to comment.