-
-
Notifications
You must be signed in to change notification settings - Fork 17
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
Support wrappingComponent & wrappingComponentProps #157
Support wrappingComponent & wrappingComponentProps #157
Conversation
4029646
to
d135ae5
Compare
Thanks for the contribution. I'm happy to take a look in detail when this is out of draft. Please let me know if you run into any roadblocks along the way. |
@robertknight Great, thank you!
|
2225edb
to
6f1bb9c
Compare
@robertknight Alighty, it is done! Please take a look. I'm quite happy with the solution so far and it works with context providers. I sampled this change with several tests in my org and nearly all of them pass. Some tests don't pass because they do weird/deprecated stuff and those tests need to be refactored anyway. |
6f1bb9c
to
7d7790d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a few small queries and comments. Generally this looks good though. Please let me know once you've had a chance to look through and make any changes, and then I'll get this shipped ASAP.
src/Adapter.ts
Outdated
* Using those props complicates a potential future migration to a different testing library. | ||
* Instead, wrap a component like this: | ||
* ``` | ||
* shallow(<Wrapper><Component/></Wrapper>) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would tests then use wrapper.dive()
to get at the wrapped component?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. I verified that you need to add an additional .dive()
.
Before:
const wrapperProps = {
wrappingComponent: MyTestContext.Provider,
wrappingComponentProps: { value: { isMyTest: true } },
};
const wrapper: ShallowWrapper<{ isMyTest: boolean }> = shallow(
<MyTitle>sup</MyTitle>,
wrapperProps,
)
.dive()
.dive();
After:
const wrapper: ShallowWrapper<{ isMyTest: boolean }> = shallow(
<MyTestContext.Provider value={true}>
<MyTitle>sup</MyTitle>
</MyTestContext.Provider>,
wrapperProps,
)
.dive()
.dive()
.dive();
options?: ShallowRendererProps | ||
) => { | ||
return { | ||
RootFinder: RootFinder, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To check my understanding, RootFinder
is essentially a marker to allow location of the wrapped component in the rendered tree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand the Enzyme source code correctly, it's purpose is to properly pass down context, see here https://github.com/enzymejs/enzyme/blob/57baba5ceaccec7a3ada40d7559f5bf71289cbe7/packages/enzyme/src/ShallowWrapper.js#L356 and here https://github.com/enzymejs/enzyme/blob/57baba5ceaccec7a3ada40d7559f5bf71289cbe7/packages/enzyme/src/ShallowWrapper.js#L322. I tried what happens if I don't insert RootFinder into wrapWithWrappingComponent
and it didn't work.
@@ -0,0 +1,7 @@ | |||
import { Component } from 'preact'; | |||
|
|||
export default class RootFinder extends Component { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be useful to have a brief comment explaining what RootFinder
is used for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
|
||
let nodeWithValidChildren = node; | ||
|
||
if (typeof nodeWithValidChildren.props.children === 'string') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To summarize, this is normalizing VNodes like { type: Widget, props: { children: 'test' }, ... }
to { type: Widget, props: { children: ['test'] } }
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
src/wrapWithWrappingComponent.ts
Outdated
if (typeof nodeWithValidChildren.props.children === 'string') { | ||
// This prevents an error when `.dive()` is used: | ||
// `TypeError: ShallowWrapper::dive() can only be called on components` | ||
nodeWithValidChildren = Object.assign({}, nodeWithValidChildren); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Object.assign
does a shallow clone so nodeWithValidChildren.props
refers to the same object as node.props
. You can use cloneElement to clone the node in order to modify the props.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
test/MountRenderer_test.tsx
Outdated
.instance; | ||
const expectedInstance = renderedTree[0].instance; | ||
|
||
assert.equal(resultInstance.toString(), expectedInstance.toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the purpose of these asserts just to check that the rendered DOM structure is the same? If so comparing the outerHTML
of the two instances with a single assert may be simpler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@robertknight Thanks for the review! Very helpful. I followed up on your feedback. Additionally, I upgraded the Enzyme dependency to 3.11.0 because it allows diving into shallow components with context: enzymejs/enzyme#1966. Then I added three integration tests, one for mount
and two for shallow
.
One note: The change in this PR doesn't result in 100% matching behavior with the React adapter when wrappingComponent
is used, in particular when it comes to shallow rendering. This might need follow-up enhancements to this library. So far, for the few cases where I found a shallow test to pass with React's but not Preact's adapter, a good solution was to migrate the test to using mount
instead of shallow
.
options?: ShallowRendererProps | ||
) => { | ||
return { | ||
RootFinder: RootFinder, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand the Enzyme source code correctly, it's purpose is to properly pass down context, see here https://github.com/enzymejs/enzyme/blob/57baba5ceaccec7a3ada40d7559f5bf71289cbe7/packages/enzyme/src/ShallowWrapper.js#L356 and here https://github.com/enzymejs/enzyme/blob/57baba5ceaccec7a3ada40d7559f5bf71289cbe7/packages/enzyme/src/ShallowWrapper.js#L322. I tried what happens if I don't insert RootFinder into wrapWithWrappingComponent
and it didn't work.
src/Adapter.ts
Outdated
* Using those props complicates a potential future migration to a different testing library. | ||
* Instead, wrap a component like this: | ||
* ``` | ||
* shallow(<Wrapper><Component/></Wrapper>) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. I verified that you need to add an additional .dive()
.
Before:
const wrapperProps = {
wrappingComponent: MyTestContext.Provider,
wrappingComponentProps: { value: { isMyTest: true } },
};
const wrapper: ShallowWrapper<{ isMyTest: boolean }> = shallow(
<MyTitle>sup</MyTitle>,
wrapperProps,
)
.dive()
.dive();
After:
const wrapper: ShallowWrapper<{ isMyTest: boolean }> = shallow(
<MyTestContext.Provider value={true}>
<MyTitle>sup</MyTitle>
</MyTestContext.Provider>,
wrapperProps,
)
.dive()
.dive()
.dive();
@@ -0,0 +1,7 @@ | |||
import { Component } from 'preact'; | |||
|
|||
export default class RootFinder extends Component { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
|
||
let nodeWithValidChildren = node; | ||
|
||
if (typeof nodeWithValidChildren.props.children === 'string') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
src/wrapWithWrappingComponent.ts
Outdated
if (typeof nodeWithValidChildren.props.children === 'string') { | ||
// This prevents an error when `.dive()` is used: | ||
// `TypeError: ShallowWrapper::dive() can only be called on components` | ||
nodeWithValidChildren = Object.assign({}, nodeWithValidChildren); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
test/MountRenderer_test.tsx
Outdated
.instance; | ||
const expectedInstance = renderedTree[0].instance; | ||
|
||
assert.equal(resultInstance.toString(), expectedInstance.toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect 👍
51ef2af
to
9fc65e2
Compare
For new tests and when modernizing existing tests I would recommend using |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Thank-you for the contribution, including the extensive tests.
Published as v3.4.0. |
); | ||
}); | ||
|
||
it('passes wrappingComponentProps to wrappingComponent', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: A bit late because this PR is already merged but: I just realized that this test is basically a duplicate of the previous test. I used to run different testing logic in here but after some refactor I hadn't realized that it has become a redundant test. I'd suggest removing it in a future update.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind submitting a small follow-up PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done: #158
Support for
wrappingComponent
has been requested in #99 and this PR adds it for components rendered using theshallow
andmount
Enzyme utils.How was this tested?
wrappingComponent
andwrappingComponentProps
for bothshallow
andmount
components.Note for my future self: this is how I verified the changes locally…
yalc
is installed (as recommended in the contributing guide):yarn global add yalc
yalc publish
in the (forked) project locally.yalc add enzyme-adapter-preact-pure
. This updates the package.json and lock file. Then runyarn install
.yalc publish --push
and the other project will use the latest codeTips for faster development:
nodemon --watch "test/**" --watch "src/**" --ext "ts,tsx" --exec yarn test
nodemon --watch "src/**" --ext "ts" --exec "yalc publish --push --changed"