react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / browser / __tests__ / ReactBrowserEventEmitter-test.js
81155 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @emails react-core9*/1011"use strict";1213require('mock-modules')14.dontMock('EventPluginHub')15.dontMock('ReactMount')16.dontMock('ReactBrowserEventEmitter')17.dontMock('ReactInstanceHandles')18.dontMock('EventPluginHub')19.dontMock('TapEventPlugin')20.dontMock('TouchEventUtils')21.dontMock('keyOf');222324var keyOf = require('keyOf');25var mocks = require('mocks');2627var ReactMount = require('ReactMount');28var idToNode = {};29var getID = ReactMount.getID;30var setID = function(el, id) {31ReactMount.setID(el, id);32idToNode[id] = el;33};34var oldGetNode = ReactMount.getNode;3536var EventPluginHub;37var ReactBrowserEventEmitter;38var ReactTestUtils;39var TapEventPlugin;40var EventListener;4142var tapMoveThreshold;43var idCallOrder = [];44var recordID = function(id) {45idCallOrder.push(id);46};47var recordIDAndStopPropagation = function(id, event) {48recordID(id);49event.stopPropagation();50};51var recordIDAndReturnFalse = function(id, event) {52recordID(id);53return false;54};55var LISTENER = mocks.getMockFunction();56var ON_CLICK_KEY = keyOf({onClick: null});57var ON_TOUCH_TAP_KEY = keyOf({onTouchTap: null});58var ON_CHANGE_KEY = keyOf({onChange: null});596061/**62* Since `ReactBrowserEventEmitter` is fairly well separated from the DOM, we63* can test almost all of `ReactBrowserEventEmitter` without ever rendering64* anything in the DOM. As long as we provide IDs that follow `React's`65* conventional id namespace hierarchy. The only reason why we create these DOM66* nodes is so that when we feed them into `ReactBrowserEventEmitter` (through67* `ReactTestUtils`), the event handlers may receive a DOM node to inspect.68*/69var CHILD = document.createElement('div');70var PARENT = document.createElement('div');71var GRANDPARENT = document.createElement('div');72setID(CHILD, '.0.0.0.0');73setID(PARENT, '.0.0.0');74setID(GRANDPARENT, '.0.0');7576function registerSimpleTestHandler() {77ReactBrowserEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, LISTENER);78var listener =79ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);80expect(listener).toEqual(LISTENER);81return ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);82}838485describe('ReactBrowserEventEmitter', function() {86beforeEach(function() {87require('mock-modules').dumpCache();88LISTENER.mockClear();89EventPluginHub = require('EventPluginHub');90TapEventPlugin = require('TapEventPlugin');91ReactMount = require('ReactMount');92EventListener = require('EventListener');93ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');94ReactTestUtils = require('ReactTestUtils');95ReactMount.getNode = function(id) {96return idToNode[id];97};98idCallOrder = [];99tapMoveThreshold = TapEventPlugin.tapMoveThreshold;100EventPluginHub.injection.injectEventPluginsByName({101TapEventPlugin: TapEventPlugin102});103});104105afterEach(function() {106ReactMount.getNode = oldGetNode;107});108109it('should store a listener correctly', function() {110registerSimpleTestHandler();111var listener =112ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);113expect(listener).toBe(LISTENER);114});115116it('should retrieve a listener correctly', function() {117registerSimpleTestHandler();118var listener =119ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);120expect(listener).toEqual(LISTENER);121});122123it('should clear all handlers when asked to', function() {124registerSimpleTestHandler();125ReactBrowserEventEmitter.deleteAllListeners(getID(CHILD));126var listener =127ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);128expect(listener).toBe(undefined);129});130131it('should invoke a simple handler registered on a node', function() {132registerSimpleTestHandler();133ReactTestUtils.Simulate.click(CHILD);134expect(LISTENER.mock.calls.length).toBe(1);135});136137it(138'should not invoke handlers if ReactBrowserEventEmitter is disabled',139function() {140registerSimpleTestHandler();141ReactBrowserEventEmitter.setEnabled(false);142ReactTestUtils.SimulateNative.click(CHILD);143expect(LISTENER.mock.calls.length).toBe(0);144ReactBrowserEventEmitter.setEnabled(true);145ReactTestUtils.SimulateNative.click(CHILD);146expect(LISTENER.mock.calls.length).toBe(1);147}148);149150it('should bubble simply', function() {151ReactBrowserEventEmitter.putListener(152getID(CHILD),153ON_CLICK_KEY,154recordID.bind(null, getID(CHILD))155);156ReactBrowserEventEmitter.putListener(157getID(PARENT),158ON_CLICK_KEY,159recordID.bind(null, getID(PARENT))160);161ReactBrowserEventEmitter.putListener(162getID(GRANDPARENT),163ON_CLICK_KEY,164recordID.bind(null, getID(GRANDPARENT))165);166ReactTestUtils.Simulate.click(CHILD);167expect(idCallOrder.length).toBe(3);168expect(idCallOrder[0]).toBe(getID(CHILD));169expect(idCallOrder[1]).toBe(getID(PARENT));170expect(idCallOrder[2]).toBe(getID(GRANDPARENT));171});172173it('should set currentTarget', function() {174ReactBrowserEventEmitter.putListener(175getID(CHILD),176ON_CLICK_KEY,177function(event) {178recordID(getID(CHILD));179expect(event.currentTarget).toBe(CHILD);180}181);182ReactBrowserEventEmitter.putListener(183getID(PARENT),184ON_CLICK_KEY,185function(event) {186recordID(getID(PARENT));187expect(event.currentTarget).toBe(PARENT);188}189);190ReactBrowserEventEmitter.putListener(191getID(GRANDPARENT),192ON_CLICK_KEY,193function(event) {194recordID(getID(GRANDPARENT));195expect(event.currentTarget).toBe(GRANDPARENT);196}197);198ReactTestUtils.Simulate.click(CHILD);199expect(idCallOrder.length).toBe(3);200expect(idCallOrder[0]).toBe(getID(CHILD));201expect(idCallOrder[1]).toBe(getID(PARENT));202expect(idCallOrder[2]).toBe(getID(GRANDPARENT));203});204205it('should support stopPropagation()', function() {206ReactBrowserEventEmitter.putListener(207getID(CHILD),208ON_CLICK_KEY,209recordID.bind(null, getID(CHILD))210);211ReactBrowserEventEmitter.putListener(212getID(PARENT),213ON_CLICK_KEY,214recordIDAndStopPropagation.bind(null, getID(PARENT))215);216ReactBrowserEventEmitter.putListener(217getID(GRANDPARENT),218ON_CLICK_KEY,219recordID.bind(null, getID(GRANDPARENT))220);221ReactTestUtils.Simulate.click(CHILD);222expect(idCallOrder.length).toBe(2);223expect(idCallOrder[0]).toBe(getID(CHILD));224expect(idCallOrder[1]).toBe(getID(PARENT));225});226227it('should stop after first dispatch if stopPropagation', function() {228ReactBrowserEventEmitter.putListener(229getID(CHILD),230ON_CLICK_KEY,231recordIDAndStopPropagation.bind(null, getID(CHILD))232);233ReactBrowserEventEmitter.putListener(234getID(PARENT),235ON_CLICK_KEY,236recordID.bind(null, getID(PARENT))237);238ReactBrowserEventEmitter.putListener(239getID(GRANDPARENT),240ON_CLICK_KEY,241recordID.bind(null, getID(GRANDPARENT))242);243ReactTestUtils.Simulate.click(CHILD);244expect(idCallOrder.length).toBe(1);245expect(idCallOrder[0]).toBe(getID(CHILD));246});247248it('should stopPropagation if false is returned, but warn', function() {249ReactBrowserEventEmitter.putListener(250getID(CHILD),251ON_CLICK_KEY,252recordIDAndReturnFalse.bind(null, getID(CHILD))253);254ReactBrowserEventEmitter.putListener(255getID(PARENT),256ON_CLICK_KEY,257recordID.bind(null, getID(PARENT))258);259ReactBrowserEventEmitter.putListener(260getID(GRANDPARENT),261ON_CLICK_KEY,262recordID.bind(null, getID(GRANDPARENT))263);264spyOn(console, 'warn');265ReactTestUtils.Simulate.click(CHILD);266expect(idCallOrder.length).toBe(1);267expect(idCallOrder[0]).toBe(getID(CHILD));268expect(console.warn.calls.length).toEqual(1);269expect(console.warn.calls[0].args[0]).toBe(270'Warning: Returning `false` from an event handler is deprecated and ' +271'will be ignored in a future release. Instead, manually call ' +272'e.stopPropagation() or e.preventDefault(), as appropriate.'273);274});275276/**277* The entire event registration state of the world should be "locked-in" at278* the time the event occurs. This is to resolve many edge cases that come279* about from a listener on a lower-in-DOM node causing structural changes at280* places higher in the DOM. If this lower-in-DOM node causes new content to281* be rendered at a place higher-in-DOM, we need to be careful not to invoke282* these new listeners.283*/284285it('should invoke handlers that were removed while bubbling', function() {286var handleParentClick = mocks.getMockFunction();287var handleChildClick = function(event) {288ReactBrowserEventEmitter.deleteAllListeners(getID(PARENT));289};290ReactBrowserEventEmitter.putListener(291getID(CHILD),292ON_CLICK_KEY,293handleChildClick294);295ReactBrowserEventEmitter.putListener(296getID(PARENT),297ON_CLICK_KEY,298handleParentClick299);300ReactTestUtils.Simulate.click(CHILD);301expect(handleParentClick.mock.calls.length).toBe(1);302});303304it('should not invoke newly inserted handlers while bubbling', function() {305var handleParentClick = mocks.getMockFunction();306var handleChildClick = function(event) {307ReactBrowserEventEmitter.putListener(308getID(PARENT),309ON_CLICK_KEY,310handleParentClick311);312};313ReactBrowserEventEmitter.putListener(314getID(CHILD),315ON_CLICK_KEY,316handleChildClick317);318ReactTestUtils.Simulate.click(CHILD);319expect(handleParentClick.mock.calls.length).toBe(0);320});321322it('should infer onTouchTap from a touchStart/End', function() {323ReactBrowserEventEmitter.putListener(324getID(CHILD),325ON_TOUCH_TAP_KEY,326recordID.bind(null, getID(CHILD))327);328ReactTestUtils.SimulateNative.touchStart(329CHILD,330ReactTestUtils.nativeTouchData(0, 0)331);332ReactTestUtils.SimulateNative.touchEnd(333CHILD,334ReactTestUtils.nativeTouchData(0, 0)335);336expect(idCallOrder.length).toBe(1);337expect(idCallOrder[0]).toBe(getID(CHILD));338});339340it('should infer onTouchTap from when dragging below threshold', function() {341ReactBrowserEventEmitter.putListener(342getID(CHILD),343ON_TOUCH_TAP_KEY,344recordID.bind(null, getID(CHILD))345);346ReactTestUtils.SimulateNative.touchStart(347CHILD,348ReactTestUtils.nativeTouchData(0, 0)349);350ReactTestUtils.SimulateNative.touchEnd(351CHILD,352ReactTestUtils.nativeTouchData(0, tapMoveThreshold - 1)353);354expect(idCallOrder.length).toBe(1);355expect(idCallOrder[0]).toBe(getID(CHILD));356});357358it('should not onTouchTap from when dragging beyond threshold', function() {359ReactBrowserEventEmitter.putListener(360getID(CHILD),361ON_TOUCH_TAP_KEY,362recordID.bind(null, getID(CHILD))363);364ReactTestUtils.SimulateNative.touchStart(365CHILD,366ReactTestUtils.nativeTouchData(0, 0)367);368ReactTestUtils.SimulateNative.touchEnd(369CHILD,370ReactTestUtils.nativeTouchData(0, tapMoveThreshold + 1)371);372expect(idCallOrder.length).toBe(0);373});374375it('should listen to events only once', function() {376spyOn(EventListener, 'listen');377ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);378ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);379expect(EventListener.listen.callCount).toBe(1);380});381382it('should work with event plugins without dependencies', function() {383spyOn(EventListener, 'listen');384385ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);386387expect(EventListener.listen.argsForCall[0][1]).toBe('click');388});389390it('should work with event plugins with dependencies', function() {391spyOn(EventListener, 'listen');392spyOn(EventListener, 'capture');393394ReactBrowserEventEmitter.listenTo(ON_CHANGE_KEY, document);395396var setEventListeners = [];397var listenCalls = EventListener.listen.argsForCall;398var captureCalls = EventListener.capture.argsForCall;399for (var i = 0, l = listenCalls.length; i < l; i++) {400setEventListeners.push(listenCalls[i][1]);401}402for (i = 0, l = captureCalls.length; i < l; i++) {403setEventListeners.push(captureCalls[i][1]);404}405406var module =407ReactBrowserEventEmitter.registrationNameModules[ON_CHANGE_KEY];408var dependencies = module.eventTypes.change.dependencies;409expect(setEventListeners.length).toEqual(dependencies.length);410411for (i = 0, l = setEventListeners.length; i < l; i++) {412expect(dependencies.indexOf(setEventListeners[i])).toBeTruthy();413}414});415416it('should bubble onTouchTap', function() {417ReactBrowserEventEmitter.putListener(418getID(CHILD),419ON_TOUCH_TAP_KEY,420recordID.bind(null, getID(CHILD))421);422ReactBrowserEventEmitter.putListener(423getID(PARENT),424ON_TOUCH_TAP_KEY,425recordID.bind(null, getID(PARENT))426);427ReactBrowserEventEmitter.putListener(428getID(GRANDPARENT),429ON_TOUCH_TAP_KEY,430recordID.bind(null, getID(GRANDPARENT))431);432ReactTestUtils.SimulateNative.touchStart(433CHILD,434ReactTestUtils.nativeTouchData(0, 0)435);436ReactTestUtils.SimulateNative.touchEnd(437CHILD,438ReactTestUtils.nativeTouchData(0, 0)439);440expect(idCallOrder.length).toBe(3);441expect(idCallOrder[0]).toBe(getID(CHILD));442expect(idCallOrder[1]).toBe(getID(PARENT));443expect(idCallOrder[2]).toBe(getID(GRANDPARENT));444});445446});447448449