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

Remove tests in ReactDOMComponent-test depending on internal API #11337

Merged

Conversation

AudyOdi
Copy link
Contributor

@AudyOdi AudyOdi commented Oct 23, 2017

Removed inputValueTracking dependency in ReactDOMComponent-test based on list in #11299. I took this approach based on #11309 review. Please let me know if this is not the way we should test it.

Copy link
Collaborator

@gaearon gaearon left a comment

Choose a reason for hiding this comment

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

Doesn't this just create another tracker on top? I'm not sure this actually tells us that the existing tracker works.

I think there might not be a super clear way to implement this. You could just read the tracker from node. _valueTracker instead (and assert it exists). This does test an implementation detail, but at least it’ll be obvious when it breaks. I’m open to better suggestions though.

@zhaozhiming
Copy link

Hi @gaearon , could just use the container to verify the value? Thanks.

it('should track input values', () => {
      var container = document.createElement('div');
      var inst = ReactDOM.render(
        <input type="text" defaultValue="foo" />,
        container,
      );
-
-     var tracker = inputValueTracking._getTrackerFromNode(inst);
-
-     expect(tracker.getValue()).toEqual('foo');
+     expect(container.firstChild.value).toEqual('foo');
    });

@AudyOdi
Copy link
Contributor Author

AudyOdi commented Oct 24, 2017

@gaearon Thank you for reviewing. Actually I did it the way you mentioned, using node._valueTracker, but then I realized that _valueTracker is also a private API because it begins with _. But if it turns out it's fine to use it, I will do it. I also had the same question with @zhaozhiming because in this file we should test the value of the component that honors defaultValue (sets value accordingly), not the tracker right?

@gaearon
Copy link
Collaborator

gaearon commented Oct 24, 2017

I haven’t dived deeply into what it’s supposed to test. (I’m not very familiar with that part of code.)

But it would be nice if you could try to “break” it on master by doing bad changes to inputValueTracking, and then see if you can still “break” it in your branch. In other words, we want to be sure the test actually covers the code.

@AudyOdi AudyOdi force-pushed the remove-inputvaluetrackingdep-reactdomcomp branch from 06be042 to 7117f52 Compare October 26, 2017 03:18
@AudyOdi
Copy link
Contributor Author

AudyOdi commented Oct 26, 2017

@gaearon I took your advice to use node._valueTracker, so the getTracker function will just return the existing one instead of creating new tracker. I also added some assertions to make sure the tracker exists and tracks the value. I also assert the value doesn't change if the defaultValue is changed afterwards.

@AudyOdi AudyOdi force-pushed the remove-inputvaluetrackingdep-reactdomcomp branch from 7117f52 to 3e9bfe3 Compare October 26, 2017 14:52
@zhaozhiming
Copy link

