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

Implement createNodeMock for ReactTestRenderer #7649

Merged
merged 15 commits into from
Sep 13, 2016

Conversation

aweary
Copy link
Contributor

@aweary aweary commented Sep 2, 2016

This lets users pass in an optional testOptions object as the second argument to ReactTestRenderer.create that allows for custom mocking behavior. As of now that only includes createNodeMock, which accepts the current element and should return a mock ref object.

The testOptions is stored on the component instance so it doesn't leak between instances.

cc @gaearon @spicyj

@@ -20,6 +20,10 @@ var getHostComponentFromComposite = require('getHostComponentFromComposite');
var instantiateReactComponent = require('instantiateReactComponent');
var invariant = require('invariant');

type TestRendererMockConfig = {
getMockRef: (element: ReactElement) => Object,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure how this would be typed, is there a better way to type an object that looks like a DOM node?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks fine to me.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.01%) to 87.243% when pulling d7bd655 on Aweary:mock-dom-instance into a70acb3 on facebook:master.

@aweary
Copy link
Contributor Author

aweary commented Sep 3, 2016

FYI this has some issues with refs rendered by nested components, still working that out

@aweary
Copy link
Contributor Author

aweary commented Sep 5, 2016

Hmm, I'm not sure how I can efficiently provide access to the mockConfig to children at an arbitrary depth. I can walk the render tree and attach it to each instance, but that's not very efficient.

@gaearon
Copy link
Collaborator

gaearon commented Sep 5, 2016

I haven't looked at the diff in detail but I think you can make it have a custom transaction (like server rendering does). Then you can read the config from the transaction since it's threaded all the way down.

An alternative is to put context on TopLevelWrapper but this is probably more hacky.

@aweary
Copy link
Contributor Author

aweary commented Sep 5, 2016

Yeah I tried TopLevelWrapper first, but it's not actually set on all instances. It only seems to be set on instances at the top of the render tree. I'll give the transaction approach a try.

@gaearon
Copy link
Collaborator

gaearon commented Sep 5, 2016

Context should propagate downwards too so it must be something else. Regardless transaction seems to be a better approach.

@aweary
Copy link
Contributor Author

aweary commented Sep 5, 2016

For _topLevelWrapper you can see in ReactTestMount that it's only set on the top-level _renderedComponent. So that might just be another separate issue we should address.

I'll look into the transactional approach today 👍

@gaearon
Copy link
Collaborator

gaearon commented Sep 5, 2016

Does _topLevelWrapper field matter? I don't think that field is important for context propagation. Context is passed down through ReactCompositeComponent and afaik this should work any number of composites down.

@aweary
Copy link
Contributor Author

aweary commented Sep 5, 2016

Oh, no, sorry I was just responding to

An alternative is to put context on TopLevelWrapper but this is probably more hacky.

@aweary
Copy link
Contributor Author

aweary commented Sep 5, 2016

One issue I see with trying to use the transaction to propagate the mock config, is that ReactTestComponent.prototype.getPublicInstance doesn't seem to have access to any transaction object.

@gaearon
Copy link
Collaborator

gaearon commented Sep 5, 2016

Let's pass it in there?

@gaearon
Copy link
Collaborator

gaearon commented Sep 5, 2016

Actually it should be easier to save mock config from transaction in mountComponent as an instance field. Then access it anytime. Would that work?

@aweary
Copy link
Contributor Author

aweary commented Sep 5, 2016

Let's pass it in there?

ReactRef doesn't currently have access to transaction, but I suppose it could be threaded in from ReactReconcilier.

Actually it should be easier to save mock config from transaction in mountComponent as an instance field. Then access it anytime. Would that work?

Does it make sense to use the transaction as a host for instance-specific config? To make this work we'd have to call injectReconcileTransaction every time ReactTestRenderer.create is called, since mockConfig implementation can diff between calls.

Also, Is there a guarantee that transaction passed to mountComponent will always be the transaction type that we inject in create? If we have sequential create calls it seems like we may have a potential race condition where the next transaction type is injected before the first's mountComponent method is called.

Global injection makes this tricky 😄

@gaearon
Copy link
Collaborator

gaearon commented Sep 5, 2016

Maybe I misunderstand how it works but I thought transaction is created on every root mount. For example server rendering creates a transaction (passing makeStaticMarkup, a config just like getMockRef) on every renderToStringImpl call. I don't see any injection being done there, transaction is just passed as an argument to root reconciler call. Can we do the same here?

@aweary
Copy link
Contributor Author

aweary commented Sep 5, 2016

I'm probably the one misunderstanding, but from what I can see it injects the transaction class in ReactTestRenderer:

ReactUpdates.injection.injectReconcileTransaction(
  ReactTestReconcileTransaction
);

Then the actual transaction object is constructed from the object pool every time it's needed, which is on mount and on update

  var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
  var image = transaction.perform(
    mountComponentIntoNode,
    null,
    componentInstance,
    transaction
  );
  ReactUpdates.ReactReconcileTransaction.release(transaction);

but the way ReactServerRenderingTransaction works with makeStaticMarkup is that makeStaticMarkup is actually passed to the getPooled call every time the transaction is created. So even though the transaction is recreated from an injected global class, it gets that call-specific config every time. So I think we can just do the same here! I'll give that a try.

@gaearon
Copy link
Collaborator

gaearon commented Sep 5, 2016

Dynamic injection is a mess. Sorry.

@aweary
Copy link
Contributor Author

aweary commented Sep 5, 2016

Yes it is, and no sorry needed 🤘 It's at the very least a fun challenge

@aweary aweary closed this Sep 5, 2016
@aweary aweary reopened this Sep 5, 2016
@aweary
Copy link
Contributor Author

aweary commented Sep 12, 2016

@gaearon @spicyj updated it to createNodeMock 👍

@ghost ghost added the CLA Signed label Sep 12, 2016
@gaearon gaearon changed the title Implement getMockRef for ReactTestRenderer Implement createNode for ReactTestRenderer Sep 13, 2016
@gaearon gaearon changed the title Implement createNode for ReactTestRenderer Implement createNodeMock for ReactTestRenderer Sep 13, 2016
@gaearon
Copy link
Collaborator

gaearon commented Sep 13, 2016

Great job.
Leaving the honor of pressing "Squash and Merge" to you 😄

@ghost ghost added the CLA Signed label Sep 13, 2016
@gaearon gaearon assigned aweary and unassigned gaearon Sep 13, 2016
@ghost ghost added the CLA Signed label Sep 13, 2016
@aweary aweary merged commit f3569a2 into facebook:master Sep 13, 2016
@zpao zpao modified the milestones: 15-next, 15.4.0 Oct 4, 2016
zpao pushed a commit that referenced this pull request Oct 4, 2016
* Implement optional mockConfig and getMockRef

* default mockConfig, walk render tree

* Pass mockConfig to transaction

* Attach mockConfig to transaction

* type mockConfig in ReactRef

* Expect object in native component ref test

* Fix argument name for attachRefs

* Add mockConfig support to legacy refs

* Pass transaction to getPublicInstance

* Implement getMockConfig on ReactTestReconcileTransaction

* Merge defaultMockConfig and mockConfig options

* Rename mockConfig to testOptions

* Break getPublicInstnce into three lines

* createMockRef -> createNodeMock

(cherry picked from commit f3569a2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants