// Trigger React's synthetic change events on input, textarea and select elements // https://github.com/vitalyq/react-trigger-change /******************IMPORTANT NOTE******************/ /* This file is a modification of the */ /* 'react-trigger-change' library linked above. */ /* That library breaks when 'onFocus' events are */ /* added to components under test because it */ /* dispatches focus events to ensure changes are */ /* triggered in some versions of IE. */ /* This modification removes the accomodations */ /* 'react-trigger-change' makes for IE to ensure */ /* our tests can pass in chrome and firefox. */ /**************************************************/ 'use strict'; // Constants and functions are declared inside the closure. // In this way, reactTriggerChange can be passed directly to executeScript in Selenium. module.exports = function reactTriggerChange(node) { var supportedInputTypes = { color: true, date: true, datetime: true, 'datetime-local': true, email: true, month: true, number: true, password: true, range: true, search: true, tel: true, text: true, time: true, url: true, week: true }; var nodeName = node.nodeName.toLowerCase(); var type = node.type; var event; var descriptor; var initialValue; var initialChecked; var initialCheckedRadio; // Do not try to delete non-configurable properties. // Value and checked properties on DOM elements are non-configurable in PhantomJS. function deletePropertySafe(elem, prop) { var desc = Object.getOwnPropertyDescriptor(elem, prop); if (desc && desc.configurable) { delete elem[prop]; } } function getCheckedRadio(radio) { var name = radio.name; var radios; var i; if (name) { radios = document.querySelectorAll('input[type="radio"][name="' + name + '"]'); for (i = 0; i < radios.length; i += 1) { if (radios[i].checked) { return radios[i] !== radio ? radios[i] : null; } } } return null; } function preventChecking(e) { e.preventDefault(); if (!initialChecked) { e.target.checked = false; } if (initialCheckedRadio) { initialCheckedRadio.checked = true; } } if (nodeName === 'select' || (nodeName === 'input' && type === 'file')) { // IE9-IE11, non-IE // Dispatch change. event = document.createEvent('HTMLEvents'); event.initEvent('change', true, false); node.dispatchEvent(event); } else if ((nodeName === 'input' && supportedInputTypes[type]) || nodeName === 'textarea') { // React 16 // Cache artificial value property descriptor. // Property doesn't exist in React <16, descriptor is undefined. descriptor = Object.getOwnPropertyDescriptor(node, 'value'); // Update inputValueTracking cached value. // Remove artificial value property. // Restore initial value to trigger event with it. initialValue = node.value; node.value = initialValue + '#'; deletePropertySafe(node, 'value'); node.value = initialValue; // React 0.14: IE10-IE11, non-IE // React 15: non-IE // React 16: IE10-IE11, non-IE event = document.createEvent('HTMLEvents'); event.initEvent('input', true, false); node.dispatchEvent(event); // React 16 // Restore artificial value property descriptor. if (descriptor) { Object.defineProperty(node, 'value', descriptor); } } else if (nodeName === 'input' && type === 'checkbox') { // Invert inputValueTracking cached value. node.checked = !node.checked; // Dispatch click. // Click event inverts checked value. event = document.createEvent('MouseEvents'); event.initEvent('click', true, true); node.dispatchEvent(event); } else if (nodeName === 'input' && type === 'radio') { // Cache initial checked value. initialChecked = node.checked; // Find and cache initially checked radio in the group. initialCheckedRadio = getCheckedRadio(node); // React 16 // Cache property descriptor. // Invert inputValueTracking cached value. // Remove artificial checked property. // Restore initial value, otherwise preventDefault will eventually revert the value. descriptor = Object.getOwnPropertyDescriptor(node, 'checked'); node.checked = !initialChecked; deletePropertySafe(node, 'checked'); node.checked = initialChecked; // Prevent toggling during event capturing phase. // Set checked value to false if initialChecked is false, // otherwise next listeners will see true. // Restore initially checked radio in the group. node.addEventListener('click', preventChecking, true); // Dispatch click. // Click event inverts checked value. event = document.createEvent('MouseEvents'); event.initEvent('click', true, true); node.dispatchEvent(event); // Remove listener to stop further change prevention. node.removeEventListener('click', preventChecking, true); // React 16 // Restore artificial checked property descriptor. if (descriptor) { Object.defineProperty(node, 'checked', descriptor); } } };