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

Handle arrays when merging snapshots #7089

Merged
merged 10 commits into from Apr 22, 2019

Conversation

Projects
None yet
@timtrinidad
Copy link
Contributor

commented Oct 2, 2018

Summary

#6455 fixes properties in an object being left out of the snapshot if they are not specified in propertyMatchers by merging the matchers and the received snapshot.

However, arrays are not being merged so fields are being left out in the snapshot. For example:

const result = {
  data: {
    one: 'one',
    two: 'two',
    three: [
      {
        four: 'four',
        five: 'five',
      },
    ],
  }
}

expect(result).toMatchSnapshot({
  data: {
    two: expect.any(String),
    three: [
      {
        four: expect.any(String),
      },
    ],
  },
});

Expected Snapshot:

exports[`snapshot 1`] = `
Object {
  "data": Object {
    "one": "one",
    "three": Array [
      Object {
        "five": "five", // <-- Missing from "Actual"
        "four": Any<String>,
      },
    ],
    "two": Any<String>,
  },
}
`;

Actual snapshot:

exports[`snapshot 1`] = `
Object {
  "data": Object {
    "one": "one",
    "three": Array [
      Object {
        "four": Any<String>,
      },
    ],
    "two": Any<String>,
  },
}
`;

Test plan

The test case has been updated to include an array to be merged for testing deepMerge.

@facebook-github-bot

This comment has been minimized.

Copy link

commented Oct 2, 2018

Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please sign up at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need the corporate CLA signed.

If you have received this in error or have any questions, please contact us at cla@fb.com. Thanks!

@SimenB SimenB requested a review from rickhanlonii Oct 2, 2018

@facebook-github-bot

This comment has been minimized.

Copy link

commented Oct 2, 2018

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks!

@SimenB

This comment has been minimized.

Copy link
Collaborator

commented Oct 18, 2018

@rickhanlonii mind reviewing this?

