diff --git a/.gitignore b/.gitignore
index ca2991c..cc166bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
-node_modules
-coverage
-dist
-.idea
+node_modules/
+coverage/
+dist/
+.idea/
.DS_Store
yarn-error.log
diff --git a/examples/__tests__/input-event.js b/examples/__tests__/input-event.js
index 4df8861..1dea5d9 100644
--- a/examples/__tests__/input-event.js
+++ b/examples/__tests__/input-event.js
@@ -1,6 +1,6 @@
import React from 'react';
-import { Text, TextInput, View } from 'react-native';
-import { render, fireEvent } from 'native-testing-library';
+import { TextInput } from 'react-native';
+import { render, fireEvent } from '../../src';
class CostInput extends React.Component {
state = {
diff --git a/examples/__tests__/react-context.js b/examples/__tests__/react-context.js
index 3a8ab13..72ab617 100644
--- a/examples/__tests__/react-context.js
+++ b/examples/__tests__/react-context.js
@@ -1,7 +1,7 @@
import 'jest-native/extend-expect';
import React from 'react';
import { Text } from 'react-native';
-import { render } from 'native-testing-library';
+import { render } from '../../src';
import { NameContext, NameProvider, NameConsumer } from '../react-context';
diff --git a/examples/__tests__/react-intl.js b/examples/__tests__/react-intl.js
index 7536d2f..bdd16b4 100644
--- a/examples/__tests__/react-intl.js
+++ b/examples/__tests__/react-intl.js
@@ -5,7 +5,7 @@ import { FormattedDate } from 'react-intl-native';
import IntlPolyfill from 'intl';
import 'intl/locale-data/jsonp/pt';
-import { getByText, render } from 'native-testing-library';
+import { getByText, render } from '../../src';
const setupTests = () => {
if (global.Intl) {
diff --git a/examples/__tests__/react-navigation.js b/examples/__tests__/react-navigation.js
index 36f6436..929673b 100644
--- a/examples/__tests__/react-navigation.js
+++ b/examples/__tests__/react-navigation.js
@@ -3,7 +3,7 @@ import React from 'react';
import { Button, Text, View } from 'react-native';
import { createStackNavigator, createAppContainer, withNavigation } from 'react-navigation';
-import { render, fireEvent } from 'native-testing-library';
+import { render, fireEvent } from '../../src';
jest.mock('NativeAnimatedHelper').mock('react-native-gesture-handler', () => {
const View = require('react-native').View;
@@ -15,25 +15,6 @@ jest.mock('NativeAnimatedHelper').mock('react-native-gesture-handler', () => {
};
});
-const originalConsoleWarn = console.warn;
-console.warn = arg => {
- const warnings = [
- 'Calling .measureInWindow()',
- 'Calling .measureLayout()',
- 'Calling .setNativeProps()',
- 'Calling .focus()',
- 'Calling .blur()',
- ];
-
- const finalArgs = warnings.reduce((acc, curr) => (arg.includes(curr) ? [...acc, arg] : acc), []);
-
- if (finalArgs.length) {
- return;
- }
-
- originalConsoleWarn(message);
-};
-
const Home = ({ navigation }) => (
Home page
@@ -70,14 +51,14 @@ function renderWithNavigation({ screens = {}, navigatorConfig = {} } = {}) {
const App = createAppContainer(AppNavigator);
- return { ...render(), navigationContainer: App };
+ return { ...render(), navigationContainer: App };
}
test('full app rendering/navigating', async () => {
- const { findByText, getByTestId, getByText } = renderWithNavigation();
+ const { findByText, getByTestId, getByTitle } = renderWithNavigation();
expect(getByTestId('title')).toHaveTextContent('Home page');
- fireEvent.press(getByText(/Go to about/i));
+ fireEvent.press(getByTitle(/Go to about/i));
const result = await findByText('About page');
expect(result).toHaveTextContent('About page');
diff --git a/examples/__tests__/react-redux.js b/examples/__tests__/react-redux.js
index 4bb157a..738f25d 100644
--- a/examples/__tests__/react-redux.js
+++ b/examples/__tests__/react-redux.js
@@ -3,7 +3,7 @@ import React from 'react';
import { createStore } from 'redux';
import { Provider, connect } from 'react-redux';
import { Button, Text, View } from 'react-native';
-import { render, fireEvent } from 'native-testing-library';
+import { render, fireEvent } from '../../src';
class Counter extends React.Component {
increment = () => {
@@ -53,26 +53,26 @@ function renderWithRedux(ui, { initialState, store = createStore(reducer, initia
}
test('can render with redux with defaults', () => {
- const { getByTestId, getByText } = renderWithRedux();
- fireEvent.press(getByText('+'));
+ const { getByTestId, getByTitle } = renderWithRedux();
+ fireEvent.press(getByTitle('+'));
expect(getByTestId('count-value')).toHaveTextContent(1);
});
test('can render with redux with custom initial state', () => {
- const { getByTestId, getByText } = renderWithRedux(, {
+ const { getByTestId, getByTitle } = renderWithRedux(, {
initialState: { count: 3 },
});
- fireEvent.press(getByText('-'));
+ fireEvent.press(getByTitle('-'));
expect(getByTestId('count-value')).toHaveTextContent(2);
});
test('can render with redux with custom store', () => {
const store = createStore(() => ({ count: 1000 }));
- const { getByTestId, getByText } = renderWithRedux(, {
+ const { getByTestId, getByTitle } = renderWithRedux(, {
store,
});
- fireEvent.press(getByText('+'));
+ fireEvent.press(getByTitle('+'));
expect(getByTestId('count-value')).toHaveTextContent(1000);
- fireEvent.press(getByText('-'));
+ fireEvent.press(getByTitle('-'));
expect(getByTestId('count-value')).toHaveTextContent(1000);
});
diff --git a/examples/__tests__/update-props.js b/examples/__tests__/update-props.js
index 6f95b51..3f00468 100644
--- a/examples/__tests__/update-props.js
+++ b/examples/__tests__/update-props.js
@@ -1,7 +1,7 @@
import React from 'react';
import 'jest-native/extend-expect';
import { Text, View } from 'react-native';
-import { render } from 'native-testing-library';
+import { render } from '../../src';
let idCounter = 1;
diff --git a/examples/__tests__/use-hooks.js b/examples/__tests__/use-hooks.js
new file mode 100644
index 0000000..1611176
--- /dev/null
+++ b/examples/__tests__/use-hooks.js
@@ -0,0 +1,21 @@
+import { useState } from 'react';
+import { renderHook, act } from 'react-hooks-testing-library';
+
+describe('useState tests', () => {
+ test('should use setState value', () => {
+ const { result } = renderHook(() => useState('foo'));
+ const [value] = result.current;
+
+ expect(value).toBe('foo');
+ });
+
+ test('should update setState value using setter', () => {
+ const { result } = renderHook(() => useState('foo'));
+ const [_, setValue] = result.current;
+
+ act(() => setValue('bar'));
+
+ const [value] = result.current;
+ expect(value).toBe('bar');
+ });
+});
diff --git a/examples/jest.config.js b/examples/jest.config.js
deleted file mode 100644
index d919bf3..0000000
--- a/examples/jest.config.js
+++ /dev/null
@@ -1,9 +0,0 @@
-module.exports = {
- displayName: 'example',
- preset: 'react-native',
- roots: [__dirname],
- rootDir: __dirname,
- moduleNameMapper: {
- 'native-testing-library': 'src',
- },
-};
diff --git a/jest-preset.js b/jest-preset.js
new file mode 100644
index 0000000..9b714d1
--- /dev/null
+++ b/jest-preset.js
@@ -0,0 +1,6 @@
+const jestPreset = require('react-native/jest-preset');
+
+module.exports = Object.assign(jestPreset, {
+ transformIgnorePatterns: ['node_modules/(?!(react-native.*|@?react-navigation.*)/)'],
+ setupFiles: [...jestPreset.setupFiles, require.resolve('./src/preset/setup.js')],
+});
diff --git a/jest.config.js b/jest.config.js
index a1110e1..d1f1f13 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,10 +1,9 @@
+const jestPreset = require('./jest-preset');
+
const ignores = ['/node_modules/', '/__tests__/helpers/', '__mocks__'];
-module.exports = {
- preset: 'react-native',
- transformIgnorePatterns: ['node_modules/(?!(react-native.*|@?react-navigation.*)/)'],
- collectCoverageFrom: ['src/**/*.+(js|jsx|ts|tsx)'],
- testMatch: ['**/__tests__/**/*.+(js|jsx|ts|tsx)'],
+module.exports = Object.assign(jestPreset, {
+ collectCoverageFrom: ['src/lib/**/*.+(js|jsx|ts|tsx)'],
testPathIgnorePatterns: [...ignores],
coverageThreshold: {
global: {
@@ -14,4 +13,4 @@ module.exports = {
statements: 100,
},
},
-};
+});
diff --git a/package.json b/package.json
index 71bfe78..3a707f1 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,9 @@
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
- "dist/**"
+ "dist/**",
+ "typings",
+ "jest-preset.js"
],
"engines": {
"node": ">=8"
@@ -16,8 +18,8 @@
"commit:all": "npm run commit:add && npm run commit",
"readme:toc": "doctoc README.md --maxlevel 3 --title '## Table of Contents'",
"test": "jest",
- "pretty-quick": "pretty-quick --staged --pattern '**/*.(js|jsx|ts|tsx)'",
- "prepublish": "rm -rf dist; babel src --out-dir dist --ignore 'src/__tests__/*' && cp src/index.d.ts dist/index.d.ts",
+ "pretty-quick": "pretty-quick --staged",
+ "prepublishOnly": "rm -rf dist; babel src --out-dir dist --ignore 'src/**/__tests__/*'",
"semantic-release": "semantic-release",
"test:coverage": "jest --coverage",
"test:watch": "jest --watch --coverage"
@@ -35,6 +37,7 @@
"author": "Brandon Carroll (https://github.com/bcarroll22)",
"license": "MIT",
"dependencies": {
+ "jest-native": "2.0.0-alpha.5",
"pretty-format": "^24.5.0",
"react-test-renderer": "^16.8.5",
"wait-for-expect": "^1.1.1"
@@ -50,18 +53,17 @@
"jest": "24.5.0",
"jest-fetch-mock": "^2.1.1",
"jest-in-case": "^1.0.2",
- "jest-native": "^1.2.0",
"metro-react-native-babel-preset": "^0.52.0",
"prettier": "^1.16.4",
"pretty-quick": "^1.10.0",
"react": "^16.8.5",
+ "react-hooks-testing-library": "^0.5.0",
"react-intl": "^2.8.0",
"react-intl-native": "^2.1.2",
"react-native": "^0.59.0",
"react-native-gesture-handler": "^1.1.0",
"react-navigation": "^3.5.1",
"react-redux": "6.0.1",
- "react-test-renderer-tree-to-json": "^1.0.1",
"redux": "^4.0.0",
"semantic-release": "^15.13.3"
},
diff --git a/setup.js b/setup.js
deleted file mode 100644
index e69de29..0000000
diff --git a/src/__tests__/__snapshots__/fetch.js.snap b/src/__tests__/__snapshots__/fetch.js.snap
deleted file mode 100644
index 762e687..0000000
--- a/src/__tests__/__snapshots__/fetch.js.snap
+++ /dev/null
@@ -1,28 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Fetch makes an API call and displays the greeting when load-greeting is clicked 1`] = `
-
-
-
- Fetch
-
-
-
- hello there
-
-
-`;
diff --git a/src/__tests__/filter.js b/src/__tests__/filter.js
deleted file mode 100644
index 9255f16..0000000
--- a/src/__tests__/filter.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import React from 'react';
-import { Text } from 'react-native';
-
-import { render } from '../';
-
-test('it calls a custom filter', () => {
- const { queryByTestId } = render(hi);
- const filterSpy = jest.fn();
-
- queryByTestId('test', { filter: filterSpy });
- expect(filterSpy).toHaveBeenCalled();
-});
-
-test('custom filters modify results', () => {
- const filter = node => {
- return node.type === 'TextInput';
- };
-
- const { queryAllByTestId } = render(hi);
- expect(queryAllByTestId('test', { filter }).length).toEqual(0);
-});
diff --git a/src/__tests__/hooks/async-hook.js b/src/__tests__/hooks/async-hook.js
deleted file mode 100644
index 88a70f4..0000000
--- a/src/__tests__/hooks/async-hook.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import { useState, useEffect } from 'react';
-
-import { act, renderHook } from '../../';
-
-const getSomeName = () => Promise.resolve('Betty');
-
-const useName = prefix => {
- const [name, setName] = useState('nobody');
-
- useEffect(() => {
- getSomeName().then(theName => {
- act(() => {
- setName(prefix ? `${prefix} ${theName}` : theName);
- });
- });
- }, [prefix]);
-
- return name;
-};
-
-test('should wait for next update', async () => {
- const { result, waitForNextUpdate } = renderHook(() => useName());
-
- expect(result.current).toBe('nobody');
-
- await waitForNextUpdate();
-
- expect(result.current).toBe('Betty');
-});
-
-test('should wait for multiple updates', async () => {
- const { result, waitForNextUpdate, rerender } = renderHook(({ prefix }) => useName(prefix), {
- initialProps: { prefix: 'Mrs.' },
- });
-
- expect(result.current).toBe('nobody');
-
- await waitForNextUpdate();
-
- expect(result.current).toBe('Mrs. Betty');
-
- rerender({ prefix: 'Ms.' });
-
- await waitForNextUpdate();
-
- expect(result.current).toBe('Ms. Betty');
-});
-
-test('should resolve all when updating', async () => {
- const { result, waitForNextUpdate } = renderHook(({ prefix }) => useName(prefix), {
- initialProps: { prefix: 'Mrs.' },
- });
-
- expect(result.current).toBe('nobody');
-
- await Promise.all([waitForNextUpdate(), waitForNextUpdate(), waitForNextUpdate()]);
-
- expect(result.current).toBe('Mrs. Betty');
-});
diff --git a/src/__tests__/hooks/custom-hook.js b/src/__tests__/hooks/custom-hook.js
deleted file mode 100644
index 41d0059..0000000
--- a/src/__tests__/hooks/custom-hook.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import { useState, useCallback } from 'react';
-
-import { renderHook, act } from '../../';
-
-function useCounter(initialCount = 0) {
- const [count, setCount] = useState(initialCount);
-
- const incrementBy = useCallback(n => setCount(count + n), [count]);
- const decrementBy = useCallback(n => setCount(count - n), [count]);
-
- return { count, incrementBy, decrementBy };
-}
-
-test('should create counter', () => {
- const { result } = renderHook(() => useCounter());
-
- expect(result.current.count).toBe(0);
-});
-
-test('should increment counter', () => {
- const { result } = renderHook(() => useCounter());
-
- act(() => result.current.incrementBy(1));
-
- expect(result.current.count).toBe(1);
-
- act(() => result.current.incrementBy(2));
-
- expect(result.current.count).toBe(3);
-});
-
-test('should decrement counter', () => {
- const { result } = renderHook(() => useCounter());
-
- act(() => result.current.decrementBy(1));
-
- expect(result.current.count).toBe(-1);
-
- act(() => result.current.decrementBy(2));
-
- expect(result.current.count).toBe(-3);
-});
diff --git a/src/__tests__/hooks/error-hook.js b/src/__tests__/hooks/error-hook.js
deleted file mode 100644
index 145ef21..0000000
--- a/src/__tests__/hooks/error-hook.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import { useState, useEffect } from 'react';
-
-import { act, renderHook } from '../../';
-
-function useError(throwError) {
- if (throwError) {
- throw new Error('expected');
- }
- return true;
-}
-
-const somePromise = () => Promise.resolve();
-
-function useAsyncError(throwError) {
- const [value, setValue] = useState();
- useEffect(() => {
- somePromise().then(() => {
- act(() => {
- setValue(throwError);
- });
- });
- }, [throwError]);
- return useError(value);
-}
-
-test('should raise error', () => {
- const { result } = renderHook(() => useError(true));
-
- expect(() => {
- expect(result.current).not.toBe(undefined);
- }).toThrow(Error('expected'));
-});
-
-test('should capture error', () => {
- const { result } = renderHook(() => useError(true));
-
- expect(result.error).toEqual(Error('expected'));
-});
-
-test('should not capture error', () => {
- const { result } = renderHook(() => useError(false));
-
- expect(result.current).not.toBe(undefined);
- expect(result.error).toBe(undefined);
-});
-
-test('should reset error', () => {
- const { result, rerender } = renderHook(throwError => useError(throwError), {
- initialProps: true,
- });
-
- expect(result.error).not.toBe(undefined);
-
- rerender(false);
-
- expect(result.current).not.toBe(undefined);
- expect(result.error).toBe(undefined);
-});
-
-test('should raise async error', async () => {
- const { result, waitForNextUpdate } = renderHook(() => useAsyncError(true));
-
- await waitForNextUpdate();
-
- expect(() => {
- expect(result.current).not.toBe(undefined);
- }).toThrow(Error('expected'));
-});
-
-test('should capture async error', async () => {
- const { result, waitForNextUpdate } = renderHook(() => useAsyncError(true));
-
- await waitForNextUpdate();
-
- expect(result.error).toEqual(Error('expected'));
-});
-
-test('should not capture async error', async () => {
- const { result, waitForNextUpdate } = renderHook(() => useAsyncError(false));
-
- await waitForNextUpdate();
-
- expect(result.current).not.toBe(undefined);
- expect(result.error).toBe(undefined);
-});
-
-test('should reset async error', async () => {
- const { result, waitForNextUpdate, rerender } = renderHook(
- throwError => useAsyncError(throwError),
- {
- initialProps: true,
- },
- );
-
- await waitForNextUpdate();
-
- expect(result.error).not.toBe(undefined);
-
- rerender(false);
-
- await waitForNextUpdate();
-
- expect(result.current).not.toBe(undefined);
- expect(result.error).toBe(undefined);
-});
diff --git a/src/__tests__/hooks/test-hook.js b/src/__tests__/hooks/test-hook.js
deleted file mode 100644
index dfec1e8..0000000
--- a/src/__tests__/hooks/test-hook.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import React, { useState, useEffect } from 'react';
-
-import { act, renderHook } from '../../';
-
-test('renderHook calls the callback', () => {
- const spy = jest.fn();
- renderHook(spy);
- expect(spy).toHaveBeenCalledTimes(1);
-});
-
-test('confirm we can safely call a React Hook from within the callback', () => {
- renderHook(() => useState());
-});
-
-test('returns a function to unmount component', () => {
- let isMounted;
- const { unmount } = renderHook(() => {
- useEffect(() => {
- isMounted = true;
- return () => {
- isMounted = false;
- };
- });
- });
- expect(isMounted).toBe(true);
- unmount();
- expect(isMounted).toBe(false);
-});
-
-test('returns a function to rerender component', () => {
- let renderCount = 0;
- const { rerender } = renderHook(() => {
- useEffect(() => {
- renderCount++;
- });
- });
-
- expect(renderCount).toBe(1);
- rerender();
- expect(renderCount).toBe(2);
-});
-
-test('accepts wrapper option to wrap rendered hook with', () => {
- const ctxA = React.createContext();
- const ctxB = React.createContext();
- const useHook = () => {
- return React.useContext(ctxA) * React.useContext(ctxB);
- };
- let actual;
- renderHook(
- () => {
- actual = useHook();
- },
- {
- // eslint-disable-next-line react/display-name
- wrapper: props => (
-
-
-
- ),
- },
- );
- expect(actual).toBe(12);
-});
-
-test('returns result ref with latest result from hook execution', () => {
- function useCounter({ initialCount = 0, step = 1 } = {}) {
- const [count, setCount] = React.useState(initialCount);
- const increment = () => setCount(c => c + step);
- const decrement = () => setCount(c => c - step);
- return { count, increment, decrement };
- }
-
- const { result } = renderHook(useCounter);
- expect(result.current.count).toBe(0);
- act(() => {
- result.current.increment();
- });
- expect(result.current.count).toBe(1);
-});
diff --git a/src/__tests__/hooks/use-context.js b/src/__tests__/hooks/use-context.js
deleted file mode 100644
index 7e6afe3..0000000
--- a/src/__tests__/hooks/use-context.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import React, { createContext, useContext } from 'react';
-
-import { renderHook } from '../../';
-
-test('should get default value from context', () => {
- const TestContext = createContext('foo');
-
- const { result } = renderHook(() => useContext(TestContext));
-
- const value = result.current;
-
- expect(value).toBe('foo');
-});
-
-test('should get value from context provider', () => {
- const TestContext = createContext('foo');
-
- const wrapper = ({ children }) => (
- {children}
- );
-
- const { result } = renderHook(() => useContext(TestContext), { wrapper });
-
- expect(result.current).toBe('bar');
-});
-
-test('should update value in context', () => {
- const TestContext = createContext('foo');
-
- const value = { current: 'bar' };
-
- const wrapper = ({ children }) => (
- {children}
- );
-
- const { result, rerender } = renderHook(() => useContext(TestContext), { wrapper });
-
- value.current = 'baz';
-
- rerender();
-
- expect(result.current).toBe('baz');
-});
diff --git a/src/__tests__/hooks/use-effect.js b/src/__tests__/hooks/use-effect.js
deleted file mode 100644
index 5eacd80..0000000
--- a/src/__tests__/hooks/use-effect.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { useEffect, useLayoutEffect } from 'react';
-
-import { renderHook } from '../../';
-
-test('should handle useEffect hook', () => {
- const sideEffect = { [1]: false, [2]: false };
-
- const { rerender, unmount } = renderHook(
- ({ id }) => {
- useEffect(() => {
- sideEffect[id] = true;
- return () => {
- sideEffect[id] = false;
- };
- }, [id]);
- },
- { initialProps: { id: 1 } },
- );
-
- expect(sideEffect[1]).toBe(true);
- expect(sideEffect[2]).toBe(false);
-
- rerender({ id: 2 });
-
- expect(sideEffect[1]).toBe(false);
- expect(sideEffect[2]).toBe(true);
-
- unmount();
-
- expect(sideEffect[1]).toBe(false);
- expect(sideEffect[2]).toBe(false);
-});
-
-test('should handle useLayoutEffect hook', () => {
- const sideEffect = { [1]: false, [2]: false };
-
- const { rerender, unmount } = renderHook(
- ({ id }) => {
- useLayoutEffect(() => {
- sideEffect[id] = true;
- return () => {
- sideEffect[id] = false;
- };
- }, [id]);
- },
- { initialProps: { id: 1 } },
- );
-
- expect(sideEffect[1]).toBe(true);
- expect(sideEffect[2]).toBe(false);
-
- rerender({ id: 2 });
-
- expect(sideEffect[1]).toBe(false);
- expect(sideEffect[2]).toBe(true);
-
- unmount();
-
- expect(sideEffect[1]).toBe(false);
- expect(sideEffect[2]).toBe(false);
-});
diff --git a/src/__tests__/hooks/use-memo.js b/src/__tests__/hooks/use-memo.js
deleted file mode 100644
index b2c5e06..0000000
--- a/src/__tests__/hooks/use-memo.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import { useMemo, useCallback } from 'react';
-
-import { renderHook } from '../../';
-
-test('should handle useMemo hook', () => {
- const { result, rerender } = renderHook(({ value }) => useMemo(() => ({ value }), [value]), {
- initialProps: { value: 1 },
- });
-
- const value1 = result.current;
-
- expect(value1).toEqual({ value: 1 });
-
- rerender();
-
- const value2 = result.current;
-
- expect(value2).toEqual({ value: 1 });
-
- expect(value2).toBe(value1);
-
- rerender({ value: 2 });
-
- const value3 = result.current;
-
- expect(value3).toEqual({ value: 2 });
-
- expect(value3).not.toBe(value1);
-});
-
-test('should handle useCallback hook', () => {
- const { result, rerender } = renderHook(
- ({ value }) => {
- const callback = () => ({ value });
- return useCallback(callback, [value]);
- },
- { initialProps: { value: 1 } },
- );
-
- const callback1 = result.current;
-
- const calbackValue1 = callback1();
-
- expect(calbackValue1).toEqual({ value: 1 });
-
- const callback2 = result.current;
-
- const calbackValue2 = callback2();
-
- expect(calbackValue2).toEqual({ value: 1 });
-
- expect(callback2).toBe(callback1);
-
- rerender({ value: 2 });
-
- const callback3 = result.current;
-
- const calbackValue3 = callback3();
-
- expect(calbackValue3).toEqual({ value: 2 });
-
- expect(callback3).not.toBe(callback1);
-});
diff --git a/src/__tests__/hooks/use-reducer.js b/src/__tests__/hooks/use-reducer.js
deleted file mode 100644
index 51f6170..0000000
--- a/src/__tests__/hooks/use-reducer.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { useReducer } from 'react';
-
-import { renderHook, act } from '../../';
-
-test('should handle useReducer hook', () => {
- const reducer = (state, action) => (action.type === 'inc' ? state + 1 : state);
- const { result } = renderHook(() => useReducer(reducer, 0));
-
- const [initialState, dispatch] = result.current;
-
- expect(initialState).toBe(0);
-
- act(() => dispatch({ type: 'inc' }));
-
- const [state] = result.current;
-
- expect(state).toBe(1);
-});
diff --git a/src/__tests__/hooks/use-ref.js b/src/__tests__/hooks/use-ref.js
deleted file mode 100644
index e7d8896..0000000
--- a/src/__tests__/hooks/use-ref.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { useRef, useImperativeHandle } from 'react';
-
-import { renderHook } from '../../';
-
-test('should handle useRef hook', () => {
- const { result } = renderHook(() => useRef());
-
- const refContainer = result.current;
-
- expect(Object.keys(refContainer)).toEqual(['current']);
- expect(refContainer.current).toBeUndefined();
-});
-
-test('should handle useImperativeHandle hook', () => {
- const { result } = renderHook(() => {
- const ref = useRef();
- useImperativeHandle(ref, () => ({
- fakeImperativeMethod: () => true,
- }));
- return ref;
- });
-
- const refContainer = result.current;
-
- expect(refContainer.current.fakeImperativeMethod()).toBe(true);
-});
diff --git a/src/__tests__/hooks/use-state.js b/src/__tests__/hooks/use-state.js
deleted file mode 100644
index 8cbafc6..0000000
--- a/src/__tests__/hooks/use-state.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { useState } from 'react';
-
-import { renderHook, act } from '../../';
-
-test('should use setState value', () => {
- const { result } = renderHook(() => useState('foo'));
-
- const [value] = result.current;
-
- expect(value).toBe('foo');
-});
-
-test('should update setState value using setter', () => {
- const { result } = renderHook(() => useState('foo'));
-
- const [_, setValue] = result.current;
-
- act(() => setValue('bar'));
-
- const [value] = result.current;
-
- expect(value).toBe('bar');
-});
diff --git a/src/__tests__/misc.js b/src/__tests__/misc.js
deleted file mode 100644
index a326316..0000000
--- a/src/__tests__/misc.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import { View } from 'react-native';
-
-import { render } from '../';
-import { queryByProp, queryByTestId } from '../';
-
-// we used to use queryByProp internally, but we don't anymore. Some people
-// use it as an undocumented part of the API, so we'll keep it around.
-test('queryByProp', () => {
- const { container } = render(
-
-
-
-
- ,
- );
-
- expect(queryByTestId(container, 'foo')).not.toBeNull();
- expect(queryByProp('importantForAccessibility', container, 'auto')).toBeNull();
- expect(() => queryByProp('importantForAccessibility', container, /no/)).toThrow(
- /multiple elements/,
- );
-});
diff --git a/src/__tests__/query-helpers.js b/src/__tests__/query-helpers.js
deleted file mode 100644
index ef7127b..0000000
--- a/src/__tests__/query-helpers.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { defaultFilter, filterNodeByType } from '../query-helpers';
-
-test('filterNodeByType returns `true` when node.type matches the provided type', () => {
- expect(filterNodeByType({ type: 'Text' }, 'Text')).toEqual(true);
-});
-test('filterNodeByType returns `false` when node.type does not match the provided type', () => {
- expect(filterNodeByType({ type: 'Text' }, 'Test')).toEqual(false);
-});
-
-test('defaultFilter returns `true` when node.type is in the mocked type list', () => {
- expect(defaultFilter({ type: 'Text' })).toEqual(true);
-});
-test('defaultFilter returns `false` when node.type is not in the mocked type list', () => {
- expect(defaultFilter({ type: 'Test' })).toEqual(false);
-});
diff --git a/src/hooks.js b/src/hooks.js
deleted file mode 100644
index fc7758d..0000000
--- a/src/hooks.js
+++ /dev/null
@@ -1,41 +0,0 @@
-function TestHook({ callback, hookProps, children }) {
- try {
- children(callback(hookProps));
- } catch (e) {
- children(undefined, e);
- }
-
- return null;
-}
-
-function resultContainer() {
- let value = null;
- let error = null;
- const resolvers = [];
-
- const result = {
- get current() {
- if (error) {
- throw error;
- }
- return value;
- },
- get error() {
- return error;
- },
- };
-
- return {
- result,
- addResolver: resolver => {
- resolvers.push(resolver);
- },
- updateResult: (val, err) => {
- value = val;
- error = err;
- resolvers.splice(0, resolvers.length).forEach(resolve => resolve());
- },
- };
-}
-
-export { resultContainer, TestHook };
diff --git a/src/index.d.ts b/src/index.d.ts
deleted file mode 100644
index 354b3c7..0000000
--- a/src/index.d.ts
+++ /dev/null
@@ -1,318 +0,0 @@
-import { ReactElement, ComponentType } from 'react'
-import { ReactTestInstance, ReactTestRenderer, act } from 'react-test-renderer'
-import { OptionsReceived } from 'pretty-format'
-import {
- NativeSyntheticEvent,
- TextInputFocusEventData,
- TextInputChangeEventData,
- TextInputContentSizeChangeEventData,
- TextInputEndEditingEventData,
- TextInputKeyPressEventData,
- TextInputSubmitEditingEventData,
- LayoutChangeEvent,
- TextInputSelectionChangeEventData,
- GestureResponderEvent,
- ScrollResponderEvent,
- ImageLoadEventData,
- ImageErrorEventData,
- ImageProgressEventDataIOS,
-} from 'react-native'
-
-// EVENTS
-// ------
-
-type EventInit = Partial> & { validTargets?: string[] }
-
-export declare class NativeEvent {
- constructor(type: 'focus', init?: EventInit)
- constructor(type: 'blur', init?: EventInit)
- constructor(type: 'change', init?: EventInit)
- constructor(type: 'changeText', value: string)
- constructor(type: 'contentSizeChange', init?: EventInit)
- constructor(type: 'endEditing', init?: EventInit)
- constructor(type: 'keyPress', init?: EventInit)
- constructor(type: 'submitEditing', init?: EventInit)
- constructor(type: 'layout', init?: EventInit)
- constructor(type: 'selectionChange', init?: EventInit)
- constructor(type: 'longPress', init?: EventInit)
- constructor(type: 'press', init?: EventInit)
- constructor(type: 'pressIn', init?: EventInit)
- constructor(type: 'pressOut', init?: EventInit)
- constructor(type: 'momentumScrollBegin', init?: EventInit)
- constructor(type: 'momentumScrollEnd', init?: EventInit)
- constructor(type: 'scroll', init?: EventInit)
- constructor(type: 'scrollBeginDrag', init?: EventInit)
- constructor(type: 'scrollEndDrag', init?: EventInit)
- constructor(type: 'load', init?: EventInit)
- constructor(type: 'error', init?: EventInit)
- constructor(type: 'progress', init?: EventInit)
-}
-
-export declare function getEventHandlerName(key: string): string
-
-export interface FireEventFn {
- (element: NativeTestInstance, event: NativeEvent): any
- focus(element: NativeTestInstance, init?: EventInit): any
- blur(element: NativeTestInstance, init?: EventInit): any
- change(element: NativeTestInstance, init?: EventInit): any
- changeText(element: NativeTestInstance, value: string): any
- contentSizeChange(element: NativeTestInstance, init?: EventInit): any
- endEditing(element: NativeTestInstance, init?: EventInit): any
- keyPress(element: NativeTestInstance, init?: EventInit): any
- submitEditing(element: NativeTestInstance, init?: EventInit): any
- layout(element: NativeTestInstance, init?: EventInit): any
- selectionChange(element: NativeTestInstance, init?: EventInit): any
- longPress(element: NativeTestInstance, init?: EventInit): any
- press(element: NativeTestInstance, init?: EventInit): any
- pressIn(element: NativeTestInstance, init?: EventInit): any
- pressOut(element: NativeTestInstance, init?: EventInit): any
- momentumScrollBegin(element: NativeTestInstance, init?: EventInit): any
- momentumScrollEnd(element: NativeTestInstance, init?: EventInit): any
- scroll(element: NativeTestInstance, init?: EventInit): any
- scrollBeginDrag(element: NativeTestInstance, init?: EventInit): any
- scrollEndDrag(element: NativeTestInstance, init?: EventInit): any
- load(element: NativeTestInstance, init?: EventInit): any
- error(element: NativeTestInstance, init?: EventInit): any
- progress(element: NativeTestInstance, init?: EventInit): any
-}
-
-export declare const fireEvent: FireEventFn
-
-// GET NODE TEXT
-// -------------
-
-export declare function getNodeText(node: NativeTestInstance): string
-
-// GET QUERIES FOR ELEMENT
-// -----------------------
-
-export declare function getQueriesForElement(element: ReactElement, queries?: T): BoundQueries
-export declare function within(element: ReactElement, queries?: T): BoundQueries
-
-// PREETY PRINT
-// ------------
-
-export declare function prettyPrint(element: ReactTestRenderer | NativeTestInstance | string, maxLength?: number, options?: OptionsReceived): string
-
-// QUERIES
-// -------
-
-type Omit = Pick>
-type Bound = T extends (arg: any, ...rest: infer U) => infer V ? (...args: U) => V : never
-type BoundQueries = { [P in keyof T]: Bound }
-
-export type NativeTestInstance = Omit
-
-export type TextMatch = string | RegExp | ((value: string) => boolean)
-export type FilterFn = (value: string, index: number) => boolean
-export type NormalizerFn = (input: string) => string
-
-export interface NormalizerOptions {
- exact?: boolean,
- trim?: boolean,
- collapseWhitespace?: boolean,
- filter?: FilterFn,
- normalizer?: NormalizerFn,
-}
-
-export interface TextNormalizerOptions extends NormalizerOptions {
- types?: string[]
-}
-
-export declare function getByA11yHint(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance
-export declare function getByA11yLabel(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance
-export declare function getByA11yRole(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance
-export declare function getByA11yStates(container: NativeTestInstance, match: string[], options?: NormalizerOptions): NativeTestInstance
-export declare function getByA11yTraits(container: NativeTestInstance, match: string[], options?: NormalizerOptions): NativeTestInstance
-export declare function getByPlaceholder(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance
-export declare function getByText(container: NativeTestInstance, match: TextMatch, options?: TextNormalizerOptions): NativeTestInstance
-export declare function getByValue(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance
-export declare function getByTestId(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance
-
-export declare function getAllByA11yHint(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance[]
-export declare function getAllByA11yLabel(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance[]
-export declare function getAllByA11yRole(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance[]
-export declare function getAllByA11yStates(container: NativeTestInstance, match: string[], options?: NormalizerOptions): NativeTestInstance[]
-export declare function getAllByA11yTraits(container: NativeTestInstance, match: string[], options?: NormalizerOptions): NativeTestInstance[]
-export declare function getAllByPlaceholder(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance[]
-export declare function getAllByText(container: NativeTestInstance, match: TextMatch, options?: TextNormalizerOptions): NativeTestInstance[]
-export declare function getAllByValue(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance[]
-export declare function getAllByTestId(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance[]
-
-export declare function queryByA11yHint(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance | null
-export declare function queryByA11yLabel(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance | null
-export declare function queryByA11yRole(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance | null
-export declare function queryByA11yStates(container: NativeTestInstance, match: string[], options?: NormalizerOptions): NativeTestInstance | null
-export declare function queryByA11yTraits(container: NativeTestInstance, match: string[], options?: NormalizerOptions): NativeTestInstance | null
-export declare function queryByPlaceholder(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance | null
-export declare function queryByText(container: NativeTestInstance, match: TextMatch, options?: TextNormalizerOptions): NativeTestInstance | null
-export declare function queryByValue(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance | null
-export declare function queryByTestId(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): NativeTestInstance | null
-
-export declare function findByA11yHint(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): Promise
-export declare function findByA11yLabel(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): Promise
-export declare function findByA11yRole(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): Promise
-export declare function findByA11yStates(container: NativeTestInstance, match: string[], options?: NormalizerOptions): Promise
-export declare function findByA11yTraits(container: NativeTestInstance, match: string[], options?: NormalizerOptions): Promise
-export declare function findByPlaceholder(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): Promise
-export declare function findByText(container: NativeTestInstance, match: TextMatch, options?: TextNormalizerOptions): Promise
-export declare function findByValue(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): Promise
-export declare function findByTestId(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): Promise
-
-export declare function findAllByA11yHint(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): Promise
-export declare function findAllByA11yLabel(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): Promise
-export declare function findAllByA11yRole(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): Promise
-export declare function findAllByA11yStates(container: NativeTestInstance, match: string[], options?: NormalizerOptions): Promise
-export declare function findAllByA11yTraits(container: NativeTestInstance, match: string[], options?: NormalizerOptions): Promise
-export declare function findAllByPlaceholder(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): Promise
-export declare function findAllByText(container: NativeTestInstance, match: TextMatch, options?: TextNormalizerOptions): Promise
-export declare function findAllByValue(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): Promise
-export declare function findAllByTestId(container: NativeTestInstance, match: TextMatch, options?: NormalizerOptions): Promise
-
-export interface Queries {
- getByA11yHint: typeof getByA11yHint
- getByA11yLabel: typeof getByA11yLabel
- getByA11yRole: typeof getByA11yRole
- getByA11yStates: typeof getByA11yStates
- getByA11yTraits: typeof getByA11yTraits
- getByPlaceholder: typeof getByPlaceholder
- getByText: typeof getByText
- getByValue: typeof getByA11yHint
- getByTestId: typeof getByTestId
-
- getAllByA11yHint: typeof getAllByA11yHint
- getAllByA11yLabel: typeof getAllByA11yLabel
- getAllByA11yRole: typeof getAllByA11yRole
- getAllByA11yStates: typeof getAllByA11yStates
- getAllByA11yTraits: typeof getAllByA11yTraits
- getAllByPlaceholder: typeof getAllByPlaceholder
- getAllByText: typeof getAllByText
- getAllByValue: typeof getAllByA11yHint
- getAllByTestId: typeof getAllByTestId
-
- queryByA11yHint: typeof queryByA11yHint
- queryByA11yLabel: typeof queryByA11yLabel
- queryByA11yRole: typeof queryByA11yRole
- queryByA11yStates: typeof queryByA11yStates
- queryByA11yTraits: typeof queryByA11yTraits
- queryByPlaceholder: typeof queryByPlaceholder
- queryByText: typeof queryByText
- queryByValue: typeof queryByA11yHint
- queryByTestId: typeof queryByTestId
-
- findByA11yHint: typeof findByA11yHint
- findByA11yLabel: typeof findByA11yLabel
- findByA11yRole: typeof findByA11yRole
- findByA11yStates: typeof findByA11yStates
- findByA11yTraits: typeof findByA11yTraits
- findByPlaceholder: typeof findByPlaceholder
- findByText: typeof findByText
- findByValue: typeof findByA11yHint
- findByTestId: typeof findByTestId
-
- findAllByA11yHint: typeof findAllByA11yHint
- findAllByA11yLabel: typeof findAllByA11yLabel
- findAllByA11yRole: typeof findAllByA11yRole
- findAllByA11yStates: typeof findAllByA11yStates
- findAllByA11yTraits: typeof findAllByA11yTraits
- findAllByPlaceholder: typeof findAllByPlaceholder
- findAllByText: typeof findAllByText
- findAllByValue: typeof findAllByA11yHint
- findAllByTestId: typeof findAllByTestId
-}
-
-// QUERY HELPERS
-// -------------
-
-export declare function defaultFilter(node: NativeTestInstance): boolean
-export declare function getBaseElement(container: ReactTestRenderer | ReactTestInstance): ReactTestInstance
-export declare function getElementError(message: string, container: ReactTestRenderer): Error
-export declare function filterNodeByType(node: NativeTestInstance, type: string): boolean
-export declare function queryAllByProp(
- attribute: string,
- container: ReactTestRenderer,
- match: TextMatch,
- options?: NormalizerOptions,
-): NativeTestInstance[]
-export declare function queryByProp(
- attribute: string,
- container: ReactTestRenderer,
- match: TextMatch,
- options?: NormalizerOptions,
-): NativeTestInstance | null
-export function removeBadProperties(node: ReactTestInstance): NativeTestInstance
-
-// WAIT
-// ----
-
-export interface WaitOptions {
- timeout?: number
- interval?: number
-}
-export declare function wait(callback?: () => void, options?: WaitOptions): Promise
-
-// WAIT FOR ELEMENT
-// ----------------
-
-export interface WaitOptions {
- timeout?: number
- interval?: number
-}
-export declare function waitForElement(callback: () => T, options?: WaitOptions): Promise
-
-// WAIT FOR ELEMENT TO BE REMOVED
-// ------------------------------
-
-export interface WaitOptions {
- timeout?: number
- interval?: number
-}
-export declare function waitForElementToBeRemoved(callback: () => any, options?: WaitOptions): Promise
-
-// MATCHES
-// -------
-
-export interface DefaultNormalizerOptions {
- trim?: boolean,
- collapseWhitespace?: boolean,
-}
-export declare function getDefaultNormalizer(options: DefaultNormalizerOptions): NormalizerFn
-
-// INDEX
-// -----
-
-export interface RenderOptions {
- wrapper?: ComponentType<{ children: ReactElement }>
-}
-export interface RenderOptionsWithQueries extends RenderOptions {
- queries?: T
-}
-
-export declare function render(ui: ReactElement, options?: RenderOptions): RenderResult & BoundQueries
-export declare function render(ui: ReactElement, options: RenderOptionsWithQueries): RenderResult & BoundQueries
-
-export interface RenderResult {
- container: ReactTestRenderer
- baseElement: NativeTestInstance
- debug: (el?: NativeTestInstance) => void
- rerender: (ui: ReactElement) => void
- unmount: () => void
-}
-
-export interface RenderHookOptions extends RenderOptions {
- initialProps?: T
-}
-
-export declare function renderHook(callback: (props: T) => U, options?: RenderHookOptions): RenderHookResult
-
-export interface RenderHookResult {
- result: {
- current: U
- }
- error?: Error
- waitForNextUpdate: () => Promise
- rerender: (newProps?: T) => void
- unmount: () => void
-}
-
-export { act }
diff --git a/src/index.js b/src/index.js
index 68304e4..f41a696 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,90 +1 @@
-import React from 'react';
-import TR from 'react-test-renderer';
-
-import act from './act-compat';
-import * as queries from './queries';
-import { prettyPrint } from './pretty-print';
-import * as queryHelpers from './query-helpers';
-import { resultContainer, TestHook } from './hooks';
-import { getQueriesForElement } from './get-queries-for-element';
-import { fireEvent as rntlFireEvent, NativeEvent } from './events';
-
-function render(ui, { options = {}, wrapper: WrapperComponent } = {}) {
- const wrapUiIfNeeded = innerElement =>
- WrapperComponent ? {innerElement} : innerElement;
-
- let container = {};
-
- act(() => {
- container = TR.create(wrapUiIfNeeded(ui), options);
- });
-
- const baseElement = queryHelpers.removeBadProperties(container.root);
-
- return {
- container,
- baseElement,
- debug: (el = baseElement) => console.log(prettyPrint(el)),
- unmount: () => container.unmount(),
- rerender: rerenderUi => {
- act(() => {
- container.update(wrapUiIfNeeded(rerenderUi));
- });
- },
- ...getQueriesForElement(container),
- };
-}
-
-function renderHook(callback, { initialProps, ...options } = {}) {
- const { result, updateResult, addResolver } = resultContainer();
- const hookProps = { current: initialProps };
-
- const toRender = () => (
-
- {updateResult}
-
- );
-
- const { unmount, rerender: rerenderComponent } = render(toRender(), options);
-
- return {
- result,
- waitForNextUpdate: () => new Promise(resolve => addResolver(resolve)),
- rerender: (newProps = hookProps.current) => {
- hookProps.current = newProps;
- rerenderComponent(toRender());
- },
- unmount,
- };
-}
-
-function fireEvent(...args) {
- let returnValue;
- act(() => {
- returnValue = rntlFireEvent(...args);
- });
- return returnValue;
-}
-
-Object.keys(rntlFireEvent).forEach(key => {
- fireEvent[key] = (...args) => {
- let returnValue;
- act(() => {
- returnValue = rntlFireEvent[key](...args);
- });
- return returnValue;
- };
-});
-
-export * from './events';
-export * from './get-node-text';
-export * from './get-queries-for-element';
-export * from './pretty-print';
-export * from './queries';
-export * from './query-helpers';
-export * from './wait';
-export * from './wait-for-element';
-export * from './wait-for-element-to-be-removed';
-export { getDefaultNormalizer } from './matches';
-
-export { act, fireEvent, queries, queryHelpers, render, renderHook, NativeEvent };
+export * from './lib';
diff --git a/src/lib/__tests__/__snapshots__/fetch.js.snap b/src/lib/__tests__/__snapshots__/fetch.js.snap
new file mode 100644
index 0000000..a9e9abd
--- /dev/null
+++ b/src/lib/__tests__/__snapshots__/fetch.js.snap
@@ -0,0 +1,17 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Fetch makes an API call and displays the greeting when load-greeting is clicked 1`] = `
+
+
+
+ Fetch
+
+
+
+ hello there
+
+
+`;
diff --git a/src/__tests__/act.js b/src/lib/__tests__/act.js
similarity index 89%
rename from src/__tests__/act.js
rename to src/lib/__tests__/act.js
index ec5daf4..84791c7 100644
--- a/src/__tests__/act.js
+++ b/src/lib/__tests__/act.js
@@ -22,11 +22,11 @@ test('fireEvent triggers useEffect calls', () => {
const [count, setCount] = React.useState(0);
return
),
},
+ getByTitle: {
+ query: /his/,
+ tree: (
+
+
+
+
+ ),
+ },
getByValue: {
query: /his/,
tree: (
@@ -174,6 +183,15 @@ cases(
),
},
+ queryByTitle: {
+ query: /his/,
+ tree: (
+
+
+
+
+ ),
+ },
queryByValue: {
query: /his/,
tree: (
diff --git a/src/__tests__/helpers.js b/src/lib/__tests__/helpers.js
similarity index 100%
rename from src/__tests__/helpers.js
rename to src/lib/__tests__/helpers.js
diff --git a/src/__tests__/matches.js b/src/lib/__tests__/matches.js
similarity index 100%
rename from src/__tests__/matches.js
rename to src/lib/__tests__/matches.js
diff --git a/src/lib/__tests__/misc.js b/src/lib/__tests__/misc.js
new file mode 100644
index 0000000..a4d419b
--- /dev/null
+++ b/src/lib/__tests__/misc.js
@@ -0,0 +1,37 @@
+import React from 'react';
+import { Picker, View } from 'react-native';
+
+import { fireEvent, render, queryByProp, queryByTestId } from '../';
+
+test('queryByProp', () => {
+ const { container } = render(
+
+
+
+
+ ,
+ );
+
+ expect(queryByTestId(container, 'foo')).not.toBeNull();
+ expect(queryByProp('importantForAccessibility', container, 'auto')).toBeNull();
+ expect(() => queryByProp('importantForAccessibility', container, /no/)).toThrow(
+ /multiple elements/,
+ );
+});
+
+test('picker', () => {
+ function Wrapper() {
+ const [value, setValue] = React.useState('js');
+
+ return (
+ setValue(itemValue)}>
+
+
+
+ );
+ }
+ const { findByValue, getByValue } = render();
+
+ fireEvent.valueChange(getByValue('js'), 'java');
+ expect(() => findByValue('js')).not.toThrow();
+});
diff --git a/src/__tests__/no-act.js b/src/lib/__tests__/no-act.js
similarity index 100%
rename from src/__tests__/no-act.js
rename to src/lib/__tests__/no-act.js
diff --git a/src/__tests__/old-act.js b/src/lib/__tests__/old-act.js
similarity index 100%
rename from src/__tests__/old-act.js
rename to src/lib/__tests__/old-act.js
diff --git a/src/__tests__/pretty-print.js b/src/lib/__tests__/pretty-print.js
similarity index 66%
rename from src/__tests__/pretty-print.js
rename to src/lib/__tests__/pretty-print.js
index 12f8bfe..9fc51b5 100644
--- a/src/__tests__/pretty-print.js
+++ b/src/lib/__tests__/pretty-print.js
@@ -5,19 +5,19 @@ import { render } from '../';
import { prettyPrint } from '../pretty-print';
test('it prints correctly with no children', () => {
- const { baseElement } = render();
+ const { container } = render();
- expect(prettyPrint(baseElement)).toMatchInlineSnapshot(`"[36m[39m"`);
+ expect(prettyPrint(container.toJSON())).toMatchInlineSnapshot(`"[36m[39m"`);
});
test('it prints correctly with one child', () => {
- const { baseElement } = render(
+ const { container } = render(
Hello World!
,
);
- expect(prettyPrint(baseElement)).toMatchInlineSnapshot(`
+ expect(prettyPrint(container.toJSON())).toMatchInlineSnapshot(`
"[36m[39m
[36m[39m
[0mHello World![0m
@@ -27,14 +27,14 @@ test('it prints correctly with one child', () => {
});
test('it prints correctly with multiple children', () => {
- const { baseElement } = render(
+ const { container } = render(
Hello
World!
,
);
- expect(prettyPrint(baseElement)).toMatchInlineSnapshot(`
+ expect(prettyPrint(container.toJSON())).toMatchInlineSnapshot(`
"[36m[39m
[36m[39m
[0mHello[0m
@@ -47,11 +47,11 @@ test('it prints correctly with multiple children', () => {
});
test('it supports truncating the output length', () => {
- const { baseElement } = render(
+ const { container } = render(
Hello World!
,
);
- expect(prettyPrint(baseElement, 5)).toMatch(/\.\.\./);
+ expect(prettyPrint(container.toJSON(), 5)).toMatch(/\.\.\./);
});
diff --git a/src/__tests__/queries.find.js b/src/lib/__tests__/queries.find.js
similarity index 90%
rename from src/__tests__/queries.find.js
rename to src/lib/__tests__/queries.find.js
index b23b6fa..d730a90 100644
--- a/src/__tests__/queries.find.js
+++ b/src/lib/__tests__/queries.find.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { Image, Text, TextInput, View } from 'react-native';
+import { Button, Image, Text, TextInput, View } from 'react-native';
import { render } from '../.';
@@ -13,6 +13,7 @@ test('find asynchronously finds elements', async () => {
findAllByPlaceholder,
findAllByTestId,
findAllByText,
+ findAllByTitle,
findAllByValue,
findByA11yHint,
findByA11yLabel,
@@ -22,12 +23,14 @@ test('find asynchronously finds elements', async () => {
findByPlaceholder,
findByTestId,
findByText,
+ findByTitle,
findByValue,
} = render(
test text content
+
@@ -56,6 +59,9 @@ test('find asynchronously finds elements', async () => {
await expect(findByText('test text content')).resolves.toBeTruthy();
await expect(findAllByText('test text content')).resolves.toHaveLength(1);
+ await expect(findByTitle('button')).resolves.toBeTruthy();
+ await expect(findAllByTitle('button')).resolves.toHaveLength(1);
+
await expect(findByA11yRole('text')).resolves.toBeTruthy();
await expect(findAllByA11yRole('text')).resolves.toHaveLength(1);
@@ -76,6 +82,7 @@ test('find rejects when something cannot be found', async () => {
findAllByPlaceholder,
findAllByTestId,
findAllByText,
+ findAllByTitle,
findAllByValue,
findByA11yHint,
findByA11yLabel,
@@ -85,6 +92,7 @@ test('find rejects when something cannot be found', async () => {
findByPlaceholder,
findByTestId,
findByText,
+ findByTitle,
findByValue,
} = render();
@@ -112,6 +120,9 @@ test('find rejects when something cannot be found', async () => {
await expect(findByText('x', qo, wo)).rejects.toThrow('x');
await expect(findAllByText('x', qo, wo)).rejects.toThrow('x');
+ await expect(findByTitle('x', qo, wo)).rejects.toThrow('x');
+ await expect(findAllByTitle('x', qo, wo)).rejects.toThrow('x');
+
await expect(findByValue('x', qo, wo)).rejects.toThrow('x');
await expect(findAllByValue('x', qo, wo)).rejects.toThrow('x');
diff --git a/src/lib/__tests__/query-helpers.js b/src/lib/__tests__/query-helpers.js
new file mode 100644
index 0000000..7d4f777
--- /dev/null
+++ b/src/lib/__tests__/query-helpers.js
@@ -0,0 +1,33 @@
+import { defaultFilter, filterNodeByType, proxyUnsafeProperties } from '../query-helpers';
+
+test('filterNodeByType returns `true` when node.type matches the provided type', () => {
+ expect(filterNodeByType({ type: 'Text' }, 'Text')).toEqual(true);
+});
+test('filterNodeByType returns `false` when node.type does not match the provided type', () => {
+ expect(filterNodeByType({ type: 'Text' }, 'Test')).toEqual(false);
+});
+
+test('defaultFilter returns `true` when node.type is in the mocked type list', () => {
+ expect(defaultFilter({ type: 'Text' })).toEqual(true);
+});
+test('defaultFilter returns `false` when node.type is not in the mocked type list', () => {
+ expect(defaultFilter({ type: 'Test' })).toEqual(false);
+});
+
+test('proxyUnsafeProperties ignores what it should', () => {
+ const testElement = proxyUnsafeProperties({
+ _fiber: 'should work',
+ findAllByProps: jest.fn(),
+ findAllByType: jest.fn(),
+ findByProps: jest.fn(),
+ findByType: jest.fn(),
+ instance: jest.fn(),
+ });
+
+ expect(testElement._fiber).toBe('should work');
+ expect(testElement.findAllByProps).toBe(undefined);
+ expect(testElement.findAllByType).toBe(undefined);
+ expect(testElement.findByProps).toBe(undefined);
+ expect(testElement.findByType).toBe(undefined);
+ expect(testElement.instance).toBe(undefined);
+});
diff --git a/src/__tests__/render.js b/src/lib/__tests__/render.js
similarity index 100%
rename from src/__tests__/render.js
rename to src/lib/__tests__/render.js
diff --git a/src/__tests__/rerender.js b/src/lib/__tests__/rerender.js
similarity index 100%
rename from src/__tests__/rerender.js
rename to src/lib/__tests__/rerender.js
diff --git a/src/__tests__/stopwatch.js b/src/lib/__tests__/stopwatch.js
similarity index 92%
rename from src/__tests__/stopwatch.js
rename to src/lib/__tests__/stopwatch.js
index 58daeba..59920ee 100644
--- a/src/__tests__/stopwatch.js
+++ b/src/lib/__tests__/stopwatch.js
@@ -41,8 +41,8 @@ const wait = time => new Promise(resolve => setTimeout(resolve, time));
test('unmounts a component', async () => {
jest.spyOn(console, 'error').mockImplementation(() => {});
- const { unmount, getByText, container } = render();
- fireEvent.press(getByText('Start'));
+ const { unmount, getByTitle, container } = render();
+ fireEvent.press(getByTitle('Start'));
unmount();
diff --git a/src/__tests__/text-matchers.js b/src/lib/__tests__/text-matchers.js
similarity index 100%
rename from src/__tests__/text-matchers.js
rename to src/lib/__tests__/text-matchers.js
diff --git a/src/__tests__/wait-for-element-to-be-removed.fake-timers.js b/src/lib/__tests__/wait-for-element-to-be-removed.fake-timers.js
similarity index 100%
rename from src/__tests__/wait-for-element-to-be-removed.fake-timers.js
rename to src/lib/__tests__/wait-for-element-to-be-removed.fake-timers.js
diff --git a/src/__tests__/wait-for-element-to-be-removed.js b/src/lib/__tests__/wait-for-element-to-be-removed.js
similarity index 100%
rename from src/__tests__/wait-for-element-to-be-removed.js
rename to src/lib/__tests__/wait-for-element-to-be-removed.js
diff --git a/src/__tests__/wait-for-element.js b/src/lib/__tests__/wait-for-element.js
similarity index 100%
rename from src/__tests__/wait-for-element.js
rename to src/lib/__tests__/wait-for-element.js
diff --git a/src/__tests__/wait.js b/src/lib/__tests__/wait.js
similarity index 100%
rename from src/__tests__/wait.js
rename to src/lib/__tests__/wait.js
diff --git a/src/__tests__/within.js b/src/lib/__tests__/within.js
similarity index 100%
rename from src/__tests__/within.js
rename to src/lib/__tests__/within.js
diff --git a/src/act-compat.js b/src/lib/act-compat.js
similarity index 100%
rename from src/act-compat.js
rename to src/lib/act-compat.js
diff --git a/src/config.js b/src/lib/config.js
similarity index 100%
rename from src/config.js
rename to src/lib/config.js
diff --git a/src/events.js b/src/lib/events.js
similarity index 72%
rename from src/events.js
rename to src/lib/events.js
index b09c0bb..21d6af1 100644
--- a/src/events.js
+++ b/src/lib/events.js
@@ -3,40 +3,34 @@ const eventMap = {
type: 'FocusEvent',
validTargets: [
'TextInput',
- 'Touchable',
- 'TouchableBounce',
'TouchableHighlight',
+ 'TouchableNativeFeedback',
'TouchableOpacity',
'TouchableWithoutFeedback',
- 'TouchableNativeFeedback',
],
},
blur: {
type: 'BlurEvent',
validTargets: [
'TextInput',
- 'Touchable',
- 'TouchableBounce',
'TouchableHighlight',
+ 'TouchableNativeFeedback',
'TouchableOpacity',
'TouchableWithoutFeedback',
],
},
change: {
type: 'ChangeEvent',
- validTargets: [
- 'SegmentedControlIOS',
- 'PickerIOS',
- 'Switch',
- 'DatePickerIOS',
- 'Checkbox',
- 'TextInput',
- ],
+ validTargets: ['Switch', 'TextInput'],
},
changeText: {
type: 'ChangeEvent',
validTargets: ['TextInput'],
},
+ valueChange: {
+ type: 'ChangeEvent',
+ validTargets: ['Picker'],
+ },
contentSizeChange: {
type: 'ContentSizeChangeEvent',
validTargets: ['VirtualizedList', 'FlatList', 'SectionList', 'TextInput', 'ScrollView'],
@@ -57,22 +51,20 @@ const eventMap = {
type: 'LayoutEvent',
validTargets: [
'ActivityIndicator',
- 'FlatList',
+ 'Button',
+ 'DrawerLayoutAndroid',
'Image',
- 'KeyboardAvoidingView',
+ 'Modal',
+ 'RefreshControl',
'ScrollView',
- 'ScrollViewStickyHeader',
- 'SectionList',
+ 'Switch',
'Text',
'TextInput',
- 'Touchable',
'TouchableHighlight',
'TouchableNativeFeedback',
'TouchableOpacity',
'TouchableWithoutFeedback',
'View',
- 'VirtualizedList',
- 'WindowedListView',
],
},
selectionChange: {
@@ -82,109 +74,63 @@ const eventMap = {
longPress: {
type: 'PressEvent',
validTargets: [
- 'Touchable',
- 'TouchableBounce',
+ 'Text',
'TouchableHighlight',
+ 'TouchableNativeFeedback',
'TouchableOpacity',
'TouchableWithoutFeedback',
- 'TouchableNativeFeedback',
- 'Text',
],
},
press: {
type: 'PressEvent',
validTargets: [
'Button',
- 'Touchable',
- 'TouchableBounce',
+ 'Text',
'TouchableHighlight',
+ 'TouchableNativeFeedback',
'TouchableOpacity',
'TouchableWithoutFeedback',
- 'TouchableNativeFeedback',
- 'Text',
],
},
pressIn: {
type: 'PressEvent',
validTargets: [
- 'Touchable',
- 'TouchableBounce',
+ 'Text',
'TouchableHighlight',
+ 'TouchableNativeFeedback',
'TouchableOpacity',
'TouchableWithoutFeedback',
- 'TouchableNativeFeedback',
- 'YellowBoxPressable',
- 'Text',
],
},
pressOut: {
type: 'PressEvent',
validTargets: [
- 'Touchable',
- 'TouchableBounce',
+ 'Text',
'TouchableHighlight',
+ 'TouchableNativeFeedback',
'TouchableOpacity',
'TouchableWithoutFeedback',
- 'TouchableNativeFeedback',
- 'YellowBoxPressable',
- 'Text',
],
},
momentumScrollBegin: {
type: 'ScrollEvent',
- validTargets: [
- 'ScrollResponder',
- 'FlatList',
- 'ScrollView',
- 'SectionList',
- 'VirtualizedList',
- 'WindowedListView',
- ],
+ validTargets: ['FlatList', 'ScrollView', 'SectionList', 'VirtualizedList'],
},
momentumScrollEnd: {
type: 'ScrollEvent',
- validTargets: [
- 'ScrollResponder',
- 'FlatList',
- 'ScrollView',
- 'SectionList',
- 'VirtualizedList',
- 'WindowedListView',
- ],
+ validTargets: ['FlatList', 'ScrollView', 'SectionList', 'VirtualizedList'],
},
scroll: {
type: 'ScrollEvent',
- validTargets: [
- 'ScrollResponder',
- 'FlatList',
- 'ScrollView',
- 'SectionList',
- 'TextInput',
- 'VirtualizedList',
- 'WindowedListView',
- ],
+ validTargets: ['FlatList', 'ScrollView', 'SectionList', 'TextInput', 'VirtualizedList'],
},
scrollBeginDrag: {
type: 'ScrollEvent',
- validTargets: [
- 'ScrollResponder',
- 'FlatList',
- 'ScrollView',
- 'SectionList',
- 'VirtualizedList',
- 'WindowedListView',
- ],
+ validTargets: ['FlatList', 'ScrollView', 'SectionList', 'VirtualizedList'],
},
scrollEndDrag: {
type: 'ScrollEvent',
- validTargets: [
- 'ScrollResponder',
- 'FlatList',
- 'ScrollView',
- 'SectionList',
- 'VirtualizedList',
- 'WindowedListView',
- ],
+ validTargets: ['FlatList', 'ScrollView', 'SectionList', 'VirtualizedList'],
},
load: {
type: 'ImageLoadEvent',
@@ -207,7 +153,7 @@ const eventMap = {
};
const disableableElements = [
- 'Slider',
+ 'Button',
'Switch',
'TouchableHighlight',
'TouchableNativeFeedback',
@@ -239,11 +185,7 @@ function getEventHandlerName(key) {
}
function validateElementType(list, element) {
- return (
- list.includes(element.type) ||
- list.includes(element.type.name) ||
- list.includes(element.type.displayName)
- );
+ return list.includes(element.type) || list.includes(element.type.displayName);
}
function isValidTarget(element, event) {
@@ -251,7 +193,11 @@ function isValidTarget(element, event) {
}
function isDisabled(element) {
- return element.props.disabled && validateElementType(disableableElements, element);
+ const { accessibilityStates = [], disabled } = element.props;
+ const propDisabled = disabled;
+ const stateDisabled = Array.from(accessibilityStates).includes('disabled');
+
+ return (propDisabled || stateDisabled) && validateElementType(disableableElements, element);
}
function findEventHandler(element, event) {
@@ -267,7 +213,7 @@ function findEventHandler(element, event) {
return element.props[eventHandler];
}
- if (element.parent === null || element.parent.parent === null) {
+ if (element.parent === null) {
throw new Error(`No target found for event: "${typeArg}"`);
}
diff --git a/src/get-node-text.js b/src/lib/get-node-text.js
similarity index 100%
rename from src/get-node-text.js
rename to src/lib/get-node-text.js
diff --git a/src/get-queries-for-element.js b/src/lib/get-queries-for-element.js
similarity index 100%
rename from src/get-queries-for-element.js
rename to src/lib/get-queries-for-element.js
diff --git a/src/helpers.js b/src/lib/helpers.js
similarity index 100%
rename from src/helpers.js
rename to src/lib/helpers.js
diff --git a/src/lib/index.js b/src/lib/index.js
new file mode 100644
index 0000000..0150960
--- /dev/null
+++ b/src/lib/index.js
@@ -0,0 +1,66 @@
+import React from 'react';
+import TR from 'react-test-renderer';
+
+import act from './act-compat';
+import * as queries from './queries';
+import { prettyPrint } from './pretty-print';
+import * as queryHelpers from './query-helpers';
+import { getQueriesForElement } from './get-queries-for-element';
+import { fireEvent as rntlFireEvent, NativeEvent } from './events';
+
+function render(ui, { options = {}, wrapper: WrapperComponent } = {}) {
+ const wrapUiIfNeeded = innerElement =>
+ WrapperComponent ? {innerElement} : innerElement;
+
+ let container = {};
+
+ act(() => {
+ container = TR.create(wrapUiIfNeeded(ui), options);
+ });
+
+ const baseElement = queryHelpers.proxyUnsafeProperties(container.root);
+
+ return {
+ container,
+ baseElement,
+ debug: () => console.log(prettyPrint(container.toJSON())),
+ unmount: () => container.unmount(),
+ rerender: rerenderUi => {
+ act(() => {
+ container.update(wrapUiIfNeeded(rerenderUi));
+ });
+ },
+ ...getQueriesForElement(container),
+ };
+}
+
+function fireEvent(...args) {
+ let returnValue;
+ act(() => {
+ returnValue = rntlFireEvent(...args);
+ });
+ return returnValue;
+}
+
+Object.keys(rntlFireEvent).forEach(key => {
+ fireEvent[key] = (...args) => {
+ let returnValue;
+ act(() => {
+ returnValue = rntlFireEvent[key](...args);
+ });
+ return returnValue;
+ };
+});
+
+export * from './events';
+export * from './get-node-text';
+export * from './get-queries-for-element';
+export * from './pretty-print';
+export * from './queries';
+export * from './query-helpers';
+export * from './wait';
+export * from './wait-for-element';
+export * from './wait-for-element-to-be-removed';
+export { getDefaultNormalizer } from './matches';
+
+export { act, fireEvent, queries, queryHelpers, render, NativeEvent };
diff --git a/src/matches.js b/src/lib/matches.js
similarity index 100%
rename from src/matches.js
rename to src/lib/matches.js
diff --git a/src/lib/pretty-print.js b/src/lib/pretty-print.js
new file mode 100644
index 0000000..b6daec7
--- /dev/null
+++ b/src/lib/pretty-print.js
@@ -0,0 +1,18 @@
+import prettyFormat from 'pretty-format';
+import React from 'react';
+const { ReactTestComponent, ReactElement } = prettyFormat.plugins;
+
+function prettyPrint(element, maxLength, options) {
+ const debugContent = prettyFormat(element, {
+ plugins: [ReactTestComponent, ReactElement],
+ printFunctionName: false,
+ highlight: true,
+ ...options,
+ });
+
+ return maxLength !== undefined && debugContent && debugContent.toString().length > maxLength
+ ? `${debugContent.slice(0, maxLength)}...`
+ : debugContent;
+}
+
+export { prettyPrint };
diff --git a/src/queries/a11y-hint.js b/src/lib/queries/a11y-hint.js
similarity index 87%
rename from src/queries/a11y-hint.js
rename to src/lib/queries/a11y-hint.js
index cca2ef7..3480d5e 100644
--- a/src/queries/a11y-hint.js
+++ b/src/lib/queries/a11y-hint.js
@@ -1,4 +1,4 @@
-import { queryAllByProp, buildQueries, getPropMatchAsString } from './all-utils';
+import { queryAllByProp, buildQueries } from './all-utils';
const queryAllByA11yHint = queryAllByProp.bind(null, 'accessibilityHint');
diff --git a/src/queries/a11y-label.js b/src/lib/queries/a11y-label.js
similarity index 100%
rename from src/queries/a11y-label.js
rename to src/lib/queries/a11y-label.js
diff --git a/src/queries/a11y-role.js b/src/lib/queries/a11y-role.js
similarity index 100%
rename from src/queries/a11y-role.js
rename to src/lib/queries/a11y-role.js
diff --git a/src/queries/a11y-states.js b/src/lib/queries/a11y-states.js
similarity index 100%
rename from src/queries/a11y-states.js
rename to src/lib/queries/a11y-states.js
diff --git a/src/queries/a11y-traits.js b/src/lib/queries/a11y-traits.js
similarity index 100%
rename from src/queries/a11y-traits.js
rename to src/lib/queries/a11y-traits.js
diff --git a/src/queries/all-utils.js b/src/lib/queries/all-utils.js
similarity index 100%
rename from src/queries/all-utils.js
rename to src/lib/queries/all-utils.js
diff --git a/src/queries/index.js b/src/lib/queries/index.js
similarity index 91%
rename from src/queries/index.js
rename to src/lib/queries/index.js
index 6cb9081..7c5bea4 100644
--- a/src/queries/index.js
+++ b/src/lib/queries/index.js
@@ -6,4 +6,5 @@ export * from './a11y-traits';
export * from './placeholder';
export * from './test-id';
export * from './text';
+export * from './title';
export * from './value';
diff --git a/src/queries/placeholder.js b/src/lib/queries/placeholder.js
similarity index 100%
rename from src/queries/placeholder.js
rename to src/lib/queries/placeholder.js
diff --git a/src/queries/test-id.js b/src/lib/queries/test-id.js
similarity index 100%
rename from src/queries/test-id.js
rename to src/lib/queries/test-id.js
diff --git a/src/queries/text.js b/src/lib/queries/text.js
similarity index 80%
rename from src/queries/text.js
rename to src/lib/queries/text.js
index a5d722b..2bbfecd 100644
--- a/src/queries/text.js
+++ b/src/lib/queries/text.js
@@ -5,7 +5,7 @@ import {
getNodeText,
buildQueries,
getBaseElement,
- removeBadProperties,
+ proxyUnsafeProperties,
} from './all-utils';
function queryAllByText(
@@ -17,17 +17,9 @@ function queryAllByText(
const matcher = exact ? matches : fuzzyMatches;
const matchNormalizer = makeNormalizer({ collapseWhitespace, trim, normalizer });
- const baseArray = ['Text', 'TextInput'].reduce(
- (accumulator, currentValue) => [
- ...accumulator,
- ...baseElement.findAll(n => n.type === currentValue),
- ],
- [],
- );
-
- return baseArray
+ return Array.from(baseElement.findAll(n => ['TextInput', 'Text'].includes(n.type)))
.filter(node => matcher(getNodeText(node), node, text, matchNormalizer))
- .map(removeBadProperties);
+ .map(proxyUnsafeProperties);
}
const getMultipleError = (c, text) => `Found multiple elements with the text: ${text}`;
diff --git a/src/lib/queries/title.js b/src/lib/queries/title.js
new file mode 100644
index 0000000..adcbe00
--- /dev/null
+++ b/src/lib/queries/title.js
@@ -0,0 +1,14 @@
+import { queryAllByProp, buildQueries } from './all-utils';
+
+const queryAllByTitle = queryAllByProp.bind(null, 'title');
+
+const getMultipleError = (c, title) => `Found multiple elements with the title: ${title}`;
+const getMissingError = (c, title) => `Unable to find an element with the title: ${title}`;
+
+const [queryByTitle, getAllByTitle, getByTitle, findAllByTitle, findByTitle] = buildQueries(
+ queryAllByTitle,
+ getMultipleError,
+ getMissingError,
+);
+
+export { queryByTitle, queryAllByTitle, getByTitle, getAllByTitle, findAllByTitle, findByTitle };
diff --git a/src/queries/value.js b/src/lib/queries/value.js
similarity index 73%
rename from src/queries/value.js
rename to src/lib/queries/value.js
index 00b91f3..67fba33 100644
--- a/src/queries/value.js
+++ b/src/lib/queries/value.js
@@ -9,8 +9,14 @@ function queryAllByValue(
const matchNormalizer = makeNormalizer({ collapseWhitespace, trim, normalizer });
const baseElement = getBaseElement(container);
- return Array.from(baseElement.findAll(n => n.type === 'TextInput')).filter(node =>
- matcher(node.props.value, node, value, matchNormalizer),
+ return Array.from(baseElement.findAll(n => ['TextInput', 'Picker'].includes(n.type))).filter(
+ node => {
+ if (node.type === 'Picker') {
+ return matcher(node.props.selectedValue, node, value, matchNormalizer);
+ }
+
+ return matcher(node.props.value, node, value, matchNormalizer);
+ },
);
}
diff --git a/src/query-helpers.js b/src/lib/query-helpers.js
similarity index 79%
rename from src/query-helpers.js
rename to src/lib/query-helpers.js
index 8a2c8a8..1a77a8b 100644
--- a/src/query-helpers.js
+++ b/src/lib/query-helpers.js
@@ -1,11 +1,13 @@
+import { getCoreComponents } from '../preset/core-components';
+
import { prettyPrint } from './pretty-print';
import { waitForElement } from './wait-for-element';
import { fuzzyMatches, makeNormalizer, matches } from './matches';
-function debugTree(htmlElement) {
+function debugTree(container) {
const limit = process.env.DEBUG_PRINT_LIMIT || 7000;
- return prettyPrint(htmlElement.toJSON(), limit);
+ return prettyPrint(container.toJSON(), limit);
}
function getElementError(message, container) {
@@ -23,28 +25,15 @@ function getMultipleElementsFoundError(message, container) {
);
}
-function defaultFilter(node) {
- const mockedTypes = [
- 'Image',
- 'Text',
- 'TextInput',
- 'Modal',
- 'View',
- 'RefreshControl',
- 'ScrollView',
- 'ActivityIndicator',
- 'ListView',
- 'ListViewDataSource',
- ];
-
+function defaultFilter(node, mockedTypes = getCoreComponents()) {
return mockedTypes.includes(node.type);
}
function getBaseElement(container) {
- return container.root ? container.root : container;
+ return proxyUnsafeProperties(container.root ? container.root : container);
}
-function removeBadProperties(node) {
+function proxyUnsafeProperties(node) {
// We take the guiding principles seriously around these parts. These methods just let
// you do too much unfortunately, and they make it hard to follow the rules of the
// testing-library. It's not that we don't trust you, in fact we do trust you! We've
@@ -58,11 +47,30 @@ function removeBadProperties(node) {
// Of course if you can't figure that out, we still want you to be able to use this library,
// so use `findAll`, just use it sparingly! We really believe you can do everything you
// need using the query methods provided on the `render` API.
- ['find', 'findAllByProps', 'findAllByType', 'findByProps', 'findByType', 'instance'].forEach(op =>
- Object.defineProperty(node, op, { get: undefined }),
- );
+ const UNSAFE_METHODS = [
+ 'findAllByProps',
+ 'findAllByType',
+ 'findByProps',
+ 'findByType',
+ 'instance',
+ ];
- return node;
+ return new Proxy(node, {
+ get(target, key) {
+ // You can use these two, but we still want to filter out most components
+ if (key === 'findAll' || key === 'find') {
+ const ref = target[key];
+ return function(...args) {
+ return ref.apply(this, args).filter(el => defaultFilter(el));
+ };
+ } else if (UNSAFE_METHODS.includes(key)) {
+ return undefined;
+ }
+
+ // Let things behave normally if you're not running a query
+ return target[key];
+ },
+ });
}
function getPropMatchAsString(prop) {
@@ -73,20 +81,19 @@ function queryAllByProp(
attribute,
container,
match,
- { filter, exact = true, collapseWhitespace, trim, normalizer } = {},
+ { exact = true, collapseWhitespace, trim, normalizer } = {},
) {
const baseElement = getBaseElement(container);
const matcher = exact ? matches : fuzzyMatches;
const matchNormalizer = makeNormalizer({ collapseWhitespace, trim, normalizer });
return Array.from(baseElement.findAll(c => c.props[attribute]))
- .filter((node, index) => (filter ? filter(node, index) : defaultFilter(node, index)))
.filter(({ props }) =>
typeof props[attribute] === 'string'
? matcher(props[attribute], baseElement, match, matchNormalizer)
: JSON.stringify(props[attribute]) === JSON.stringify(match),
)
- .map(removeBadProperties);
+ .map(proxyUnsafeProperties);
}
function queryByProp(prop, container, match, ...args) {
@@ -152,5 +159,5 @@ export {
makeSingleQuery,
queryAllByProp,
queryByProp,
- removeBadProperties,
+ proxyUnsafeProperties,
};
diff --git a/src/wait-for-element-to-be-removed.js b/src/lib/wait-for-element-to-be-removed.js
similarity index 100%
rename from src/wait-for-element-to-be-removed.js
rename to src/lib/wait-for-element-to-be-removed.js
diff --git a/src/wait-for-element.js b/src/lib/wait-for-element.js
similarity index 100%
rename from src/wait-for-element.js
rename to src/lib/wait-for-element.js
diff --git a/src/wait.js b/src/lib/wait.js
similarity index 100%
rename from src/wait.js
rename to src/lib/wait.js
diff --git a/src/preset/core-components.js b/src/preset/core-components.js
new file mode 100644
index 0000000..4244c58
--- /dev/null
+++ b/src/preset/core-components.js
@@ -0,0 +1,27 @@
+const CoreComponents = [
+ 'ActivityIndicator',
+ 'Button',
+ 'DrawerLayoutAndroid',
+ 'Image',
+ 'Modal',
+ 'RefreshControl',
+ 'ScrollView',
+ 'Switch',
+ 'Text',
+ 'TextInput',
+ 'TouchableHighlight',
+ 'TouchableNativeFeedback',
+ 'TouchableOpacity',
+ 'TouchableWithoutFeedback',
+ 'View',
+];
+
+function setCoreComponents(components) {
+ return [...CoreComponents, ...components];
+}
+
+function getCoreComponents() {
+ return [...CoreComponents, 'Picker'];
+}
+
+export { getCoreComponents, setCoreComponents };
diff --git a/src/preset/index.js b/src/preset/index.js
new file mode 100644
index 0000000..09158bd
--- /dev/null
+++ b/src/preset/index.js
@@ -0,0 +1,8 @@
+import { mockComponent } from './mock-component';
+import { getCoreComponents, setCoreComponents } from './core-components';
+
+module.exports = {
+ CoreComponents: getCoreComponents(),
+ dangerouslyMockComponent: ({ __mock }) => mockComponent({ name: __mock.name, path: __mock.path }),
+ dangerouslySetCoreComponents: ({ __components }) => setCoreComponents(__components),
+};
diff --git a/src/preset/mock-component.js b/src/preset/mock-component.js
new file mode 100644
index 0000000..1db8c0d
--- /dev/null
+++ b/src/preset/mock-component.js
@@ -0,0 +1,66 @@
+'use strict';
+
+import React from 'react';
+
+import { getCoreComponents } from './core-components';
+
+function mockComponent(component, path = component) {
+ const RealComponent = jest.requireActual(path);
+ const { mockNativeMethods } = jest.requireActual('./mock-native-methods');
+
+ const displayName =
+ RealComponent.displayName ||
+ RealComponent.name ||
+ (RealComponent.render // handle React.forwardRef
+ ? RealComponent.render.displayName || RealComponent.render.name
+ : 'Unknown');
+
+ const SuperClass = typeof RealComponent === 'function' ? RealComponent : React.Component;
+
+ class Component extends SuperClass {
+ static displayName = displayName;
+
+ render() {
+ const props = Object.assign({}, RealComponent.defaultProps);
+
+ if (this.props) {
+ Object.keys(this.props).forEach(prop => {
+ // We can't just assign props on top of defaultProps
+ // because React treats undefined as special and different from null.
+ // If a prop is specified but set to undefined it is ignored and the
+ // default prop is used instead. If it is set to null, then the
+ // null value overwrites the default value.
+ if (this.props[prop] !== undefined) {
+ props[prop] = this.props[prop];
+ }
+ });
+ }
+
+ return React.createElement(displayName, props, this.props.children);
+ }
+ }
+
+ Object.keys(RealComponent).forEach(classStatic => {
+ Component[classStatic] = RealComponent[classStatic];
+ });
+
+ Object.assign(Component.prototype, mockNativeMethods);
+
+ return Component;
+}
+
+getCoreComponents().forEach(component => {
+ try {
+ jest.doMock(component, () => mockComponent(component));
+ } catch (e) {
+ //
+ }
+});
+
+jest.doMock('Picker', () => {
+ const Picker = mockComponent('Picker');
+ Picker.Item = ({ children, ...props }) => React.createElement('Picker.Item', props, children);
+ return Picker;
+});
+
+export { mockComponent };
diff --git a/src/preset/mock-native-methods.js b/src/preset/mock-native-methods.js
new file mode 100644
index 0000000..3533961
--- /dev/null
+++ b/src/preset/mock-native-methods.js
@@ -0,0 +1,12 @@
+'use strict';
+
+const mockNativeMethods = {
+ measure: jest.fn(),
+ measureInWindow: jest.fn(),
+ measureLayout: jest.fn(),
+ setNativeProps: jest.fn(),
+ focus: jest.fn(),
+ blur: jest.fn(),
+};
+
+export { mockNativeMethods };
diff --git a/src/preset/native-modules.js b/src/preset/native-modules.js
new file mode 100644
index 0000000..b7b22e5
--- /dev/null
+++ b/src/preset/native-modules.js
@@ -0,0 +1,11 @@
+const NativeModules = require('NativeModules');
+
+jest.doMock('NativeModules', () => ({
+ ...NativeModules,
+ NativeAnimatedModule: {
+ addAnimatedEventToView: jest.fn(),
+ createAnimatedNode: jest.fn(),
+ connectAnimatedNodes: jest.fn(),
+ connectAnimatedNodeToView: jest.fn(),
+ },
+}));
diff --git a/src/preset/setup.js b/src/preset/setup.js
new file mode 100644
index 0000000..7738ad9
--- /dev/null
+++ b/src/preset/setup.js
@@ -0,0 +1,8 @@
+// Un-mock the things that we'll be mocking
+require('./unmock');
+
+// Mock native modules
+require('./native-modules');
+
+// Mock lean core components
+require('./mock-component');
diff --git a/src/preset/unmock.js b/src/preset/unmock.js
new file mode 100644
index 0000000..49ad34f
--- /dev/null
+++ b/src/preset/unmock.js
@@ -0,0 +1,10 @@
+import { getCoreComponents } from './core-components';
+
+getCoreComponents().forEach(component => {
+ try {
+ // try to un-mock the component
+ jest.unmock(component);
+ } catch (e) {
+ // do nothing if we can't
+ }
+});
diff --git a/src/pretty-print.js b/src/pretty-print.js
deleted file mode 100644
index 1f17d4c..0000000
--- a/src/pretty-print.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import prettyFormat from 'pretty-format';
-import React from 'react';
-const { ReactTestComponent, ReactElement } = prettyFormat.plugins;
-
-function toJSON(node) {
- if (typeof node === 'string') {
- return node;
- }
-
- const { children, ...props } = node.props;
- let renderedChildren = null;
-
- if (children) {
- React.Children.forEach(children, child => {
- const renderedChild = toJSON(child);
-
- if (renderedChildren === null) {
- renderedChildren = [renderedChild];
- } else {
- renderedChildren.push(renderedChild);
- }
- });
- }
-
- const json = {
- type: node.type.displayName || node.type,
- props: props,
- children: renderedChildren,
- };
-
- Object.defineProperty(json, '$$typeof', {
- value: Symbol.for('react.test.json'),
- });
-
- return json;
-}
-
-function prettyPrint(reactElement, maxLength, options) {
- const debugContent = prettyFormat(toJSON(reactElement), {
- plugins: [ReactTestComponent, ReactElement],
- printFunctionName: false,
- highlight: true,
- ...options,
- });
-
- return maxLength !== undefined && debugContent && debugContent.toString().length > maxLength
- ? `${debugContent.slice(0, maxLength)}...`
- : debugContent;
-}
-
-export { prettyPrint };
diff --git a/typings/config.d.ts b/typings/config.d.ts
new file mode 100644
index 0000000..a5eadbf
--- /dev/null
+++ b/typings/config.d.ts
@@ -0,0 +1,5 @@
+export interface Config {
+ asyncWrapper(cb: Function): Promise;
+}
+
+export const getConfig: () => Config;
diff --git a/typings/events.d.ts b/typings/events.d.ts
new file mode 100644
index 0000000..c7d878e
--- /dev/null
+++ b/typings/events.d.ts
@@ -0,0 +1,80 @@
+import { NativeTestInstance } from './query-helpers';
+
+import {
+ NativeSyntheticEvent,
+ TextInputFocusEventData,
+ TextInputChangeEventData,
+ TextInputContentSizeChangeEventData,
+ TextInputEndEditingEventData,
+ TextInputKeyPressEventData,
+ TextInputSubmitEditingEventData,
+ LayoutChangeEvent,
+ TextInputSelectionChangeEventData,
+ GestureResponderEvent,
+ ScrollResponderEvent,
+ ImageLoadEventData,
+ ImageErrorEventData,
+ ImageProgressEventDataIOS,
+} from 'react-native';
+
+export type EventType =
+ | 'focus'
+ | 'blur'
+ | 'change'
+ | 'changeText'
+ | 'valueChange'
+ | 'contentSizeChange'
+ | 'endEditing'
+ | 'keyPress'
+ | 'submitEditing'
+ | 'layout'
+ | 'selectionChange'
+ | 'longPress'
+ | 'press'
+ | 'pressIn'
+ | 'pressOut'
+ | 'momentumScrollBegin'
+ | 'momentumScrollEnd'
+ | 'scroll'
+ | 'scrollBeginDrag'
+ | 'scrollEndDrag'
+ | 'load'
+ | 'error'
+ | 'progress'
+ | 'custom';
+
+type EventInit = Partial> & { validTargets?: string[] };
+
+export declare class NativeEvent {
+ constructor(type: 'focus', init?: EventInit);
+ constructor(type: 'blur', init?: EventInit);
+ constructor(type: 'change', init?: EventInit);
+ constructor(type: 'valueChange', value: string);
+ constructor(type: 'changeText', value: string);
+ constructor(type: 'contentSizeChange', init?: EventInit);
+ constructor(type: 'endEditing', init?: EventInit);
+ constructor(type: 'keyPress', init?: EventInit);
+ constructor(type: 'submitEditing', init?: EventInit);
+ constructor(type: 'layout', init?: EventInit);
+ constructor(type: 'selectionChange', init?: EventInit);
+ constructor(type: 'longPress', init?: EventInit);
+ constructor(type: 'press', init?: EventInit);
+ constructor(type: 'pressIn', init?: EventInit);
+ constructor(type: 'pressOut', init?: EventInit);
+ constructor(type: 'momentumScrollBegin', init?: EventInit);
+ constructor(type: 'momentumScrollEnd', init?: EventInit);
+ constructor(type: 'scroll', init?: EventInit);
+ constructor(type: 'scrollBeginDrag', init?: EventInit);
+ constructor(type: 'scrollEndDrag', init?: EventInit);
+ constructor(type: 'load', init?: EventInit);
+ constructor(type: 'error', init?: EventInit);
+ constructor(type: 'progress', init?: EventInit);
+}
+
+export type FireFunction = (element: NativeTestInstance, event: NativeEvent) => boolean;
+export type FireObject = {
+ [K in EventType]: (element: NativeTestInstance, options?: {}) => boolean
+};
+
+export const getEventHandlerName: (key: string) => string;
+export const fireEvent: FireFunction & FireObject;
diff --git a/typings/get-node-text.d.ts b/typings/get-node-text.d.ts
new file mode 100644
index 0000000..3707345
--- /dev/null
+++ b/typings/get-node-text.d.ts
@@ -0,0 +1,3 @@
+import { NativeTestInstance } from './query-helpers';
+
+export declare function getNodeText(node: NativeTestInstance): string;
diff --git a/typings/get-queries-for-element.d.ts b/typings/get-queries-for-element.d.ts
new file mode 100644
index 0000000..0d2830d
--- /dev/null
+++ b/typings/get-queries-for-element.d.ts
@@ -0,0 +1,34 @@
+import { ReactTestRenderer } from 'react-test-renderer';
+
+import * as queries from './queries';
+
+export type BoundFunction = T extends (
+ attribute: string,
+ element: ReactTestRenderer,
+ text: infer P,
+ options: infer Q,
+) => infer R
+ ? (text: P, options?: Q) => R
+ : T extends (a1: any, text: infer P, options: infer Q) => infer R
+ ? (text: P, options?: Q) => R
+ : never;
+export type BoundFunctions = { [P in keyof T]: BoundFunction };
+
+interface Query extends Function {
+ (container: ReactTestRenderer, ...args: any[]):
+ | Error
+ | Promise
+ | Promise
+ | ReactTestRenderer[]
+ | ReactTestRenderer
+ | null;
+}
+
+interface Queries {
+ [T: string]: Query;
+}
+
+export function getQueriesForElement(
+ element: ReactTestRenderer,
+ queriesToBind?: T,
+): BoundFunctions;
diff --git a/typings/index.d.ts b/typings/index.d.ts
new file mode 100644
index 0000000..b4acdef
--- /dev/null
+++ b/typings/index.d.ts
@@ -0,0 +1,61 @@
+import { ReactElement, ComponentType } from 'react';
+import { ReactTestRenderer } from 'react-test-renderer';
+
+import { getQueriesForElement, BoundFunction } from './get-queries-for-element';
+import * as queries from './queries';
+import * as queryHelpers from './query-helpers';
+import { NativeTestInstance } from './query-helpers';
+
+declare const within: typeof getQueriesForElement;
+
+interface Query extends Function {
+ (container: ReactTestRenderer, ...args: any[]):
+ | Error
+ | Promise
+ | Promise
+ | HTMLElement[]
+ | HTMLElement
+ | null;
+}
+
+interface Queries {
+ [T: string]: Query;
+}
+
+export type RenderResult = {
+ container: ReactTestRenderer;
+ baseElement: NativeTestInstance;
+ debug: () => void;
+ rerender: (ui: ReactElement) => void;
+ unmount: () => void;
+} & { [P in keyof Q]: BoundFunction };
+
+export interface RenderOptions {
+ queries?: Q;
+ wrapper?: ComponentType;
+}
+
+type Omit = Pick>;
+
+export function render(
+ ui: ReactElement,
+ options?: Omit,
+): RenderResult;
+export function render(
+ ui: ReactElement,
+ options: RenderOptions,
+): RenderResult;
+
+export const act: (callback: () => void) => void;
+
+export { queries, queryHelpers, within };
+export * from './config';
+export * from './events';
+export * from './get-node-text';
+export * from './get-queries-for-element';
+export * from './matches';
+export * from './pretty-print';
+export * from './queries';
+export * from './query-helpers';
+export * from './wait';
+export * from './wait-for-element';
diff --git a/typings/matches.d.ts b/typings/matches.d.ts
new file mode 100644
index 0000000..db6579b
--- /dev/null
+++ b/typings/matches.d.ts
@@ -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 declare function getDefaultNormalizer(options?: DefaultNormalizerOptions): NormalizerFn;
+
+// N.B. Don't expose fuzzyMatches + matches here: they're not public API
diff --git a/typings/pretty-print.d.ts b/typings/pretty-print.d.ts
new file mode 100644
index 0000000..eb48e4b
--- /dev/null
+++ b/typings/pretty-print.d.ts
@@ -0,0 +1,8 @@
+import { ReactTestRenderer } from 'react-test-renderer';
+
+import { NativeTestInstance } from './query-helpers';
+
+export function prettyPrint(
+ element: ReactTestRenderer | NativeTestInstance | string,
+ maxLength?: number,
+): string | false;
diff --git a/typings/queries.d.ts b/typings/queries.d.ts
new file mode 100644
index 0000000..82bd682
--- /dev/null
+++ b/typings/queries.d.ts
@@ -0,0 +1,122 @@
+import { ReactTestRenderer } from 'react-test-renderer';
+
+import { Matcher, MatcherOptions } from './matches';
+import { WaitForElementOptions } from './wait-for-element';
+import { SelectorMatcherOptions } from './query-helpers';
+
+export type QueryByBoundProp = (
+ container: ReactTestRenderer,
+ id: Matcher,
+ options?: MatcherOptions,
+) => ReactTestRenderer | null;
+
+export type AllByBoundProp = (
+ container: ReactTestRenderer,
+ id: Matcher,
+ options?: MatcherOptions,
+) => ReactTestRenderer[];
+
+export type FindAllByBoundProp = (
+ container: ReactTestRenderer,
+ id: Matcher,
+ options?: MatcherOptions,
+) => Promise;
+
+export type GetByBoundProp = (
+ container: ReactTestRenderer,
+ id: Matcher,
+ options?: MatcherOptions,
+) => ReactTestRenderer;
+
+export type FindByBoundProp = (
+ container: ReactTestRenderer,
+ id: Matcher,
+ options?: MatcherOptions,
+ waitForElementOptions?: WaitForElementOptions,
+) => Promise;
+
+export type QueryByText = (
+ container: ReactTestRenderer,
+ id: Matcher,
+ options?: SelectorMatcherOptions,
+) => ReactTestRenderer | null;
+
+export type AllByText = (
+ container: ReactTestRenderer,
+ id: Matcher,
+ options?: SelectorMatcherOptions,
+) => ReactTestRenderer[];
+
+export type FindAllByText = (
+ container: ReactTestRenderer,
+ id: Matcher,
+ options?: MatcherOptions,
+ waitForElementOptions?: WaitForElementOptions,
+) => Promise;
+
+export type GetByText = (
+ container: ReactTestRenderer,
+ id: Matcher,
+ options?: WaitForElementOptions,
+) => ReactTestRenderer;
+
+export type FindByText = (
+ container: ReactTestRenderer,
+ id: Matcher,
+ options?: MatcherOptions,
+ waitForElementOptions?: WaitForElementOptions,
+) => Promise;
+
+export const getByA11yHint: GetByBoundProp;
+export const getByA11yLabel: GetByBoundProp;
+export const getByA11yRole: GetByBoundProp;
+export const getByA11yStates: GetByBoundProp;
+export const getByA11yTraits: GetByBoundProp;
+export const getByPlaceholder: GetByBoundProp;
+export const getByTestId: GetByBoundProp;
+export const getByText: GetByText;
+
+export const getAllByA11yHint: AllByBoundProp;
+export const getAllByA11yLabel: AllByBoundProp;
+export const getAllByA11yRole: AllByBoundProp;
+export const getAllByA11yStates: AllByBoundProp;
+export const getAllByA11yTraits: AllByBoundProp;
+export const getAllByPlaceholder: AllByBoundProp;
+export const getAllByTestId: AllByBoundProp;
+export const getAllByText: AllByText;
+
+export const queryByA11yHint: QueryByBoundProp;
+export const queryByA11yLabel: QueryByBoundProp;
+export const queryByA11yRole: QueryByBoundProp;
+export const queryByA11yStates: QueryByBoundProp;
+export const queryByA11yTraits: QueryByBoundProp;
+export const queryByPlaceholder: QueryByBoundProp;
+export const queryByTestId: QueryByBoundProp;
+export const queryByText: QueryByText;
+
+export const queryAllByA11yHint: AllByBoundProp;
+export const queryAllByA11yLabel: AllByBoundProp;
+export const queryAllByA11yRole: AllByBoundProp;
+export const queryAllByA11yStates: AllByBoundProp;
+export const queryAllByA11yTraits: AllByBoundProp;
+export const queryAllByPlaceholder: AllByBoundProp;
+export const queryAllByTestId: AllByBoundProp;
+export const queryAllByText: AllByText;
+
+export const findByA11yHint: FindByBoundProp;
+export const findByA11yLabel: FindByBoundProp;
+export const findByA11yRole: FindByBoundProp;
+export const findByA11yStates: FindByBoundProp;
+export const findByA11yTraits: FindByBoundProp;
+export const findByPlaceholder: FindByBoundProp;
+export const findByTestId: FindByBoundProp;
+export const findByText: FindByText;
+
+export const findAllByA11yHint: FindAllByBoundProp;
+export const findAllByA11yLabel: FindAllByBoundProp;
+export const findAllByA11yRole: FindAllByBoundProp;
+export const findAllByA11yStates: FindAllByBoundProp;
+export const findAllByA11yTraits: FindAllByBoundProp;
+export const findAllByPlaceholder: FindAllByBoundProp;
+export const findAllByTestId: FindAllByBoundProp;
+export const findAllByText: FindAllByText;
diff --git a/typings/query-helpers.d.ts b/typings/query-helpers.d.ts
new file mode 100644
index 0000000..54304b9
--- /dev/null
+++ b/typings/query-helpers.d.ts
@@ -0,0 +1,38 @@
+import { ReactTestRenderer, ReactTestInstance } from 'react-test-renderer';
+
+import { Matcher, MatcherOptions } from './matches';
+
+type Omit = Pick>;
+
+export interface SelectorMatcherOptions extends MatcherOptions {
+ selector?: string;
+}
+
+export type NativeTestInstance = Omit<
+ ReactTestInstance,
+ 'findAllByProps' | 'findAllByType' | 'findByProps' | 'findByType' | 'instance'
+>;
+
+export type QueryByProp = (
+ attribute: string,
+ container: ReactTestRenderer,
+ match: Matcher,
+ options?: MatcherOptions,
+) => NativeTestInstance | null;
+
+export type AllByProp = (
+ attribute: string,
+ container: HTMLElement,
+ id: Matcher,
+ options?: MatcherOptions,
+) => NativeTestInstance[];
+
+// TODO: finish types of the rest of the helpers
+export const defaultFilter: (node: NativeTestInstance) => boolean;
+export const getBaseElement: (
+ container: ReactTestRenderer | ReactTestInstance,
+) => ReactTestInstance;
+export const getElementError: (message: string, container: ReactTestRenderer) => Error;
+export const queryAllByProp: AllByProp;
+export const queryByProp: QueryByProp;
+export const proxyUnsafeProperties: (node: ReactTestInstance) => NativeTestInstance;
diff --git a/typings/wait-for-element-to-be-removed.d.ts b/typings/wait-for-element-to-be-removed.d.ts
new file mode 100644
index 0000000..cf1b191
--- /dev/null
+++ b/typings/wait-for-element-to-be-removed.d.ts
@@ -0,0 +1,7 @@
+export function waitForElementToBeRemoved(
+ callback: () => T,
+ options?: {
+ timeout?: number;
+ interval?: number;
+ },
+): Promise;
diff --git a/typings/wait-for-element.d.ts b/typings/wait-for-element.d.ts
new file mode 100644
index 0000000..c98c5c7
--- /dev/null
+++ b/typings/wait-for-element.d.ts
@@ -0,0 +1,6 @@
+export interface WaitForElementOptions {
+ timeout?: number;
+ interval?: number;
+}
+
+export function waitForElement(callback: () => T, options?: WaitForElementOptions): Promise;
diff --git a/typings/wait.d.ts b/typings/wait.d.ts
new file mode 100644
index 0000000..54a1fa6
--- /dev/null
+++ b/typings/wait.d.ts
@@ -0,0 +1,7 @@
+export function wait(
+ callback?: () => void,
+ options?: {
+ timeout?: number;
+ interval?: number;
+ },
+): Promise;