@AudyOdi Maybe you can take a look at this PR(#11309). The changes have a bit similar.

@gaearon
Copy link
Collaborator

gaearon commented Oct 27, 2017

Yes. I think it would be better to apply the same strategy as in #11309, if possible.

@AudyOdi
Copy link
Contributor Author

AudyOdi commented Oct 27, 2017

based on #11309, should I change the getTracker function to use Object.getOwnPropertyDescriptor too? So it will look like below (I can adjust the name later).

var getTracker = (node) => {
    return Object.getOwnPropertyDescriptor(
        node.constructor.prototype,
        'value',
    ).get;
};

and then in the test case, I still can get the value of the input by using call.

var tracker = getTracker(inst);
tracker.call(node);  // should return the value

this is possible because Object.getOwnPropertyDescriptor has both get and set function.
what do you think about this way? @zhaozhiming @gaearon

@zhaozhiming
Copy link

@AudyOdi Yes, I think so.

@zhaozhiming
Copy link

zhaozhiming commented Oct 30, 2017

Maybe we can invite @SadPandaBear to look at these changes are right?
Hi @SadPandaBear Could you help us to review this PR? Thanks.

   var ReactTestUtils;
   var ReactDOM;
   var ReactDOMServer;
-  var inputValueTracking;

   function normalizeCodeLocInfo(str) {
     return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
@@ -26,8 +25,6 @@ describe('ReactDOMComponent', () => {
     ReactDOM = require('react-dom');
     ReactDOMServer = require('react-dom/server');
     ReactTestUtils = require('react-dom/test-utils');
-    // TODO: can we express this test with only public API?
-    inputValueTracking = require('../client/inputValueTracking');
   });

   describe('updateDOM', () => {
@@ -833,6 +830,11 @@ describe('ReactDOMComponent', () => {
   describe('mountComponent', () => {
     var mountComponent;

+    var getTrackedValue = Object.getOwnPropertyDescriptor(
+      HTMLInputElement.prototype,
+      'value',
+    ).get;
+
     beforeEach(() => {
       mountComponent = function(props) {
         var container = document.createElement('div');
@@ -1151,19 +1153,13 @@ describe('ReactDOMComponent', () => {
         <input type="text" defaultValue="foo" />,
         container,
       );
-
-      var tracker = inputValueTracking._getTrackerFromNode(inst);
-
-      expect(tracker.getValue()).toEqual('foo');
+      expect(getTrackedValue.call(inst)).toEqual('foo');
     });

     it('should track textarea values', () => {
       var container = document.createElement('div');
       var inst = ReactDOM.render(<textarea defaultValue="foo" />, container);
-
-      var tracker = inputValueTracking._getTrackerFromNode(inst);
-
-      expect(tracker.getValue()).toEqual('foo');
+      expect(getTrackedValue.call(inst)).toEqual('foo');
     });

     it('should throw for children on void elements', () => {

@AudyOdi
Copy link
Contributor Author

AudyOdi commented Oct 30, 2017

@gaearon @zhaozhiming I have submitted new commit that use Object.getOwnPropertyDescriptor for tracking input value and tried to take similar approach with #11309, it's ready to be review. I'm totally open for any feedback.

node.constructor.prototype,
'value',
).get;
};
Copy link
Contributor

@SadPandaBear SadPandaBear Oct 30, 2017

Choose a reason for hiding this comment

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

Nice!

Really liked what you did here for the html interfaces. 👍

@@ -1151,19 +1154,37 @@ describe('ReactDOMComponent', () => {
<input type="text" defaultValue="foo" />,
container,
);
var getTrackedValue = getValueTracker(inst);
Copy link
Contributor

@SadPandaBear SadPandaBear Oct 30, 2017

Choose a reason for hiding this comment

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

I'm not sure if you do have to create an instance for the trackers. See, the solution I made for ReactDOMInput-test actually only had the calls for the input values i wanted to test.

Anyway, I can't see any trouble really about this solution. 😄

I see you are setting the value directly into inst. What happens if you set with the tracker instance you created (like this)? Will the test still pass (considering you write the setUntrackedValue and everything)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@SadPandaBear Thank you for reviewing. I have confirmed that changing value using tracker instance still pass the test. The reason I create an instance for the tracker is because getValueTracker is being used for both input and textarea element. Not sure about using HTMLInputElement for textarea is the correct way to do it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure about using HTMLInputElement for textarea is the correct way to do it.

Can you declare two separate helpers (for input and textarea) instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@gaearon got it, I will define the helper in each test case instead so that input will use HTMLInputElement, and textarea will use HTMLTextAreaElement

Copy link
Collaborator

@gaearon gaearon left a comment

Choose a reason for hiding this comment

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

Let's use the prototype getter to make sure we're reading the right thing. Since we test both input and textarea, let's read each and keep them in separate variables.

@AudyOdi
Copy link
Contributor Author

AudyOdi commented Oct 31, 2017

@gaearon I push new commit that move getValueTracker to each test case and use the corresponding prototype to get the tracker. It's ready to be review.

expect(getValueTracker.call(inst)).toEqual('foo');

inst.defaultValue = 'new foo';
expect(getValueTracker.call(inst)).not.toEqual('new foo');
Copy link
Collaborator

Choose a reason for hiding this comment

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

This check is redundant. You already check for the other value later.

@@ -1152,18 +1149,46 @@ describe('ReactDOMComponent', () => {
container,
);

var tracker = inputValueTracking._getTrackerFromNode(inst);
var getValueTracker = Object.getOwnPropertyDescriptor(
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should ideally be done before React is imported.

@gaearon
Copy link
Collaborator

gaearon commented Nov 3, 2017

Leaving this to @jquense to review because I don't understand what the original test is supposed to test.

@gaearon gaearon requested a review from jquense November 3, 2017 20:55
@jquense
Copy link
Contributor

jquense commented Nov 3, 2017

So the original test is supposed to confirm that the input tracking stuff is set up from initial values when you render an input for the first time. Which is to say that the tracking logic ensures change events don't fire twice for the same value.

I'm trying to think of a public side effect that would indicate this isn't working...I think the test case might be that if you initialize an input with value 'foo' then explicitly set the value to 'foo' it should be a noop, e.g. onChange doesn't fire. You'd have to use the same magic that the ChangeEventPlugin tests use tho to set an untracked value. Frankly tho this case is probably covered but the ChangeEventPlugin tests

@jquense
Copy link
Contributor

jquense commented Nov 3, 2017

If you wanted to keep it simple, I'd just test that the DOM node has the tracker attached to it and has the same value as the jsx value. Not quite public api only but I'm not really sure anything else is gonna succinctly test the right thing

@gaearon
Copy link
Collaborator

gaearon commented Nov 3, 2017

Why is it important to test that the tracker is attached? Does something break if it's not? :-)

@jquense
Copy link
Contributor

jquense commented Nov 4, 2017

You'd en up defeating the change dedupe logic at least... Tbh I don't remember if there is a more severe consequence. I do think this set of tests doesn't really belong here. They make sense when testing the specific implementation of the tracker getting attached here, but ultimately this is change event specific code

@gaearon
Copy link
Collaborator

gaearon commented Nov 4, 2017

Is there any way we can salvage these tests to offer the same coverage they currently offer without actually relying on implementation details? Or can we just delete them?

@AudyOdi
Copy link
Contributor Author

AudyOdi commented Nov 6, 2017

Thanks @jquense and @gaearon. I appreciate the insight. The original test was only testing if the tracker is attached, I added some other tests because I didn't know that it's been covered in ChangeEventPlugin test like you mentioned. I'll wait for you to determine if this should be moved, removed or replace.

Copy link
Collaborator

@gaearon gaearon left a comment

Choose a reason for hiding this comment

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

Let's just remove these test.
I'll add a follow up to #11333 instead.

@AudyOdi
Copy link
Contributor Author

AudyOdi commented Nov 9, 2017

@gaearon got it. I just pushed new commit that remove the tests and change it back to original test, also move the value tracker definition, for both input and textarea, before React is imported. It's ready to review.


function normalizeCodeLocInfo(str) {
return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
}

beforeEach(() => {
jest.resetModules();

getInputValueTracker = Object.getOwnPropertyDescriptor(
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand what this does in the context of this test.

As @jquense explained above, the original purpose of the test is to verify our custom tracker object "sees" the original value.

However this notion of "tracker" is not the same thing that you get from the HTMLInputElement's prototype. I think you got confused by naming used in some other test.

The thing you read from HTMLInputElement prototype is just the jsdom's value accessor itself. So there is no need to test it at all. You're just testing that jsdom works. (But we already know it works as it has its own test suite :-)

@gaearon gaearon changed the title Rewrite ReactDOMComponent-test depending on internal API Remove tests in ReactDOMComponent-test depending on internal API Nov 10, 2017
@gaearon gaearon merged commit ae7639c into facebook:master Nov 10, 2017
@gaearon
Copy link
Collaborator

gaearon commented Nov 10, 2017

I just removed these tests, as they aren't very useful, and we'll cover the relevant scenarios as part of ChangeEventPlugin-test instead. Thanks for your help on this, and sorry the back and forth might have been frustrating.

@AudyOdi
Copy link
Contributor Author

AudyOdi commented Nov 10, 2017

@gaearon No problem! Yes I agree that the tests are not that useful, and a lot of confusion with Object.getOwnPropertyDescriptor itself. Thank you for reviewing this diff and giving a lot of feedbacks. I would love to contribute again in the future. Please let me know if there is a chance to contribute (posting in your twitter really does help!) 😄
also thanks to @zhaozhiming @SadPandaBear @jquense for the help and insight.

@gaearon gaearon mentioned this pull request Nov 20, 2017
26 tasks
Ethan-Arrowood pushed a commit to Ethan-Arrowood/react that referenced this pull request Dec 8, 2017
…ebook#11337)

* Remove inputValueTracking from ReactDOMComponent-test dependency

* prettier

* use node._valueTracker and add some test cases to make sure that value being tracked

* using Object.getOwnPropertyDescriptor to get the tracked value

* move getValueTracker to each test case and use its corresponding prototype

* remove tests and move the value tracker definition before React is imported

* Delete these tests completely
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.

6 participants