Skip to content

Commit

Permalink
fix: transformation of deeply nested arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
Filipoliko committed Feb 11, 2025
1 parent 126ae9f commit 56f913a
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/fuzzy-planets-approve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"shallow-react-snapshot": patch
---

Fix bug when component contains multiple deeply nested arrays. Enzyme did flatten the arrays, but Shallow React Snapshot did not. This is now fixed.
10 changes: 10 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ To run all tests, use `npm test`. This will execute tests for all supported reac

To run tests only for a single react version, use `npm test -- --selectProjects react-16`.

## Link

You can link the package to your project by running `npm link && npm run dev` in the `shallow-react-snapshot` project and `npm link shallow-react-snapshot` in your project. The `npm run dev` will watch for changes in the `shallow-react-snapshot` project and rebuild the package.

## Playground

When investigating react internals, it is often helpful to have a playground to test things out and check them directly in the browser.

Run `npm run playground` to open a server with a simple react component (you will be able to select the version). You can edit the rendered react component in `playground/main.jsx` file if you want to simulate a specific scenario.

## Codestyle

We are using [biome](https://biomejs.dev) for code formatting and linting.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc -p tsconfig.build.json",
"dev": "npm run build -- --watch",
"changeset": "changeset",
"lint": "npx @biomejs/biome check .",
"lint:fix": "npm run lint -- --write",
Expand Down
10 changes: 10 additions & 0 deletions src/__tests__/__snapshots__/class.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ exports[`Class component render react component with another react component as
</MyComponent>
`;

exports[`Class component render react component with array of children and component sibling 1`] = `
<div>
<div />
<div />
<div />
<div />
<div />
</div>
`;

exports[`Class component render react component with deeply nested multiple react components as a child 1`] = `
<MyComponent>
<WrapperAroundMyComponent>
Expand Down
10 changes: 10 additions & 0 deletions src/__tests__/__snapshots__/functional.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,16 @@ exports[`Functional component render react component with another whitelisted re
</MyComponent>
`;

exports[`Functional component render react component with array of children and component sibling 1`] = `
<div>
<div />
<div />
<div />
<div />
<div />
</div>
`;

exports[`Functional component render react component with deeply nested multiple react components as a child 1`] = `
<MyComponent>
<WrapperAroundMyComponent>
Expand Down
22 changes: 22 additions & 0 deletions src/__tests__/class.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -424,4 +424,26 @@ describe("Class component render", () => {

expect(result).toMatchSnapshot();
});

test("react component with array of children and component sibling", () => {
class App extends Component {
render() {
return (
<div>
{[
<div key={0} />,
<div key={1} />,
[<div key={2} />, [<div key={3} />]],
]}
<div />
</div>
);
}
}
const { container } = render(<App />);

const result = shallow(container, App);

expect(result).toMatchSnapshot();
});
});
20 changes: 20 additions & 0 deletions src/__tests__/functional.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -476,4 +476,24 @@ describe("Functional component render", () => {

expect(result).toMatchSnapshot();
});

test("react component with array of children and component sibling", () => {
function App() {
return (
<div>
{[
<div key={0} />,
<div key={1} />,
[<div key={2} />, [<div key={3} />]],
]}
<div />
</div>
);
}
const { container } = render(<App />);

const result = shallow(container, App);

expect(result).toMatchSnapshot();
});
});
20 changes: 18 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,13 +389,15 @@ function getProps(
function getChildrenFromProps(
node: ChildrenFiberOrInternalInstance,
): ReactTestChild[] | null {
const { children } = node.props || (node.children && node) || {};
const { children } = node.props || node;

if (!children) {
return null;
}

const arrayOfChildren = Array.isArray(children) ? children : [children];
const arrayOfChildren = flattenNestedArrays(
Array.isArray(children) ? children : [children],
) as ChildrenFiberOrInternalInstance[];

return arrayOfChildren.filter(reactFalsyValuesFilter).map((child) => {
// If child is any non-object value (number, string), return it as is
Expand All @@ -412,6 +414,20 @@ function getChildrenFromProps(
});
}

/**
* Convert structures like `[<div />, <div />, [<div />, [<div />, <div />]]]` to `[<div />, <div />, <div />, <div />, <div />]`
*/
// biome-ignore lint/suspicious/noExplicitAny: We are very generic here, you can really pass anything
function flattenNestedArrays(array: any[]): any[] {
return array.reduce((acc, value) => {
if (Array.isArray(value)) {
return acc.concat(flattenNestedArrays(value));
}

return acc.concat(value);
}, []);
}

/**
* Filter falsy values from React children
*/
Expand Down

0 comments on commit 56f913a

Please sign in to comment.