react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / browser / eventPlugins / __tests__ / ResponderEventPlugin-test.js
81159 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";1213var EventPluginHub;14var EventConstants;15var EventPropagators;16var ReactInstanceHandles;17var ResponderEventPlugin;18var SyntheticEvent;1920var GRANDPARENT_ID = '.0';21var PARENT_ID = '.0.0';22var CHILD_ID = '.0.0.0';2324var topLevelTypes;25var responderEventTypes;26var spies;2728var DUMMY_NATIVE_EVENT = {};29var DUMMY_RENDERED_TARGET = {};3031var onStartShouldSetResponder = function(id, cb, capture) {32var registrationNames = responderEventTypes33.startShouldSetResponder34.phasedRegistrationNames;35EventPluginHub.putListener(36id,37capture ? registrationNames.captured : registrationNames.bubbled,38cb39);40};4142var onScrollShouldSetResponder = function(id, cb, capture) {43var registrationNames = responderEventTypes44.scrollShouldSetResponder45.phasedRegistrationNames;46EventPluginHub.putListener(47id,48capture ? registrationNames.captured : registrationNames.bubbled,49cb50);51};5253var onMoveShouldSetResponder = function(id, cb, capture) {54var registrationNames = responderEventTypes55.moveShouldSetResponder56.phasedRegistrationNames;57EventPluginHub.putListener(58id,59capture ? registrationNames.captured : registrationNames.bubbled,60cb61);62};636465var onResponderGrant = function(id, cb) {66EventPluginHub.putListener(67id,68responderEventTypes.responderGrant.registrationName,69cb70);71};7273var extractForTouchStart = function(renderedTargetID) {74return ResponderEventPlugin.extractEvents(75topLevelTypes.topTouchStart,76DUMMY_NATIVE_EVENT,77renderedTargetID,78DUMMY_RENDERED_TARGET79);80};8182var extractForTouchMove = function(renderedTargetID) {83return ResponderEventPlugin.extractEvents(84topLevelTypes.topTouchMove,85DUMMY_NATIVE_EVENT,86renderedTargetID,87DUMMY_RENDERED_TARGET88);89};9091var extractForTouchEnd = function(renderedTargetID) {92return ResponderEventPlugin.extractEvents(93topLevelTypes.topTouchEnd,94DUMMY_NATIVE_EVENT,95renderedTargetID,96DUMMY_RENDERED_TARGET97);98};99100var extractForMouseDown = function(renderedTargetID) {101return ResponderEventPlugin.extractEvents(102topLevelTypes.topMouseDown,103DUMMY_NATIVE_EVENT,104renderedTargetID,105DUMMY_RENDERED_TARGET106);107};108109var extractForMouseMove = function(renderedTargetID) {110return ResponderEventPlugin.extractEvents(111topLevelTypes.topMouseMove,112DUMMY_NATIVE_EVENT,113renderedTargetID,114DUMMY_RENDERED_TARGET115);116};117118119var extractForMouseUp = function(renderedTargetID) {120return ResponderEventPlugin.extractEvents(121topLevelTypes.topMouseUp,122DUMMY_NATIVE_EVENT,123renderedTargetID,124DUMMY_RENDERED_TARGET125);126};127128var extractForScroll = function(renderedTargetID) {129return ResponderEventPlugin.extractEvents(130topLevelTypes.topScroll,131DUMMY_NATIVE_EVENT,132renderedTargetID,133DUMMY_RENDERED_TARGET134);135};136137138var onGrantChild;139var onGrantParent;140var onGrantGrandParent;141142143var existsInExtraction = function(extracted, test) {144if (Array.isArray(extracted)) {145for (var i = 0; i < extracted.length; i++) {146if (test(extracted[i])) {147return true;148}149}150} else if (extracted) {151return test(extracted);152}153return false;154};155156/**157* Helper validators.158*/159function assertGrantEvent(id, extracted) {160var test = function(event) {161return event instanceof SyntheticEvent &&162event.dispatchConfig === responderEventTypes.responderGrant &&163event.dispatchMarker === id;164};165expect(ResponderEventPlugin.getResponderID()).toBe(id);166expect(existsInExtraction(extracted, test)).toBe(true);167}168169function assertResponderMoveEvent(id, extracted) {170var test = function(event) {171return event instanceof SyntheticEvent &&172event.dispatchConfig === responderEventTypes.responderMove &&173event.dispatchMarker === id;174};175expect(ResponderEventPlugin.getResponderID()).toBe(id);176expect(existsInExtraction(extracted, test)).toBe(true);177}178179function assertTerminateEvent(id, extracted) {180var test = function(event) {181return event instanceof SyntheticEvent &&182event.dispatchConfig === responderEventTypes.responderTerminate &&183event.dispatchMarker === id;184};185expect(ResponderEventPlugin.getResponderID()).not.toBe(id);186expect(existsInExtraction(extracted, test)).toBe(true);187}188189function assertRelease(id, extracted) {190var test = function(event) {191return event instanceof SyntheticEvent &&192event.dispatchConfig === responderEventTypes.responderRelease &&193event.dispatchMarker === id;194};195expect(ResponderEventPlugin.getResponderID()).toBe(null);196expect(existsInExtraction(extracted, test)).toBe(true);197}198199200function assertNothingExtracted(extracted) {201expect(Array.isArray(extracted)).toBe(false); // No grant events.202expect(Array.isArray(extracted)).toBeFalsy();203}204205206/**207* TODO:208* - Test that returning false from `responderTerminationRequest` will never209* cause the responder to be lost.210* - Automate some of this testing by providing config data - generalize.211*/212213describe('ResponderEventPlugin', function() {214beforeEach(function() {215require('mock-modules').dumpCache();216217EventPluginHub = require('EventPluginHub');218EventConstants = require('EventConstants');219EventPropagators = require('EventPropagators');220ReactInstanceHandles = require('ReactInstanceHandles');221ResponderEventPlugin = require('ResponderEventPlugin');222SyntheticEvent = require('SyntheticEvent');223EventPluginHub.injection.injectInstanceHandle(ReactInstanceHandles);224225// dumpCache, in open-source tests, only resets existing mocks. It does not226// reset module-state though -- so we need to do this explicitly in the test227// for now. Once that's no longer the case, we can delete this line.228EventPluginHub.__purge();229230topLevelTypes = EventConstants.topLevelTypes;231responderEventTypes = ResponderEventPlugin.eventTypes;232233spies = {234onStartShouldSetResponderChild: function() {},235onStartShouldSetResponderParent: function() {},236onStartShouldSetResponderParentCapture: function() {},237onStartShouldSetResponderGrandParent: function() {},238onMoveShouldSetResponderParent: function() {},239onScrollShouldSetResponderParent: function() {}240};241242onGrantChild = function() {};243onGrantParent = function() {};244onGrantGrandParent = function() {};245});246247it('should not auto-set responder on touch start', function() {248// Notice we're not registering the startShould* handler.249var extracted = extractForTouchStart(CHILD_ID);250assertNothingExtracted(extracted);251expect(ResponderEventPlugin.getResponderID()).toBe(null);252});253254it('should not auto-set responder on mouse down', function() {255// Notice we're not registering the startShould* handler.256var extracted = extractForMouseDown(CHILD_ID);257assertNothingExtracted(extracted);258expect(ResponderEventPlugin.getResponderID()).toBe(null);259extractForMouseUp(CHILD_ID); // Let up!260expect(ResponderEventPlugin.getResponderID()).toBe(null);261262// Register `onMoveShould*` handler.263spyOn(spies, 'onMoveShouldSetResponderParent').andReturn(true);264onMoveShouldSetResponder(PARENT_ID, spies.onMoveShouldSetResponderParent);265onResponderGrant(PARENT_ID, onGrantParent);266// Move mouse while not pressing down267extracted = extractForMouseMove(CHILD_ID);268assertNothingExtracted(extracted);269// Not going to call `onMoveShould`* if not touching.270expect(spies.onMoveShouldSetResponderParent.callCount).toBe(0);271expect(ResponderEventPlugin.getResponderID()).toBe(null);272273// Now try the move extraction again, this time while holding down, and not274// letting up.275extracted = extractForMouseDown(CHILD_ID);276assertNothingExtracted(extracted);277expect(ResponderEventPlugin.getResponderID()).toBe(null);278279// Now moving can set the responder, if pressing down, even if there is no280// current responder.281extracted = extractForMouseMove(CHILD_ID);282expect(spies.onMoveShouldSetResponderParent.callCount).toBe(1);283expect(ResponderEventPlugin.getResponderID()).toBe(PARENT_ID);284assertGrantEvent(PARENT_ID, extracted);285286extractForMouseUp(CHILD_ID);287expect(ResponderEventPlugin.getResponderID()).toBe(null);288});289290it('should not extract a grant/release event if double start', function() {291// Return true - we should become the responder.292var extracted;293spyOn(spies, 'onStartShouldSetResponderChild').andReturn(true);294onStartShouldSetResponder(CHILD_ID, spies.onStartShouldSetResponderChild);295onResponderGrant(CHILD_ID, onGrantChild);296297extracted = extractForTouchStart(CHILD_ID);298assertGrantEvent(CHILD_ID, extracted);299expect(spies.onStartShouldSetResponderChild.callCount).toBe(1);300301// Now we do *not* clear out the touch via a simulated touch end. This mocks302// out an environment that likely will never happen, but could in some odd303// error state so it's nice to make sure we recover gracefully.304// extractForTouchEnd(CHILD_ID); // Clear the responder305extracted = extractForTouchStart(CHILD_ID);306assertNothingExtracted();307expect(spies.onStartShouldSetResponderChild.callCount).toBe(2);308});309310it('should bubble/capture responder on start', function() {311// Return true - we should become the responder.312var extracted;313spyOn(spies, 'onStartShouldSetResponderParent').andReturn(true);314spyOn(spies, 'onStartShouldSetResponderChild').andReturn(true);315onStartShouldSetResponder(CHILD_ID, spies.onStartShouldSetResponderChild);316onStartShouldSetResponder(PARENT_ID, spies.onStartShouldSetResponderParent);317onResponderGrant(CHILD_ID, onGrantChild);318onResponderGrant(PARENT_ID, onGrantParent);319320// Nothing extracted if no responder.321extracted = extractForTouchMove(GRANDPARENT_ID);322assertNothingExtracted(extracted);323324extracted = extractForTouchStart(CHILD_ID);325assertGrantEvent(CHILD_ID, extracted);326expect(spies.onStartShouldSetResponderChild.callCount).toBe(1);327expect(spies.onStartShouldSetResponderParent.callCount).toBe(0);328329// Even if moving on the grandparent, the child will receive responder moves330// (This is even true for mouse interactions - which we should absolutely331// test)332extracted = extractForTouchMove(GRANDPARENT_ID);333assertResponderMoveEvent(CHILD_ID, extracted);334extracted = extractForTouchMove(CHILD_ID); // Test move on child node too.335assertResponderMoveEvent(CHILD_ID, extracted);336337// Reset the responder - id passed here shouldn't matter:338// TODO: Test varying the id here.339extracted = extractForTouchEnd(GRANDPARENT_ID); // Clear the responder340assertRelease(CHILD_ID, extracted);341342// Now make sure the parent requests responder on capture.343spyOn(spies, 'onStartShouldSetResponderParentCapture').andReturn(true);344onStartShouldSetResponder(345PARENT_ID,346spies.onStartShouldSetResponderParent,347true // Capture348);349onResponderGrant(PARENT_ID, onGrantGrandParent);350extracted = extractForTouchStart(PARENT_ID);351expect(ResponderEventPlugin.getResponderID()).toBe(PARENT_ID);352assertGrantEvent(PARENT_ID, extracted);353// Now move on various nodes, ensuring that the responder move is emitted to354// the parent node.355extracted = extractForTouchMove(GRANDPARENT_ID);356assertResponderMoveEvent(PARENT_ID, extracted);357extracted = extractForTouchMove(CHILD_ID); // Test move on child node too.358assertResponderMoveEvent(PARENT_ID, extracted);359360// Reset the responder - id passed here shouldn't matter:361// TODO: Test varying the id here.362extracted = extractForTouchEnd(GRANDPARENT_ID); // Clear the responder363assertRelease(PARENT_ID, extracted);364365});366367it('should invoke callback to ask if responder is desired', function() {368// Return true - we should become the responder.369spyOn(spies, 'onStartShouldSetResponderChild').andReturn(true);370onStartShouldSetResponder(CHILD_ID, spies.onStartShouldSetResponderChild);371372var extracted = extractForTouchStart(CHILD_ID);373assertNothingExtracted(extracted);374expect(spies.onStartShouldSetResponderChild.callCount).toBe(1);375expect(ResponderEventPlugin.getResponderID()).toBe(CHILD_ID);376extractForTouchEnd(CHILD_ID); // Clear the responder377378// Now try returning false - we should not become the responder.379spies.onStartShouldSetResponderChild.andReturn(false);380onStartShouldSetResponder(CHILD_ID, spies.onStartShouldSetResponderChild);381extracted = extractForTouchStart(CHILD_ID);382assertNothingExtracted(extracted);383expect(spies.onStartShouldSetResponderChild.callCount).toBe(2);384expect(ResponderEventPlugin.getResponderID()).toBe(null);385extractForTouchEnd(CHILD_ID);386expect(ResponderEventPlugin.getResponderID()).toBe(null); // Still null387388// Same thing as before but return true from "shouldSet".389spies.onStartShouldSetResponderChild.andReturn(true);390onStartShouldSetResponder(CHILD_ID, spies.onStartShouldSetResponderChild);391onResponderGrant(CHILD_ID, onGrantChild);392extracted = extractForTouchStart(CHILD_ID);393expect(spies.onStartShouldSetResponderChild.callCount).toBe(3);394assertGrantEvent(CHILD_ID, extracted);395extracted = extractForTouchEnd(CHILD_ID); // Clear the responder396assertRelease(CHILD_ID, extracted);397});398399it('should give up responder to parent on move iff allowed', function() {400// Return true - we should become the responder.401var extracted;402spyOn(spies, 'onStartShouldSetResponderChild').andReturn(true);403spyOn(spies, 'onMoveShouldSetResponderParent').andReturn(true);404onStartShouldSetResponder(CHILD_ID, spies.onStartShouldSetResponderChild);405onMoveShouldSetResponder(PARENT_ID, spies.onMoveShouldSetResponderParent);406onResponderGrant(CHILD_ID, onGrantChild);407onResponderGrant(PARENT_ID, onGrantParent);408409spies.onStartShouldSetResponderChild.andReturn(true);410onStartShouldSetResponder(CHILD_ID, spies.onStartShouldSetResponderChild);411extracted = extractForTouchStart(CHILD_ID);412expect(spies.onStartShouldSetResponderChild.callCount).toBe(1);413expect(spies.onMoveShouldSetResponderParent.callCount).toBe(0); // none yet414assertGrantEvent(CHILD_ID, extracted); // Child is the current responder415416extracted = extractForTouchMove(CHILD_ID);417expect(spies.onMoveShouldSetResponderParent.callCount).toBe(1);418assertGrantEvent(PARENT_ID, extracted);419assertTerminateEvent(CHILD_ID, extracted);420421extracted = extractForTouchEnd(CHILD_ID); // Clear the responder422assertRelease(PARENT_ID, extracted);423});424425it('should responder move only on direct responder', function() {426// Return true - we should become the responder.427spyOn(spies, 'onStartShouldSetResponderChild').andReturn(true);428onStartShouldSetResponder(CHILD_ID, spies.onStartShouldSetResponderChild);429430var extracted = extractForTouchStart(CHILD_ID);431assertNothingExtracted(extracted);432expect(spies.onStartShouldSetResponderChild.callCount).toBe(1);433expect(ResponderEventPlugin.getResponderID()).toBe(CHILD_ID);434extractForTouchEnd(CHILD_ID); // Clear the responder435expect(ResponderEventPlugin.getResponderID()).toBe(null);436437// Now try returning false - we should not become the responder.438spies.onStartShouldSetResponderChild.andReturn(false);439onStartShouldSetResponder(CHILD_ID, spies.onStartShouldSetResponderChild);440extracted = extractForTouchStart(CHILD_ID);441assertNothingExtracted(extracted);442expect(spies.onStartShouldSetResponderChild.callCount).toBe(2);443expect(ResponderEventPlugin.getResponderID()).toBe(null);444extractForTouchEnd(CHILD_ID); // Clear the responder445446// Same thing as before but return true from "shouldSet".447spies.onStartShouldSetResponderChild.andReturn(true);448onStartShouldSetResponder(CHILD_ID, spies.onStartShouldSetResponderChild);449onResponderGrant(CHILD_ID, onGrantChild);450extracted = extractForTouchStart(CHILD_ID);451expect(spies.onStartShouldSetResponderChild.callCount).toBe(3);452assertGrantEvent(CHILD_ID, extracted);453extracted = extractForTouchEnd(CHILD_ID); // Clear the responder454assertRelease(CHILD_ID, extracted);455});456457it('should give up responder to parent on scroll iff allowed', function() {458// Return true - we should become the responder.459var extracted;460spyOn(spies, 'onStartShouldSetResponderChild').andReturn(true);461spyOn(spies, 'onMoveShouldSetResponderParent').andReturn(false);462spyOn(spies, 'onScrollShouldSetResponderParent').andReturn(true);463onStartShouldSetResponder(CHILD_ID, spies.onStartShouldSetResponderChild);464onMoveShouldSetResponder(PARENT_ID, spies.onMoveShouldSetResponderParent);465onScrollShouldSetResponder(466PARENT_ID,467spies.onScrollShouldSetResponderParent468);469onResponderGrant(CHILD_ID, onGrantChild);470onResponderGrant(PARENT_ID, onGrantParent);471472spies.onStartShouldSetResponderChild.andReturn(true);473onStartShouldSetResponder(CHILD_ID, spies.onStartShouldSetResponderChild);474extracted = extractForTouchStart(CHILD_ID);475expect(spies.onStartShouldSetResponderChild.callCount).toBe(1);476expect(spies.onMoveShouldSetResponderParent.callCount).toBe(0); // none yet477assertGrantEvent(CHILD_ID, extracted); // Child is the current responder478479extracted = extractForTouchMove(CHILD_ID);480expect(spies.onMoveShouldSetResponderParent.callCount).toBe(1);481assertNothingExtracted(extracted);482483extracted = extractForScroll(CHILD_ID); // Could have been parent here too.484expect(spies.onScrollShouldSetResponderParent.callCount).toBe(1);485assertGrantEvent(PARENT_ID, extracted);486assertTerminateEvent(CHILD_ID, extracted);487488extracted = extractForTouchEnd(CHILD_ID); // Clear the responder489assertRelease(PARENT_ID, extracted);490});491492493});494495496