diff --git a/packages/react-dom/src/events/__tests__/SelectEventPlugin-test.internal.js b/packages/react-dom/src/events/__tests__/SelectEventPlugin-test.internal.js
deleted file mode 100644
index 51795d9b48fd4..0000000000000
--- a/packages/react-dom/src/events/__tests__/SelectEventPlugin-test.internal.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * Copyright (c) 2013-present, Facebook, Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @emails react-core
- */
-
-'use strict';
-
-var React;
-var ReactDOM;
-var ReactDOMComponentTree;
-var ReactTestUtils;
-var SelectEventPlugin;
-
-describe('SelectEventPlugin', () => {
- function extract(node, topLevelEvent) {
- return SelectEventPlugin.extractEvents(
- topLevelEvent,
- ReactDOMComponentTree.getInstanceFromNode(node),
- {target: node},
- node,
- );
- }
-
- beforeEach(() => {
- React = require('react');
- ReactDOM = require('react-dom');
- ReactTestUtils = require('react-dom/test-utils');
- // TODO: can we express this test with only public API?
- ReactDOMComponentTree = require('../../client/ReactDOMComponentTree');
- SelectEventPlugin = require('../SelectEventPlugin').default;
- });
-
- it('should skip extraction if no listeners are present', () => {
- class WithoutSelect extends React.Component {
- render() {
- return ;
- }
- }
-
- var rendered = ReactTestUtils.renderIntoDocument();
- var node = ReactDOM.findDOMNode(rendered);
- node.focus();
-
- // It seems that .focus() isn't triggering this event in our test
- // environment so we need to ensure it gets set for this test to be valid.
- var fakeNativeEvent = function() {};
- fakeNativeEvent.target = node;
- ReactTestUtils.simulateNativeEventOnNode('topFocus', node, fakeNativeEvent);
-
- var mousedown = extract(node, 'topMouseDown');
- expect(mousedown).toBe(null);
-
- var mouseup = extract(node, 'topMouseUp');
- expect(mouseup).toBe(null);
- });
-
- it('should extract if an `onSelect` listener is present', () => {
- class WithSelect extends React.Component {
- render() {
- return ;
- }
- }
-
- var cb = jest.fn();
-
- var rendered = ReactTestUtils.renderIntoDocument(
- ,
- );
- var node = ReactDOM.findDOMNode(rendered);
-
- node.selectionStart = 0;
- node.selectionEnd = 0;
- node.focus();
-
- var focus = extract(node, 'topFocus');
- expect(focus).toBe(null);
-
- var mousedown = extract(node, 'topMouseDown');
- expect(mousedown).toBe(null);
-
- var mouseup = extract(node, 'topMouseUp');
- expect(mouseup).not.toBe(null);
- expect(typeof mouseup).toBe('object');
- expect(mouseup.type).toBe('select');
- expect(mouseup.target).toBe(node);
- });
-});
diff --git a/packages/react-dom/src/events/__tests__/SelectEventPlugin-test.js b/packages/react-dom/src/events/__tests__/SelectEventPlugin-test.js
new file mode 100644
index 0000000000000..f01a406d409f7
--- /dev/null
+++ b/packages/react-dom/src/events/__tests__/SelectEventPlugin-test.js
@@ -0,0 +1,111 @@
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @emails react-core
+ */
+
+'use strict';
+
+var React;
+var ReactDOM;
+
+describe('SelectEventPlugin', () => {
+ var container;
+
+ beforeEach(() => {
+ React = require('react');
+ ReactDOM = require('react-dom');
+
+ container = document.createElement('div');
+ document.body.appendChild(container);
+ });
+
+ afterEach(() => {
+ document.body.removeChild(container);
+ container = null;
+ });
+
+ // See https://github.com/facebook/react/pull/3639 for details.
+ it('does not get confused when dependent events are registered independently', () => {
+ var select = jest.fn();
+ var onSelect = event => {
+ expect(typeof event).toBe('object');
+ expect(event.type).toBe('select');
+ expect(event.target).toBe(node);
+ select(event.currentTarget);
+ };
+
+ // Pass `onMouseDown` so React registers a top-level listener.
+ var node = ReactDOM.render(
+ ,
+ container,
+ );
+ node.focus();
+
+ // Trigger `mousedown` and `mouseup`. Note that
+ // React is not currently listening to `mouseup`.
+ node.dispatchEvent(
+ new MouseEvent('mousedown', {
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+ node.dispatchEvent(
+ new MouseEvent('mouseup', {
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+
+ // Now subscribe to `onSelect`.
+ ReactDOM.render(, container);
+ node.focus();
+
+ // This triggers a `select` event in our polyfill.
+ node.dispatchEvent(
+ new KeyboardEvent('keydown', {bubbles: true, cancelable: true}),
+ );
+
+ // Verify that it doesn't get "stuck" waiting for
+ // a `mouseup` event that it has "missed" because
+ // a top-level listener didn't exist yet.
+ expect(select.mock.calls.length).toBe(1);
+ });
+
+ it('should fire `onSelect` when a listener is present', () => {
+ var select = jest.fn();
+ var onSelect = event => {
+ expect(typeof event).toBe('object');
+ expect(event.type).toBe('select');
+ expect(event.target).toBe(node);
+ select(event.currentTarget);
+ };
+
+ var node = ReactDOM.render(
+ ,
+ container,
+ );
+ node.focus();
+
+ var nativeEvent = new MouseEvent('focus', {
+ bubbles: true,
+ cancelable: true,
+ });
+ node.dispatchEvent(nativeEvent);
+ expect(select.mock.calls.length).toBe(0);
+
+ nativeEvent = new MouseEvent('mousedown', {
+ bubbles: true,
+ cancelable: true,
+ });
+ node.dispatchEvent(nativeEvent);
+ expect(select.mock.calls.length).toBe(0);
+
+ nativeEvent = new MouseEvent('mouseup', {bubbles: true, cancelable: true});
+ node.dispatchEvent(nativeEvent);
+ expect(select.mock.calls.length).toBe(1);
+ });
+});