@@ -185,6 +185,15 @@ export const deepMerge = (target: any, source: any) => {
if (isObject(source[key]) && !source[key].$$typeof) {
if (!(key in target)) Object.assign(mergedOutput, {[key]: source[key]});
else mergedOutput[key] = deepMerge(target[key], source[key]);
} else if (Array.isArray(source[key])) {
// Convert arrays to objects, merge, convert back to array

This comment has been minimized.

Copy link
@thymikee

thymikee Oct 30, 2018

Collaborator

Sounds expensive. Can we instead make deepMergeArray similar to what we have in deepCyclicCopy.js?

function deepCyclicCopyArray(
array: Array<any>,
options: DeepCyclicCopyOptions,
cycles: WeakMap<any, any>,
): Array<any> {
const newArray = options.keepPrototype
? // $FlowFixMe: getPrototypeOf an array is OK.
new (Object.getPrototypeOf(array)).constructor(array.length)
: [];
const length = array.length;
cycles.set(array, newArray);
for (let i = 0; i < length; i++) {
newArray[i] = deepCyclicCopy(
array[i],
{blacklist: EMPTY, keepPrototype: options.keepPrototype},
cycles,
);
}
return newArray;
}

@rickhanlonii

This comment has been minimized.

Copy link
Member

commented Dec 2, 2018

Hey @timtrinidad, sorry for the delay

I think we can close this if we do #7128 instead

cc @thymikee

@timtrinidad

This comment has been minimized.

Copy link
Contributor Author

commented Dec 2, 2018

Thanks for looking at this! I think #7128 is still different - the snapshot test in this issue only includes asymmetric matchers.

@rickhanlonii

This comment has been minimized.

Copy link
Member

commented Dec 2, 2018

Ah, yes I understand now, thanks @timtrinidad

@rickhanlonii
Copy link
Member

left a comment

Agree with @thymikee that we should use deepMergeArray, and the changelog needs fixed, then this can go 👍

Sorry again for the delay

@@ -195,12 +195,57 @@ test('serialize handles \\r\\n', () => {

describe('DeepMerge', () => {
it('Correctly merges objects with property matchers', () => {
const target = {data: {bar: 'bar', foo: 'foo'}};
/* eslint-disable sort-keys */
// to keep keys in numerical order rather than alphabetical

This comment has been minimized.

Copy link
@rickhanlonii

rickhanlonii Dec 2, 2018

Member

Are you just adding a missing test or is this a changed behavior?

This comment has been minimized.

Copy link
@timtrinidad

timtrinidad Dec 3, 2018

Author Contributor

This is adding a missing test for the case of nested matchers.

four: 'four',
five: 'five',
},
// Include an array element not present in the propertyMatchers

This comment has been minimized.

Copy link
@timtrinidad

timtrinidad Dec 3, 2018

Author Contributor

I added an extra array element to ensure behavior between differently-sized arrays works as expected

};
// Don't use `expect.any(string)` since that will cause a false positive
// if deepMerge incorrectly keeps two as 'two' from the target
const matcher = '--matcher--';

This comment has been minimized.

Copy link
@timtrinidad

timtrinidad Dec 3, 2018

Author Contributor

This was originally const matcher = expect.any(string), but when messing with util.deepMerge() I noticed if I just return the target unmodified, this returned a false positive.

@@ -178,13 +178,32 @@ export const saveSnapshotFile = (
);
};

const deepMergeArray = (target, source) => {

This comment has been minimized.

Copy link
@timtrinidad

timtrinidad Dec 3, 2018

Author Contributor

Inspired by the deepmerge documentation.

I looked at replacing util.deepMerge() with that npm package since it's already used indirectly by jest-website, but it looks like we had to implement the array merging logic manually either way.

@SimenB

This comment has been minimized.

Copy link
Collaborator

commented Dec 25, 2018

@rickhanlonii ping :)

@yofriadi

This comment has been minimized.

Copy link

commented Feb 18, 2019

Hi guys, when will this get merged, I've been waiting for months, thank you :)

@codecov-io

This comment has been minimized.

Copy link

commented Apr 22, 2019

Codecov Report

Merging #7089 into master will increase coverage by <.01%.
The diff coverage is 63.63%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #7089      +/-   ##
==========================================
+ Coverage   62.22%   62.22%   +<.01%     
==========================================
  Files         266      266              
  Lines       10720    10731      +11     
  Branches     2609     2611       +2     
==========================================
+ Hits         6670     6677       +7     
- Misses       3463     3465       +2     
- Partials      587      589       +2
Impacted Files Coverage Δ
packages/jest-snapshot/src/utils.ts 88.31% <63.63%> (-4.12%) ⬇️

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 65b48a7...1dada66. Read the comment docs.

@@ -185,8 +185,6 @@ const deepMergeArray = (target: Array<any>, source: Array<any>) => {
source.forEach((element, index) => {
if (typeof mergedOutput[index] === 'undefined') {
mergedOutput[index] = element;
} else if (typeof element === 'undefined') {

This comment has been minimized.

Copy link
@timtrinidad

timtrinidad Apr 22, 2019

Author Contributor

In adding tests to cover this line, I realized that this case really shouldn't happen (i.e. source having an array element that's undefined)

@SimenB

SimenB approved these changes Apr 22, 2019

@jeysal

jeysal approved these changes Apr 22, 2019

Copy link
Collaborator

left a comment

Nice work @timtrinidad!
Sorry this was left open so long, and thanks for your swift responses after Simen brought this up again :)

@SimenB SimenB merged commit 7e1e3f8 into facebook:master Apr 22, 2019

11 checks passed

ci/circleci: lint-and-typecheck Your tests passed on CircleCI!
Details
ci/circleci: test-browser Your tests passed on CircleCI!
Details
ci/circleci: test-jest-circus Your tests passed on CircleCI!
Details
ci/circleci: test-node-10 Your tests passed on CircleCI!
Details
ci/circleci: test-node-11 Your tests passed on CircleCI!
Details
ci/circleci: test-node-6 Your tests passed on CircleCI!
Details
ci/circleci: test-node-8 Your tests passed on CircleCI!
Details
ci/circleci: test-or-deploy-website Your tests passed on CircleCI!
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
deploy/netlify Deploy preview ready!
Details
facebook.jest #20190422.35 succeeded
Details
@scotthovestadt

This comment has been minimized.

Copy link
Contributor

commented May 1, 2019

I have to revert this PR for now. It introduces a simple regression around strings being converted to objects, which I took the time to fix here scotthovestadt@3fd5434 but it also introduces a regression around breaking expect.any(...) matchers within arrays, which looks like a more complicated fix. We're doing a release soon and I don't have enough confidence that any fix I do will be sufficient.

Happy to have this merged in once it doesn't break those use-cases.

@pedrottimark
Copy link
Collaborator

left a comment

Nevermind. I was in the wrong place when I did this.

scotthovestadt added a commit that referenced this pull request May 1, 2019

@SimenB

This comment has been minimized.

Copy link
Collaborator

commented May 1, 2019

@timtrinidad the revert included regression tests, so if you feel up for it I'd love a new PR landing this 🙂

timtrinidad added a commit to timtrinidad/jest that referenced this pull request May 1, 2019

@timtrinidad

This comment has been minimized.

Copy link
Contributor Author

commented May 1, 2019

This updated PR (#8408) checks specifically for arrays of arrays and arrays of scalars. The various tests were also split into cases for readability and DRYness.

@joartola

This comment has been minimized.

Copy link

commented Jun 11, 2019

Is this issue fixed? If it is, in what version of jest was fixed?

@timtrinidad

This comment has been minimized.

Copy link
Contributor Author

commented Jun 11, 2019

Not yet - PR #8408 is still open.

SimenB added a commit that referenced this pull request Jun 13, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.