Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add alternative serialize API for pretty-format plugins #4114

Merged
merged 12 commits into from
Aug 1, 2017

Conversation

pedrottimark
Copy link
Contributor

@pedrottimark pedrottimark commented Jul 24, 2017

Summary

Fixes #3518 with breaking changes to edge cases:

  • Unlike core print functions for data structures, plugins have been indenting additional lines of multiline strings. It causes additional unnecessary diff line for props if a React element moves to a different level in the render tree.

  • In arrays of elements (from enzyme selector, or rendered fragment in React 16)

    • closing tags haven’t been indented at all (therefore, not to same level as opening tags)
    • objects or elements as props values have been indented too much

New alternative API added to pretty-format and implemented in this PR for 2 plugins:

  • ReactElement
  • ReactTestComponent
export type Test = any => boolean;

export NewPrinter = (
  val: any,
  indentation: string,
  depth: number,
  refs: Refs,
) => string;

export type NewPlugin = {|
  serialize: (
    val: any,
    config: Config,
    printer: NewPrinter,
    indentation: string,
    depth: number,
    refs: Refs,
  ) => string,
  test: Test,
|};

Next step: factor out common code in these 2 plugins

Test plan

New TDD tests that failed without these improvements:

  • supports a single element with [EDIT: empty string child and] zero number child (unexpected discovery while proofreading :)
  • supports props with multiline strings
  • supports array of elements

The other new tests explicitly specify existing correct behavior.

Updated snapshots because these improvements omit a redundant color reset:

  • ReactElement plugin highlights syntax
  • ReactTestComponent plugin

@codecov-io
Copy link

codecov-io commented Jul 24, 2017

Codecov Report

Merging #4114 into master will decrease coverage by <.01%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #4114      +/-   ##
==========================================
- Coverage   60.32%   60.31%   -0.01%     
==========================================
  Files         195      195              
  Lines        6757     6756       -1     
  Branches        6        6              
==========================================
- Hits         4076     4075       -1     
  Misses       2678     2678              
  Partials        3        3
Impacted Files Coverage Δ
.../pretty-format/src/plugins/react_test_component.js 100% <100%> (ø) ⬆️
packages/pretty-format/src/index.js 98.48% <100%> (ø) ⬆️
...ackages/pretty-format/src/plugins/react_element.js 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 13e375d...9550c22. Read the comment docs.

@pedrottimark
Copy link
Contributor Author

Interpretation of performance change:

  • complex data structures are a control which suggest a measurement error about 3%
  • proposed API (which allows same indentation concatenation as core functions) takes about 75% of the time compared to baseline API (which calls indent function at each level of render tree)
scenario plugins proposed baseline ratio
complex 0 229280 235289 0.974
complex 9 586444 602192 0.974
react_test_component 9 705062 980415 0.719
react_element 9 829921 1133482 0.732

@pedrottimark
Copy link
Contributor Author

Added a test to specify another falsey child for which the result of ReactElement plugin was inconsistent with ReactTestComponent plugin. Because assertPrintedJSX expects consistent results, formatting an element with one falsey child as empty was probably a bug not a feature.

expect(prettyFormatElementPlugin(val, options)).toEqual(formatted);
expect(
prettyFormatBothPlugins(renderer.create(val).toJSON(), options),
).toEqual(formatted);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:

const formattedElementPlugin = prettyFormatElementPlugin(val, options);
const formattedBothPlugins = prettyFormatBothPlugins(renderer.create(val).toJSON(), options),
expect(formattedElementPlugin).toEqual(formattedBothPlugins);

This way we only have one expect and still test the same thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One expect requires both plugins to return consistent results, which is necessary but not sufficient. I’ll replace formatted with expected to make it clearer why 2 expect are needed:

  • Both plugins could return consistent but incorrect results.
  • If one plugin is incorrect (for example, ReactElement for one falsey child) then a combined expect has a 50% chance of implying the wrong result is expected.

Something less than ideal about assertPrintedJSX is if a test fails, the message can’t identify which plugin :(

The test of array of elements caused me to factor out helper functions which I renamed and used in several test cases near the end:

  • formatElement
  • formatTestObject

@@ -49,13 +53,20 @@ test('supports a single element with no props', () => {
);
});

test('supports a single element with number children', () => {
test('supports a single element with nonzero number child', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: non-zero

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thank you. And I moved the new test for empty string to make the pairs match up.

indentationNext + str.replace(NEWLINE_REGEXP, '\n' + indentationNext)
);
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason why these functions are now inlined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thank you for asking. To avoid the problem in #3962 where printPlugin made unused closures for the callback functions, especially now that it might call either serialize or print property.

@pedrottimark
Copy link
Contributor Author

Oh, here is a question about react.test.js which also applies to other .test.js files

const prettyFormat = require('../');
const ReactTestComponent = require('../plugins/react_test_component');
const ReactElement = require('../plugins/react_element');

Should I change it to

const prettyFormat = require('../');
const {
  ReactElement,
  ReactTestComponent,
} = prettyFormat.plugins;

to follow the pattern in

@pedrottimark
Copy link
Contributor Author

Forgot to say after the first round of pull requests to replace print with serialize in plugins, the Immutable plugins can use depth and config.maxDepth just like core functions for JavaScript data structures. Probably worthwhile for HTMLElement, ReactElement, and ReactTestComponent too.

@thymikee
Copy link
Collaborator

Feel free to adjust the plugin requires to be consistent, sure!

@cpojer
Copy link
Member

cpojer commented Jul 30, 2017

We are now using Flow in the test files, please rebase.

@pedrottimark
Copy link
Contributor Author

Yes, Flow type for tests is a super improvement.

@cpojer
Copy link
Member

cpojer commented Aug 1, 2017

Good with me. Thanks again for the hard work @pedrottimark!

@cpojer cpojer merged commit 9665de2 into jestjs:master Aug 1, 2017
tushardhole pushed a commit to tushardhole/jest that referenced this pull request Aug 21, 2017
* Add alternative serialize API for pretty-format plugins

* Fix logic in ReactElement plugin and add test for zero number child

* Add test for empty string child

* Rename 2 helper functions and use in more places

* Replace require paths to plugins with destructuring assignment

* Add any type annotation to val arg of printPlugin

* Replace overlooked occurrences of formatted with expected

* Fix pretty lint error

* Add comment and repeat to indent test

* Add comment and repeat to indent test for react also

* Fix pretty lint error in html_element.test.js
@pedrottimark pedrottimark deleted the pretty-format-serialize branch April 6, 2019 18:32
@github-actions
Copy link

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Discuss edge cases of indentation in pretty-format plugins
5 participants