diff --git a/.circleci/config.yml b/.circleci/config.yml index baeebccf3..0de14bf78 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -33,6 +33,13 @@ jobs: - run: | yarn lint yarn flow-check + typescript: + <<: *defaults + steps: + - attach_workspace: + at: ~/react-native-testing-library + - run: | + yarn typescript-check tests: <<: *defaults steps: @@ -51,6 +58,9 @@ workflows: - lint-and-flow: requires: - install-dependencies + - typescript: + requires: + - install-dependencies - tests: requires: - install-dependencies diff --git a/package.json b/package.json index 46a221b89..c86e7e72c 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.2.1", "description": "Simple React Native testing utilities helping you write better tests with less effort", "main": "src/index.js", + "typings": "./typings/index.d.ts", "repository": "git@github.com:callstack/react-native-testing-library.git", "author": "Michał Pierzchała ", "license": "MIT", @@ -11,6 +12,8 @@ "@babel/core": "^7.1.2", "@babel/runtime": "^7.1.2", "@callstack/eslint-config": "^2.0.0", + "@types/react": "^16.4.15", + "@types/react-test-renderer": "^16.0.3", "babel-core": "7.0.0-bridge.0", "babel-jest": "^23.6.0", "eslint": "^5.6.1", @@ -19,7 +22,8 @@ "metro-react-native-babel-preset": "^0.48.0", "pretty-format": "^23.6.0", "react": "^16.5.2", - "react-test-renderer": "^16.5.2" + "react-test-renderer": "^16.5.2", + "typescript": "^3.1.1" }, "peerDependencies": { "pretty-format": ">=20.0.0", @@ -32,6 +36,7 @@ "scripts": { "test": "jest", "flow-check": "flow check", + "typescript-check": "tsc --noEmit --skipLibCheck --jsx react ./typings/__tests__/*", "lint": "eslint src --cache" } } diff --git a/typings/__tests__/index.test.tsx b/typings/__tests__/index.test.tsx new file mode 100644 index 000000000..7a73f256b --- /dev/null +++ b/typings/__tests__/index.test.tsx @@ -0,0 +1,79 @@ +import * as React from 'react'; +import { ReactTestInstance } from 'react-test-renderer'; +import { render, fireEvent, shallow, flushMicrotasksQueue, debug } from '../..'; + +const View = props => props.children; +const Text = props => props.children; + +const TestComponent = () => ( + + Test component + +); + +const tree = render(); + +// getByAPI tests +const getByNameString: ReactTestInstance = tree.getByName('View'); +const getByNameContructor: ReactTestInstance = tree.getByName(View); +const getByTextString: ReactTestInstance = tree.getByText(''); +const getByTextRegExp: ReactTestInstance = tree.getByText(/View/g); +const getByProps: ReactTestInstance = tree.getByProps({ value: 2 }); +const getByTestId: ReactTestInstance = tree.getByTestId('test-id'); +const getAllByNameString: Array = tree.getAllByName('View'); +const getAllByNameConstructor: Array = tree.getAllByName( + View +); +const getAllByTextString: Array = tree.getAllByText( + '' +); +const getAllByTextRegExp: Array = tree.getAllByText(/Text/g); +const getAllByProps: Array = tree.getAllByProps({ + value: 2, +}); + +// queuryByAPI tests +const queryByNameString: ReactTestInstance | null = tree.queryByName('View'); +const queryByNameConstructor: ReactTestInstance | null = tree.queryByName(View); +const queryByTextString: ReactTestInstance | null = tree.queryByText( + '' +); +const queryByTextRegExp: ReactTestInstance | null = tree.queryByText(/View/g); +const queryByProps: ReactTestInstance | null = tree.queryByProps({ value: 2 }); +const queryByTestId: ReactTestInstance | null = tree.queryByTestId('test-id'); +const queryAllByNameString: Array = tree.getAllByName( + 'View' +); +const queryAllByNameConstructor: Array = tree.getAllByName( + View +); +const queryAllByTextString: Array = tree.queryAllByText( + 'View' +); +const queryAllByTextRegExp: Array = tree.queryAllByText( + /View/g +); +const queryAllByProps: Array = tree.getAllByProps({ + value: 2, +}); + +tree.update(); +tree.unmount(); + +// fireEvent API tests +fireEvent(getByNameString, 'press'); +fireEvent(getByNameString, 'press', 'data'); +fireEvent.press(getByNameString); +fireEvent.doublePress(getByNameString); +fireEvent.changeText(getByNameString, 'string'); +fireEvent.scroll(getByNameString, 'eventData'); + +// shallow API +const shallowTree: { output: React.ReactElement } = shallow( + +); + +const waitForFlush: Promise = flushMicrotasksQueue(); + +debug(); +debug(getByNameString); diff --git a/typings/index.d.ts b/typings/index.d.ts new file mode 100644 index 000000000..49aff95cb --- /dev/null +++ b/typings/index.d.ts @@ -0,0 +1,59 @@ +import * as React from 'react'; +import { ReactTestInstance } from 'react-test-renderer'; + +export interface GetByAPI { + getByName: (name: React.ReactType) => ReactTestInstance; + getByText: (text: string | RegExp) => ReactTestInstance; + getByProps: (props: Record) => ReactTestInstance; + getByTestId: (testID: string) => ReactTestInstance; + getAllByName: (name: React.ReactType) => Array; + getAllByText: (text: string | RegExp) => Array; + getAllByProps: (props: Record) => Array; +} + +export interface QueryByAPI { + queryByName: (name: React.ReactType) => ReactTestInstance | null; + queryByText: (name: string | RegExp) => ReactTestInstance | null; + queryByProps: (props: Record) => ReactTestInstance | null; + queryByTestId: (testID: string) => ReactTestInstance | null; + queryAllByName: (name: React.ReactType) => Array | []; + queryAllByText: (text: string | RegExp) => Array | []; + queryAllByProps: ( + props: Record + ) => Array | []; +} + +export interface RenderOptions { + createNodeMock: (element: React.ReactElement) => any; +} + +export interface RenderAPI extends GetByAPI, QueryByAPI { + update(nextElement: React.ReactElement): void; + unmount(nextElement?: React.ReactElement): void; +} + +export type FireEventFunction = ( + element: ReactTestInstance, + eventName: string, + data?: any +) => any; + +export type FireEventAPI = FireEventFunction & { + press: (element: ReactTestInstance) => any; + doublePress: (element: ReactTestInstance) => any; + changeText: (element: ReactTestInstance, data?: any) => any; + scroll: (element: ReactTestInstance, data?: any) => any; +}; + +export declare const render: ( + component: React.ReactElement, + options?: RenderOptions +) => RenderAPI; +export declare const shallow:

( + instance: ReactTestInstance | React.ReactElement

+) => { output: React.ReactElement

}; +export declare const flushMicrotasksQueue: () => Promise; +export declare const debug: ( + instance: ReactTestInstance | React.ReactElement +) => void; +export declare const fireEvent: FireEventAPI; diff --git a/yarn.lock b/yarn.lock index 63ff11dd5..c5f73aaeb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -662,6 +662,26 @@ eslint-plugin-react "^7.5.1" prettier "^1.14.2" +"@types/prop-types@*": + version "15.5.6" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.6.tgz#9c03d3fed70a8d517c191b7734da2879b50ca26c" + integrity sha512-ZBFR7TROLVzCkswA3Fmqq+IIJt62/T7aY/Dmz+QkU7CaW2QFqAitCE8Ups7IzmGhcN1YWMBT4Qcoc07jU9hOJQ== + +"@types/react-test-renderer@^16.0.3": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.0.3.tgz#cce5c983d66cc5c3582e7c2f44b274ab635a8acc" + integrity sha512-NWOAxVQeJxpXuNKgw83Hah0nquiw1nUexM9qY/Hk3a+XhZwgMtaa6GLA9E1TKMT75Odb3/KE/jiBO4enTuEJjQ== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@^16.4.15": + version "16.4.15" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.15.tgz#1f24f1d03b1fc682d92f8485be99d59bf7981191" + integrity sha512-EFQyVZhZCrRjwYDVziTEUGri/ygArIi/ES+JAI0j+3FR0LZ0uRfq2Ed7YnZ1CCn9CM3malSWwTKw5Jo0gVV82A== + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" + abab@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f" @@ -1443,6 +1463,11 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" +csstype@^2.2.0: + version "2.5.7" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.5.7.tgz#bf9235d5872141eccfb2d16d82993c6b149179ff" + integrity sha512-Nt5VDyOTIIV4/nRFswoCKps1R5CD1hkiyjBE9/thNaNZILLEviVw9yWQw15+O+CpNjQKB/uvdcxFFOrSflY3Yw== + damerau-levenshtein@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz#03191c432cb6eea168bb77f3a55ffdccb8978514" @@ -4941,6 +4966,11 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +typescript@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.1.tgz#3362ba9dd1e482ebb2355b02dfe8bcd19a2c7c96" + integrity sha512-Veu0w4dTc/9wlWNf2jeRInNodKlcdLgemvPsrNpfu5Pq39sgfFjvIIgTsvUHCoLBnMhPoUA+tFxsXjU6VexVRQ== + uglify-js@^3.1.4: version "3.4.9" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"