From 2ec67a8be6478234e3123703ce032fee9c0aafff Mon Sep 17 00:00:00 2001 From: andy Date: Tue, 29 Oct 2019 14:06:38 -0600 Subject: [PATCH 1/5] feat: formatting options for render() to remove options from debug output --- src/__tests__/debug.js | 33 +++++++++++++++++++++++++-- src/index.js | 4 ++-- src/lib/__tests__/pretty-print.js | 26 ++++++++++++++++++++++ src/lib/pretty-print.js | 37 ++++++++++++++++++++++++++++--- typings/pretty-print.d.ts | 5 +++++ 5 files changed, 98 insertions(+), 7 deletions(-) diff --git a/src/__tests__/debug.js b/src/__tests__/debug.js index 231154d..5517e44 100644 --- a/src/__tests__/debug.js +++ b/src/__tests__/debug.js @@ -1,10 +1,12 @@ import React from 'react'; -import { Text } from 'react-native'; +import { Text, View } from 'react-native'; import { cleanup, render } from '../'; +let log; + beforeEach(() => { - jest.spyOn(console, 'log').mockImplementation(() => {}); + jest.spyOn(console, 'log').mockImplementation(output => (log = output)); }); afterEach(() => { @@ -19,3 +21,30 @@ test('debug pretty prints the baseElement', () => { expect(console.log).toHaveBeenCalledTimes(1); expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Hello World')); }); + +test('debug can remove specified props from output', () => { + const options = { + propsToRemove: ['style', 'pointerEvents', 'collapsable'], + }; + + const { debug } = render( + + Hello World! + , + { formatting: options }, + ); + + debug(); + expect(console.log).toHaveBeenCalledTimes(1); + expect(log).toMatchInlineSnapshot(` + " +  +  +  + Hello World! +  +  +  + " + `); +}); diff --git a/src/index.js b/src/index.js index 32f1a04..6a58ed3 100644 --- a/src/index.js +++ b/src/index.js @@ -14,7 +14,7 @@ import act from './act-compat'; const renderers = new Set(); -function render(ui, { options = {}, wrapper: WrapperComponent, queries } = {}) { +function render(ui, { options = {}, wrapper: WrapperComponent, queries, formatting = {} } = {}) { const wrapUiIfNeeded = innerElement => WrapperComponent ? ( @@ -39,7 +39,7 @@ function render(ui, { options = {}, wrapper: WrapperComponent, queries } = {}) { return { baseElement, container, - debug: (el = baseElement) => console.log(prettyPrint(el)), + debug: (el = baseElement) => console.log(prettyPrint(el, undefined, { formatting })), unmount: () => testRenderer.unmount(), rerender: rerenderUi => { act(() => { diff --git a/src/lib/__tests__/pretty-print.js b/src/lib/__tests__/pretty-print.js index 886ca53..ec3c167 100644 --- a/src/lib/__tests__/pretty-print.js +++ b/src/lib/__tests__/pretty-print.js @@ -88,3 +88,29 @@ test('it supports truncating the output length', () => { expect(prettyPrint(container, 5)).toMatch(/\.\.\./); }); + +test('it supports removing props from output', () => { + const { container } = render( + + Hello World! + , + ); + + const options = { + formatting: { + propsToRemove: ['style', 'pointerEvents'], + }, + }; + + expect(prettyPrint(container, undefined, options)).toMatchInlineSnapshot(` + " +  +  + Hello World! +  +  + " + `); +}); diff --git a/src/lib/pretty-print.js b/src/lib/pretty-print.js index 8ef5076..888d60e 100644 --- a/src/lib/pretty-print.js +++ b/src/lib/pretty-print.js @@ -5,12 +5,20 @@ import { toJSON } from './to-json'; const { ReactTestComponent, ReactElement } = prettyFormat.plugins; -function prettyPrint(element, maxLength, options) { +function prettyPrint(element, maxLength, options = {}) { + let plugins = [ReactTestComponent, ReactElement]; + const { formatting, ...rest } = options; + + if (formatting && formatting.propsToRemove) { + const formatterPlugin = createFormatter(options.formatting.propsToRemove); + plugins = [formatterPlugin, ...plugins]; + } + const debugContent = prettyFormat(toJSON(element), { - plugins: [ReactTestComponent, ReactElement], + plugins: plugins, printFunctionName: false, highlight: true, - ...options, + ...rest, }); return maxLength !== undefined && debugContent && debugContent.toString().length > maxLength @@ -18,4 +26,27 @@ function prettyPrint(element, maxLength, options) { : debugContent; } +function createFormatter(propsToRemove) { + const plugin = { + test(val) { + return val.props !== undefined; + }, + serialize(element, config, indentation, depth, refs, printer) { + Object.keys(element.props).map(prop => { + if (propsToRemove.includes(prop)) { + delete element.props[prop]; + } + }); + + if (ReactTestComponent.test(element)) { + return ReactTestComponent.serialize(element, config, indentation, depth, refs, printer); + } + + return ReactElement.serialize(element, config, indentation, depth, refs, printer); + }, + }; + + return plugin; +} + export { prettyPrint }; diff --git a/typings/pretty-print.d.ts b/typings/pretty-print.d.ts index 82405cb..ad436b3 100644 --- a/typings/pretty-print.d.ts +++ b/typings/pretty-print.d.ts @@ -3,4 +3,9 @@ import { NativeTestInstance } from './query-helpers'; export function prettyPrint( element: NativeTestInstance | string, maxLength?: number, + options: { + formatting: { + propsToRemove: string[]; + }; + }, ): string | false; From 72586c79edada653e44ae0829c5be38e82515051 Mon Sep 17 00:00:00 2001 From: andy Date: Tue, 29 Oct 2019 14:13:32 -0600 Subject: [PATCH 2/5] update name -- propsToRemove to just removeProps --- src/__tests__/debug.js | 2 +- src/lib/__tests__/pretty-print.js | 2 +- src/lib/pretty-print.js | 10 +++------- typings/pretty-print.d.ts | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/__tests__/debug.js b/src/__tests__/debug.js index 5517e44..18d49e0 100644 --- a/src/__tests__/debug.js +++ b/src/__tests__/debug.js @@ -24,7 +24,7 @@ test('debug pretty prints the baseElement', () => { test('debug can remove specified props from output', () => { const options = { - propsToRemove: ['style', 'pointerEvents', 'collapsable'], + removeProps: ['style', 'pointerEvents', 'collapsable'], }; const { debug } = render( diff --git a/src/lib/__tests__/pretty-print.js b/src/lib/__tests__/pretty-print.js index ec3c167..71f2e62 100644 --- a/src/lib/__tests__/pretty-print.js +++ b/src/lib/__tests__/pretty-print.js @@ -98,7 +98,7 @@ test('it supports removing props from output', () => { const options = { formatting: { - propsToRemove: ['style', 'pointerEvents'], + removeProps: ['style', 'pointerEvents'], }, }; diff --git a/src/lib/pretty-print.js b/src/lib/pretty-print.js index 888d60e..6ab09da 100644 --- a/src/lib/pretty-print.js +++ b/src/lib/pretty-print.js @@ -9,8 +9,8 @@ function prettyPrint(element, maxLength, options = {}) { let plugins = [ReactTestComponent, ReactElement]; const { formatting, ...rest } = options; - if (formatting && formatting.propsToRemove) { - const formatterPlugin = createFormatter(options.formatting.propsToRemove); + if (formatting && formatting.removeProps) { + const formatterPlugin = createFormatter(options.formatting.removeProps); plugins = [formatterPlugin, ...plugins]; } @@ -38,11 +38,7 @@ function createFormatter(propsToRemove) { } }); - if (ReactTestComponent.test(element)) { - return ReactTestComponent.serialize(element, config, indentation, depth, refs, printer); - } - - return ReactElement.serialize(element, config, indentation, depth, refs, printer); + return ReactTestComponent.serialize(element, config, indentation, depth, refs, printer); }, }; diff --git a/typings/pretty-print.d.ts b/typings/pretty-print.d.ts index ad436b3..b007498 100644 --- a/typings/pretty-print.d.ts +++ b/typings/pretty-print.d.ts @@ -5,7 +5,7 @@ export function prettyPrint( maxLength?: number, options: { formatting: { - propsToRemove: string[]; + removeProps: string[]; }; }, ): string | false; From 62b5c10f0dc3b9f35d35f19e4ab955e9c39c72d5 Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 30 Oct 2019 08:34:26 -0600 Subject: [PATCH 3/5] omitProps: remove prettyPrint plugin in favour of removing props in toJSON() call --- src/__tests__/debug.js | 14 +++++++++----- src/index.js | 4 ++-- src/lib/__tests__/pretty-print.js | 9 ++------- src/lib/pretty-print.js | 28 ++-------------------------- src/lib/to-json.js | 11 ++++++----- typings/pretty-print.d.ts | 6 +++--- 6 files changed, 24 insertions(+), 48 deletions(-) diff --git a/src/__tests__/debug.js b/src/__tests__/debug.js index 18d49e0..fca9299 100644 --- a/src/__tests__/debug.js +++ b/src/__tests__/debug.js @@ -11,6 +11,7 @@ beforeEach(() => { afterEach(() => { cleanup(); + log = undefined; console.log.mockRestore(); }); @@ -23,18 +24,21 @@ test('debug pretty prints the baseElement', () => { }); test('debug can remove specified props from output', () => { - const options = { - removeProps: ['style', 'pointerEvents', 'collapsable'], - }; - const { debug } = render( Hello World! , - { formatting: options }, + { + options: { + debug: { + omitProps: ['style', 'pointerEvents', 'collapsable'], + }, + }, + }, ); debug(); + expect(console.log).toHaveBeenCalledTimes(1); expect(log).toMatchInlineSnapshot(` " diff --git a/src/index.js b/src/index.js index 6a58ed3..d16fa23 100644 --- a/src/index.js +++ b/src/index.js @@ -14,7 +14,7 @@ import act from './act-compat'; const renderers = new Set(); -function render(ui, { options = {}, wrapper: WrapperComponent, queries, formatting = {} } = {}) { +function render(ui, { options = {}, wrapper: WrapperComponent, queries } = {}) { const wrapUiIfNeeded = innerElement => WrapperComponent ? ( @@ -39,7 +39,7 @@ function render(ui, { options = {}, wrapper: WrapperComponent, queries, formatti return { baseElement, container, - debug: (el = baseElement) => console.log(prettyPrint(el, undefined, { formatting })), + debug: (el = baseElement) => console.log(prettyPrint(el, undefined, { debug: options.debug })), unmount: () => testRenderer.unmount(), rerender: rerenderUi => { act(() => { diff --git a/src/lib/__tests__/pretty-print.js b/src/lib/__tests__/pretty-print.js index 71f2e62..d6d57b4 100644 --- a/src/lib/__tests__/pretty-print.js +++ b/src/lib/__tests__/pretty-print.js @@ -96,13 +96,8 @@ test('it supports removing props from output', () => { , ); - const options = { - formatting: { - removeProps: ['style', 'pointerEvents'], - }, - }; - - expect(prettyPrint(container, undefined, options)).toMatchInlineSnapshot(` + expect(prettyPrint(container, undefined, { debug: { omitProps: ['style', 'pointerEvents'] } })) + .toMatchInlineSnapshot(` " diff --git a/src/lib/pretty-print.js b/src/lib/pretty-print.js index 6ab09da..1b36c8a 100644 --- a/src/lib/pretty-print.js +++ b/src/lib/pretty-print.js @@ -7,14 +7,9 @@ const { ReactTestComponent, ReactElement } = prettyFormat.plugins; function prettyPrint(element, maxLength, options = {}) { let plugins = [ReactTestComponent, ReactElement]; - const { formatting, ...rest } = options; + const { debug, ...rest } = options; - if (formatting && formatting.removeProps) { - const formatterPlugin = createFormatter(options.formatting.removeProps); - plugins = [formatterPlugin, ...plugins]; - } - - const debugContent = prettyFormat(toJSON(element), { + const debugContent = prettyFormat(toJSON(element, debug), { plugins: plugins, printFunctionName: false, highlight: true, @@ -26,23 +21,4 @@ function prettyPrint(element, maxLength, options = {}) { : debugContent; } -function createFormatter(propsToRemove) { - const plugin = { - test(val) { - return val.props !== undefined; - }, - serialize(element, config, indentation, depth, refs, printer) { - Object.keys(element.props).map(prop => { - if (propsToRemove.includes(prop)) { - delete element.props[prop]; - } - }); - - return ReactTestComponent.serialize(element, config, indentation, depth, refs, printer); - }, - }; - - return plugin; -} - export { prettyPrint }; diff --git a/src/lib/to-json.js b/src/lib/to-json.js index 13c55f4..deae803 100644 --- a/src/lib/to-json.js +++ b/src/lib/to-json.js @@ -1,12 +1,12 @@ -function toJSON({ _fiber: { stateNode = null } = {} } = {}) { +function toJSON({ _fiber: { stateNode = null } = {} } = {}, options = {}) { if (!stateNode) return null; if (stateNode.rootContainerInstance && stateNode.rootContainerInstance.children.length === 0) return null; - return _toJSON(stateNode); + return _toJSON(stateNode, options); } -function _toJSON(inst) { +function _toJSON(inst, { omitProps = [] }) { if (inst.isHidden) { // Omit timed out children from output entirely. This seems like the least // surprising behavior. We could perhaps add a separate API that includes @@ -23,12 +23,13 @@ function _toJSON(inst) { const { children, ...props } = inst.props; // Convert all children to the JSON format - const renderedChildren = inst.children.map(child => _toJSON(child)); + const renderedChildren = inst.children.map(child => _toJSON(child, { omitProps })); // Function props get noisy in debug output, so we'll exclude them + // Also exclude any props configured via options.omitProps let renderedProps = {}; Object.keys(props).filter(name => { - if (typeof props[name] !== 'function') { + if (typeof props[name] !== 'function' && !omitProps.includes(name)) { renderedProps[name] = props[name]; } }); diff --git a/typings/pretty-print.d.ts b/typings/pretty-print.d.ts index b007498..1210deb 100644 --- a/typings/pretty-print.d.ts +++ b/typings/pretty-print.d.ts @@ -3,9 +3,9 @@ import { NativeTestInstance } from './query-helpers'; export function prettyPrint( element: NativeTestInstance | string, maxLength?: number, - options: { - formatting: { - removeProps: string[]; + options?: { + debug: { + omitProps: string[]; }; }, ): string | false; From 53cf44e10e4d2d0bb9e89d8ea8a319ae60d68337 Mon Sep 17 00:00:00 2001 From: andy Date: Sun, 3 Nov 2019 13:41:36 -0700 Subject: [PATCH 4/5] update options in render() to destructure debug config --- src/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index d16fa23..6f7ac4e 100644 --- a/src/index.js +++ b/src/index.js @@ -15,6 +15,8 @@ import act from './act-compat'; const renderers = new Set(); function render(ui, { options = {}, wrapper: WrapperComponent, queries } = {}) { + const { debug, ...rest } = options; + const wrapUiIfNeeded = innerElement => WrapperComponent ? ( @@ -27,7 +29,7 @@ function render(ui, { options = {}, wrapper: WrapperComponent, queries } = {}) { let testRenderer; act(() => { - testRenderer = TR.create(wrapUiIfNeeded(ui), options); + testRenderer = TR.create(wrapUiIfNeeded(ui), rest); }); renderers.add(testRenderer); @@ -39,7 +41,7 @@ function render(ui, { options = {}, wrapper: WrapperComponent, queries } = {}) { return { baseElement, container, - debug: (el = baseElement) => console.log(prettyPrint(el, undefined, { debug: options.debug })), + debug: (el = baseElement) => console.log(prettyPrint(el, undefined, { debug })), unmount: () => testRenderer.unmount(), rerender: rerenderUi => { act(() => { From 9c51afca3396632c66df01f7eb745b6f83baf2b8 Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 4 Nov 2019 11:31:24 -0700 Subject: [PATCH 5/5] update filter fn to be named selector to match testing library API --- src/lib/__tests__/misc.js | 46 +++++++++++++++++++++++++++++++- src/lib/queries/display-value.js | 4 +-- src/lib/queries/role.js | 4 +-- src/lib/queries/text.js | 4 +-- src/lib/queries/title.js | 4 +-- src/lib/query-helpers.js | 4 +-- 6 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/lib/__tests__/misc.js b/src/lib/__tests__/misc.js index 8de4f75..155debc 100644 --- a/src/lib/__tests__/misc.js +++ b/src/lib/__tests__/misc.js @@ -1,5 +1,5 @@ import React from 'react'; -import { Picker, Switch, View } from 'react-native'; +import { Picker, Switch, View, Text, TextInput, Button } from 'react-native'; import { render, queryByProp, queryByTestId, cleanup } from '../../'; @@ -33,3 +33,47 @@ it('should render test', () => { expect(getByDisplayValue(true)).toBeTruthy(); }); + +test('selector option in queries filter out elements', () => { + function filterByLabel(label) { + return { + selector: ({ props }) => props.accessibilityLabel === label, + }; + } + + const { getByText, getByRole, getByDisplayValue, getByTitle } = render( + <> + hello world + hello world + + + + + + + +