Skip to content
This repository has been archived by the owner on Jul 30, 2020. It is now read-only.

Commit

Permalink
fix(getBy*): throw an error if more than one element is found
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `queryBy` and `getBy` now throw when multiple elements are returned and prompt users to use other queries if multiple results were intentional. This was done to remain in feature parity with `dom-testing-library`
  • Loading branch information
bcarroll22 committed Apr 25, 2019
1 parent 64d8a20 commit 49f2b0d
Show file tree
Hide file tree
Showing 23 changed files with 629 additions and 320 deletions.
2 changes: 1 addition & 1 deletion .all-contributorsrc
@@ -1,6 +1,6 @@
{
"projectName": "native-testing-library",
"projectOwner": "bcarroll22",
"projectOwner": "testing-library",
"repoType": "github",
"files": [
"README.md"
Expand Down
18 changes: 9 additions & 9 deletions README.md
Expand Up @@ -6,30 +6,30 @@
height="80"
width="80"
alt="goat"
src="https://raw.githubusercontent.com/bcarroll22/native-testing-library/master/other/whale.png"
src="https://raw.githubusercontent.com/testing-library/native-testing-library/master/other/whale.png"
/>
</a>

<p>Simple and complete React Native testing utilities that encourage good testing practices.</p>

[**Read The Docs**](https://native-testing-library.com/docs/intro) |
[Edit the docs](https://github.com/bcarroll22/native-testing-library-docs)
[Edit the docs](https://github.com/testing-library/native-testing-library-docs)
</div>

<hr />

[![Build Status](https://travis-ci.org/bcarroll22/native-testing-library.svg?branch=master)](https://travis-ci.org/bcarroll22/native-testing-library)
[![Code Coverage](https://img.shields.io/codecov/c/github/bcarroll22/native-testing-library.svg?style=flat-square)](https://codecov.io/github/bcarroll22/native-testing-library)
[![Build Status](https://travis-ci.org/testing-library/native-testing-library.svg?branch=master)](https://travis-ci.org/testing-library/native-testing-library)
[![Code Coverage](https://img.shields.io/codecov/c/github/testing-library/native-testing-library.svg?style=flat-square)](https://codecov.io/github/testing-library/native-testing-library)
[![version](https://img.shields.io/npm/v/native-testing-library.svg?style=flat-square)](https://www.npmjs.com/package/native-testing-library)
[![downloads](https://img.shields.io/npm/dm/native-testing-library.svg?style=flat-square)](http://www.npmtrends.com/native-testing-library)
[![MIT License](https://img.shields.io/npm/l/native-testing-library.svg?style=flat-square)](https://github.com/bcarroll22/native-testing-library/blob/master/LICENSE)
[![MIT License](https://img.shields.io/npm/l/native-testing-library.svg?style=flat-square)](https://github.com/testing-library/native-testing-library/blob/master/LICENSE)

[![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
[![Code of Conduct](https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square)](https://github.com/bcarroll22/native-testing-library/blob/master/CODE_OF_CONDUCT.md)
[![Code of Conduct](https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square)](https://github.com/testing-library/native-testing-library/blob/master/CODE_OF_CONDUCT.md)

[![Watch on GitHub](https://img.shields.io/github/watchers/bcarroll22/native-testing-library.svg?style=social)](https://github.com/bcarroll22/native-testing-library/watchers)
[![Star on GitHub](https://img.shields.io/github/stars/bcarroll22/native-testing-library.svg?style=social)](https://github.com/bcarroll22/native-testing-library/stargazers)
[![Watch on GitHub](https://img.shields.io/github/watchers/testing-library/native-testing-library.svg?style=social)](https://github.com/testing-library/native-testing-library/watchers)
[![Star on GitHub](https://img.shields.io/github/stars/testing-library/native-testing-library.svg?style=social)](https://github.com/testing-library/native-testing-library/stargazers)

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
Expand Down Expand Up @@ -196,7 +196,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore -->
<table><tr><td align="center"><a href="https://github.com/bcarroll22"><img src="https://avatars2.githubusercontent.com/u/11020406?v=4" width="100px;" alt="Brandon Carroll"/><br /><sub><b>Brandon Carroll</b></sub></a><br /><a href="https://github.com/bcarroll22/native-testing-library/commits?author=bcarroll22" title="Code">馃捇</a> <a href="https://github.com/bcarroll22/native-testing-library/commits?author=bcarroll22" title="Documentation">馃摉</a> <a href="#infra-bcarroll22" title="Infrastructure (Hosting, Build-Tools, etc)">馃殗</a> <a href="https://github.com/bcarroll22/native-testing-library/commits?author=bcarroll22" title="Tests">鈿狅笍</a></td><td align="center"><a href="http://tagraves.com"><img src="https://avatars1.githubusercontent.com/u/2263711?v=4" width="100px;" alt="Tommy Graves"/><br /><sub><b>Tommy Graves</b></sub></a><br /><a href="#ideas-TAGraves" title="Ideas, Planning, & Feedback">馃</a> <a href="#maintenance-TAGraves" title="Maintenance">馃毀</a> <a href="#review-TAGraves" title="Reviewed Pull Requests">馃憖</a></td><td align="center"><a href="https://kentcdodds.com"><img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;" alt="Kent C. Dodds"/><br /><sub><b>Kent C. Dodds</b></sub></a><br /><a href="#ideas-kentcdodds" title="Ideas, Planning, & Feedback">馃</a></td><td align="center"><a href="https://github.com/sz-piotr"><img src="https://avatars2.githubusercontent.com/u/17070569?v=4" width="100px;" alt="Piotr Szlachciak"/><br /><sub><b>Piotr Szlachciak</b></sub></a><br /><a href="https://github.com/bcarroll22/native-testing-library/commits?author=sz-piotr" title="Code">馃捇</a></td><td align="center"><a href="https://github.com/mcgloneleviROOT"><img src="https://avatars3.githubusercontent.com/u/48258981?v=4" width="100px;" alt="mcgloneleviROOT"/><br /><sub><b>mcgloneleviROOT</b></sub></a><br /><a href="https://github.com/bcarroll22/native-testing-library/issues?q=author%3AmcgloneleviROOT" title="Bug reports">馃悰</a> <a href="https://github.com/bcarroll22/native-testing-library/commits?author=mcgloneleviROOT" title="Code">馃捇</a></td><td align="center"><a href="http://exercism.io/profiles/wolverineks/619ce225090a43cb891d2edcbbf50401"><img src="https://avatars2.githubusercontent.com/u/8462274?v=4" width="100px;" alt="Kevin Sullivan"/><br /><sub><b>Kevin Sullivan</b></sub></a><br /><a href="https://github.com/bcarroll22/native-testing-library/commits?author=wolverineks" title="Documentation">馃摉</a></td></tr></table>
<table><tr><td align="center"><a href="https://github.com/bcarroll22"><img src="https://avatars2.githubusercontent.com/u/11020406?v=4" width="100px;" alt="Brandon Carroll"/><br /><sub><b>Brandon Carroll</b></sub></a><br /><a href="https://github.com/testing-library/native-testing-library/commits?author=bcarroll22" title="Code">馃捇</a> <a href="https://github.com/testing-library/native-testing-library/commits?author=bcarroll22" title="Documentation">馃摉</a> <a href="#infra-bcarroll22" title="Infrastructure (Hosting, Build-Tools, etc)">馃殗</a> <a href="https://github.com/testing-library/native-testing-library/commits?author=bcarroll22" title="Tests">鈿狅笍</a></td><td align="center"><a href="http://tagraves.com"><img src="https://avatars1.githubusercontent.com/u/2263711?v=4" width="100px;" alt="Tommy Graves"/><br /><sub><b>Tommy Graves</b></sub></a><br /><a href="#ideas-TAGraves" title="Ideas, Planning, & Feedback">馃</a> <a href="#maintenance-TAGraves" title="Maintenance">馃毀</a> <a href="#review-TAGraves" title="Reviewed Pull Requests">馃憖</a></td><td align="center"><a href="https://kentcdodds.com"><img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;" alt="Kent C. Dodds"/><br /><sub><b>Kent C. Dodds</b></sub></a><br /><a href="#ideas-kentcdodds" title="Ideas, Planning, & Feedback">馃</a></td><td align="center"><a href="https://github.com/sz-piotr"><img src="https://avatars2.githubusercontent.com/u/17070569?v=4" width="100px;" alt="Piotr Szlachciak"/><br /><sub><b>Piotr Szlachciak</b></sub></a><br /><a href="https://github.com/testing-library/native-testing-library/commits?author=sz-piotr" title="Code">馃捇</a></td><td align="center"><a href="https://github.com/mcgloneleviROOT"><img src="https://avatars3.githubusercontent.com/u/48258981?v=4" width="100px;" alt="mcgloneleviROOT"/><br /><sub><b>mcgloneleviROOT</b></sub></a><br /><a href="https://github.com/testing-library/native-testing-library/issues?q=author%3AmcgloneleviROOT" title="Bug reports">馃悰</a> <a href="https://github.com/testing-library/native-testing-library/commits?author=mcgloneleviROOT" title="Code">馃捇</a></td><td align="center"><a href="http://exercism.io/profiles/wolverineks/619ce225090a43cb891d2edcbbf50401"><img src="https://avatars2.githubusercontent.com/u/8462274?v=4" width="100px;" alt="Kevin Sullivan"/><br /><sub><b>Kevin Sullivan</b></sub></a><br /><a href="https://github.com/testing-library/native-testing-library/commits?author=wolverineks" title="Documentation">馃摉</a></td></tr></table>

<!-- ALL-CONTRIBUTORS-LIST:END -->

Expand Down
6 changes: 3 additions & 3 deletions package.json
Expand Up @@ -97,10 +97,10 @@
},
"repository": {
"type": "git",
"url": "https://github.com/bcarroll22/native-testing-library.git"
"url": "https://github.com/testing-library/native-testing-library.git"
},
"bugs": {
"url": "https://github.com/bcarroll22/native-testing-library/issues"
"url": "https://github.com/testing-library/native-testing-library/issues"
},
"homepage": "https://github.com/bcarroll22/native-testing-library#readme"
"homepage": "https://github.com/testing-library/native-testing-library#readme"
}
187 changes: 187 additions & 0 deletions src/__tests__/get-by-errors.js
@@ -0,0 +1,187 @@
import React from 'react';
import { Text, TextInput, View } from 'react-native';
import cases from 'jest-in-case';

import { render } from '../';

cases(
'getBy* queries throw an error when there are multiple elements returned',
({ name, query, tree }) => {
const utils = render(tree);
expect(() => utils[name](query)).toThrow(/multiple elements/i);
},
{
getByA11yHint: {
query: /his/,
tree: (
<View>
<View accessibilityHint="his" />
<View accessibilityHint="history" />
</View>
),
},
getByA11yLabel: {
query: /his/,
tree: (
<View>
<View accessibilityLabel="his" />
<View accessibilityLabel="history" />
</View>
),
},
getByA11yRole: {
query: 'button',
tree: (
<View>
<View accessibilityRole="button" />
<View accessibilityRole="button" />
</View>
),
},
getByA11yStates: {
query: ['selected'],
tree: (
<View>
<View accessibilityStates={['selected']} />
<View accessibilityStates={['selected']} />
</View>
),
},
getByA11yTraits: {
query: ['button'],
tree: (
<View>
<View accessibilityTraits={['button']} />
<View accessibilityTraits={['button']} />
</View>
),
},
getByPlaceholder: {
query: /his/,
tree: (
<View>
<TextInput placeholder="his" />
<TextInput placeholder="history" />
</View>
),
},
getByTestId: {
query: /his/,
tree: (
<View>
<Text testID="his">text</Text>
<Text testID="history">other</Text>
</View>
),
},
getByText: {
query: /his/,
tree: (
<View>
<Text>his</Text>
<Text>history</Text>
</View>
),
},
getByValue: {
query: /his/,
tree: (
<View>
<TextInput value="his" />
<TextInput value="history" />
</View>
),
},
},
);

cases(
'queryBy* queries throw an error when there are multiple elements returned',
({ name, query, tree }) => {
const utils = render(tree);
expect(() => utils[name](query)).toThrow(/multiple elements/i);
},
{
queryByA11yHint: {
query: /his/,
tree: (
<View>
<View accessibilityHint="his" />
<View accessibilityHint="history" />
</View>
),
},
queryByA11yLabel: {
query: /his/,
tree: (
<View>
<View accessibilityLabel="his" />
<View accessibilityLabel="history" />
</View>
),
},
queryByA11yRole: {
query: 'button',
tree: (
<View>
<View accessibilityRole="button" />
<View accessibilityRole="button" />
</View>
),
},
queryByA11yStates: {
query: ['selected'],
tree: (
<View>
<View accessibilityStates={['selected']} />
<View accessibilityStates={['selected']} />
</View>
),
},
queryByA11yTraits: {
query: ['button'],
tree: (
<View>
<View accessibilityTraits={['button']} />
<View accessibilityTraits={['button']} />
</View>
),
},
queryByPlaceholder: {
query: /his/,
tree: (
<View>
<TextInput placeholder="his" />
<TextInput placeholder="history" />
</View>
),
},
queryByTestId: {
query: /his/,
tree: (
<View>
<Text testID="his">text</Text>
<Text testID="history">other</Text>
</View>
),
},
queryByText: {
query: /his/,
tree: (
<View>
<Text>his</Text>
<Text>history</Text>
</View>
),
},
queryByValue: {
query: /his/,
tree: (
<View>
<TextInput value="his" />
<TextInput value="history" />
</View>
),
},
},
);
23 changes: 23 additions & 0 deletions src/__tests__/misc.js
@@ -0,0 +1,23 @@
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(
<View>
<View testID="foo" importantForAccessibility="no" />
<View importantForAccessibility="no" />
<View importantForAccessibility="no-hide-descendants" />
</View>,
);

expect(queryByTestId(container, 'foo')).not.toBeNull();
expect(queryByProp('importantForAccessibility', container, 'auto')).toBeNull();
expect(() => queryByProp('importantForAccessibility', container, /no/)).toThrow(
/multiple elements/,
);
});
56 changes: 47 additions & 9 deletions src/__tests__/pretty-print.js
@@ -1,19 +1,57 @@
import React from 'react';
import { Text } from 'react-native';
import { Text, View } from 'react-native';

import { render } from '../';
import { prettyPrint } from '../pretty-print';

test('it prints out the given element tree', () => {
const { container } = render(<Text>Hello World!</Text>);
expect(prettyPrint(container)).toMatchInlineSnapshot(`
"<Text>
Hello World!
</Text>"
test('it prints correctly with no children', () => {
const { baseElement } = render(<View />);

expect(prettyPrint(baseElement)).toMatchInlineSnapshot(`"<View />"`);
});

test('it prints correctly with one child', () => {
const { baseElement } = render(
<View>
<Text>Hello World!</Text>
</View>,
);

expect(prettyPrint(baseElement)).toMatchInlineSnapshot(`
"<View>
<Text>
Hello World!
</Text>
</View>"
`);
});

test('it prints correctly with multiple children', () => {
const { baseElement } = render(
<View>
<Text>Hello</Text>
<Text>World!</Text>
</View>,
);

expect(prettyPrint(baseElement)).toMatchInlineSnapshot(`
"<View>
<Text>
Hello
</Text>
<Text>
World!
</Text>
</View>"
`);
});

test('it supports truncating the output length', () => {
const { container } = render(<Text>Hello World!</Text>);
expect(prettyPrint(container, 5)).toMatch(/\.\.\./);
const { baseElement } = render(
<View>
<Text>Hello World!</Text>
</View>,
);

expect(prettyPrint(baseElement, 5)).toMatch(/\.\.\./);
});
5 changes: 5 additions & 0 deletions src/__tests__/text-matchers.js
Expand Up @@ -141,6 +141,11 @@ cases(
query: `Dwayne 'The Rock' Johnson`,
queryFn: `queryAllByPlaceholder`,
},
queryAllByValue: {
tree: <TextInput value="Dwayne 'The Rock' Johnson" />,
query: `Dwayne 'The Rock' Johnson`,
queryFn: `queryAllByValue`,
},
queryAllByAccessibilityLabel: {
tree: <Image accessibilityLabel="Finding Nemo poster " src="/finding-nemo.png" />,
query: `Finding Nemo poster`,
Expand Down
3 changes: 1 addition & 2 deletions src/index.d.ts
Expand Up @@ -227,7 +227,6 @@ export interface Queries {
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 firstResultOrNull<T extends any[], U>(query: (...args: T) => U[], ...args: T): U | null
export declare function filterNodeByType(node: NativeTestInstance, type: string): boolean
export declare function queryAllByProp(
attribute: string,
Expand Down Expand Up @@ -295,7 +294,7 @@ export declare function render<T>(ui: ReactElement, options: RenderOptionsWithQu
export interface RenderResult {
container: ReactTestRenderer
baseElement: NativeTestInstance
debug: () => void
debug: (el?: NativeTestInstance) => void
rerender: (ui: ReactElement) => void
unmount: () => void
}
Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Expand Up @@ -24,7 +24,7 @@ function render(ui, { options = {}, wrapper: WrapperComponent } = {}) {
return {
container,
baseElement,
debug: (el = container) => console.log(prettyPrint(el)),
debug: (el = baseElement) => console.log(prettyPrint(el)),
unmount: () => container.unmount(),
rerender: rerenderUi => {
act(() => {
Expand Down

0 comments on commit 49f2b0d

Please sign in to comment.