Skip to content

Commit

Permalink
try 2: fix type when previous selection in input in some cases (#5854)
Browse files Browse the repository at this point in the history
* fix type when previous selection in input

* cleanup

* cleanup more

* more cleanup

* more more cleanup final

* fix not firing input event in all cases
  • Loading branch information
kuceb authored and jennifer-shehane committed Dec 3, 2019
1 parent 02515fe commit 74006ac
Show file tree
Hide file tree
Showing 5 changed files with 531 additions and 188 deletions.
66 changes: 47 additions & 19 deletions packages/driver/src/cy/keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import * as $dom from '../dom'
import * as $document from '../dom/document'
import * as $elements from '../dom/elements'
import * as $selection from '../dom/selection'
import { HTMLTextLikeElement, HTMLTextLikeInputElement } from '../dom/types'
import $window from '../dom/window'

const debug = Debug('cypress:driver:keyboard')
Expand Down Expand Up @@ -36,7 +35,7 @@ interface KeyDetailsPartial extends Partial<KeyDetails> {
}

type SimulatedDefault = (
el: HTMLTextLikeElement,
el: HTMLElement,
key: KeyDetails,
options: any
) => void
Expand All @@ -63,6 +62,7 @@ const monthRe = /^\d{4}-(0\d|1[0-2])/
const weekRe = /^\d{4}-W(0[1-9]|[1-4]\d|5[0-3])/
const timeRe = /^([0-1]\d|2[0-3]):[0-5]\d(:[0-5]\d)?(\.[0-9]{1,3})?/
const dateTimeRe = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}/
const numberRe = /^-?(0|[1-9]\d*)(\.\d+)?(e-?(0|[1-9]\d*))?$/i
const charsBetweenCurlyBracesRe = /({.+?})/

const INITIAL_MODIFIERS = {
Expand Down Expand Up @@ -235,7 +235,7 @@ const shouldIgnoreEvent = <
return options[eventName] === false
}

const shouldUpdateValue = (el: HTMLElement, key: KeyDetails) => {
const shouldUpdateValue = (el: HTMLElement, key: KeyDetails, options) => {
if (!key.text) return false

const bounds = $selection.getSelectionBounds(el)
Expand All @@ -246,6 +246,29 @@ const shouldUpdateValue = (el: HTMLElement, key: KeyDetails) => {
return false
}

const isNumberInputType = $elements.isInput(el) && $elements.isInputType(el, 'number')

if (isNumberInputType) {
const needsValue = options.prevVal || ''
const needsValueLength = (needsValue && needsValue.length) || 0
const curVal = $elements.getNativeProp(el, 'value')
const bounds = $selection.getSelectionBounds(el)

// We need to see if the number we're about to type is a valid number, since setting a number input
// to an invalid number will not set the value and possibly throw a warning in the console
const potentialValue = $selection.insertSubstring(curVal + needsValue, key.text, [bounds.start + needsValueLength, bounds.end + needsValueLength])

if (!(numberRe.test(potentialValue))) {
debug('skipping inserting value since number input would be invalid', key.text, potentialValue)
options.prevVal = needsValue + key.text

return
}

key.text = (options.prevVal || '') + key.text
options.prevVal = null
}

if (noneSelected) {
const ml = $elements.getNativeProp(el, 'maxLength')

Expand Down Expand Up @@ -308,13 +331,15 @@ const validateTyping = (
let isWeek = false
let isDateTime = false

// use 'type' attribute instead of prop since browsers without
// support for attribute input type will have type prop of 'text'
if ($elements.isInput(el)) {
isDate = $dom.isInputType(el, 'date')
isTime = $dom.isInputType(el, 'time')
isMonth = $dom.isInputType(el, 'month')
isWeek = $dom.isInputType(el, 'week')
isDate = $elements.isAttrType(el, 'date')
isTime = $elements.isAttrType(el, 'time')
isMonth = $elements.isAttrType(el, 'month')
isWeek = $elements.isAttrType(el, 'week')
isDateTime =
$dom.isInputType(el, 'datetime') || $dom.isInputType(el, 'datetime-local')
$elements.isAttrType(el, 'datetime') || $elements.isAttrType(el, 'datetime-local')
}

const isFocusable = $elements.isFocusable($el)
Expand Down Expand Up @@ -452,11 +477,7 @@ function _getEndIndex (str, substr) {
// Simulated default actions for select few keys.
const simulatedDefaultKeyMap: { [key: string]: SimulatedDefault } = {
Enter: (el, key, options) => {
if ($elements.isContentEditable(el) || $elements.isTextarea(el)) {
key.events.input = $selection.replaceSelectionContents(el, '\n')
} else {
key.events.input = false
}
$selection.replaceSelectionContents(el, '\n')

options.onEnterPressed()
},
Expand Down Expand Up @@ -694,7 +715,7 @@ export class Keyboard {
debug('setting element value', valToSet, activeEl)

return $elements.setNativeProp(
activeEl as HTMLTextLikeInputElement,
activeEl as $elements.HTMLTextLikeInputElement,
'value',
valToSet
)
Expand Down Expand Up @@ -954,9 +975,11 @@ export class Keyboard {
const key = this.getModifierKeyDetails(_key)

if (!key.text) {
key.events.input = false
key.events.keypress = false
key.events.textInput = false
if (key.key !== 'Backspace' && key.key !== 'Delete') {
key.events.input = false
}
}

let elToType
Expand All @@ -976,9 +999,12 @@ export class Keyboard {

if (key.key === 'Enter' && $elements.isInput(elToType)) {
key.events.textInput = false
key.events.input = false
}

if ($elements.isReadOnlyInputOrTextarea(elToType)) {
if ($elements.isContentEditable(elToType)) {
key.events.input = false
} else if ($elements.isReadOnlyInputOrTextarea(elToType)) {
key.events.textInput = false
}

Expand Down Expand Up @@ -1035,7 +1061,7 @@ export class Keyboard {
this.fireSimulatedEvent(el, 'keyup', key, options)
}

getSimulatedDefaultForKey (key: KeyDetails) {
getSimulatedDefaultForKey (key: KeyDetails, options) {
debug('getSimulatedDefaultForKey', key.key)
if (key.simulatedDefault) return key.simulatedDefault

Expand All @@ -1044,7 +1070,7 @@ export class Keyboard {
}

return (el: HTMLElement) => {
if (!shouldUpdateValue(el, key)) {
if (!shouldUpdateValue(el, key, options)) {
debug('skip typing key', false)
key.events.input = false

Expand Down Expand Up @@ -1072,7 +1098,7 @@ export class Keyboard {

performSimulatedDefault (el: HTMLElement, key: KeyDetails, options: any) {
debug('performSimulatedDefault', key.key)
const simulatedDefault = this.getSimulatedDefaultForKey(key)
const simulatedDefault = this.getSimulatedDefaultForKey(key, options)

if ($elements.isTextLike(el)) {
if ($elements.isInput(el) || $elements.isTextarea(el)) {
Expand All @@ -1087,6 +1113,8 @@ export class Keyboard {
simulatedDefault(el, key, options)
}

debug({ key })

shouldIgnoreEvent('input', key.events) ||
this.fireSimulatedEvent(el, 'input', key, options)

Expand Down
3 changes: 3 additions & 0 deletions packages/driver/src/cypress/cy.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ create = (specWindow, Cypress, Cookies, state, config, log) ->
contentWindow.SVGElement.prototype.blur = ->
focused.interceptBlur(@)

contentWindow.HTMLInputElement.prototype.select = ->
$selection.interceptSelect.call(@)

contentWindow.document.hasFocus = ->
focused.documentHasFocus.call(@)

Expand Down
7 changes: 7 additions & 0 deletions packages/driver/src/dom/elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,12 @@ const isInputType = function (el: JQueryOrEl<HTMLElement>, type) {
return elType === type
}

const isAttrType = function (el: HTMLInputElement, type: string) {
const elType = (el.getAttribute('type') || '').toLowerCase()

return elType === type
}

const isScrollOrAuto = (prop) => {
return prop === 'scroll' || prop === 'auto'
}
Expand Down Expand Up @@ -1079,6 +1085,7 @@ export {
isIframe,
isTextarea,
isInputType,
isAttrType,
isFocused,
isFocusedOrInFocused,
isInputAllowingImplicitFormSubmission,
Expand Down
Loading

4 comments on commit 74006ac

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 74006ac Dec 3, 2019

Choose a reason for hiding this comment

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

Circle has built the linux x64 version of the Test Runner.

You can install this pre-release platform-specific build using instructions at https://on.cypress.io/installing-cypress#Install-pre-release-version.

You will need to use custom CYPRESS_INSTALL_BINARY url and install Cypress using an url instead of the version.

export CYPRESS_INSTALL_BINARY=https://cdn.cypress.io/beta/binary/3.7.1/linux-x64/circle-develop-74006acf66597eac84ceff31eb3af90152315fd4-205111/cypress.zip
npm install https://cdn.cypress.io/beta/npm/3.7.1/circle-develop-74006acf66597eac84ceff31eb3af90152315fd4-205101/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 74006ac Dec 3, 2019

Choose a reason for hiding this comment

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

Circle has built the darwin x64 version of the Test Runner.

You can install this pre-release platform-specific build using instructions at https://on.cypress.io/installing-cypress#Install-pre-release-version.

You will need to use custom CYPRESS_INSTALL_BINARY url and install Cypress using an url instead of the version.

export CYPRESS_INSTALL_BINARY=https://cdn.cypress.io/beta/binary/3.7.1/darwin-x64/circle-develop-74006acf66597eac84ceff31eb3af90152315fd4-205269/cypress.zip
npm install https://cdn.cypress.io/beta/npm/3.7.1/circle-develop-74006acf66597eac84ceff31eb3af90152315fd4-205114/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 74006ac Dec 3, 2019

Choose a reason for hiding this comment

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

AppVeyor has built the win32 x64 version of the Test Runner.

You can install this pre-release platform-specific build using instructions at https://on.cypress.io/installing-cypress#Install-pre-release-version.

You will need to use custom CYPRESS_INSTALL_BINARY url and install Cypress using an url instead of the version.

set CYPRESS_INSTALL_BINARY=https://cdn.cypress.io/beta/binary/3.7.1/win32-x64/appveyor-develop-74006acf66597eac84ceff31eb3af90152315fd4-29281303/cypress.zip
npm install https://cdn.cypress.io/beta/binary/3.7.1/win32-x64/appveyor-develop-74006acf66597eac84ceff31eb3af90152315fd4-29281303/cypress.zip

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 74006ac Dec 3, 2019

Choose a reason for hiding this comment

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

AppVeyor has built the win32 ia32 version of the Test Runner.

You can install this pre-release platform-specific build using instructions at https://on.cypress.io/installing-cypress#Install-pre-release-version.

You will need to use custom CYPRESS_INSTALL_BINARY url and install Cypress using an url instead of the version.

set CYPRESS_INSTALL_BINARY=https://cdn.cypress.io/beta/binary/3.7.1/win32-ia32/appveyor-develop-74006acf66597eac84ceff31eb3af90152315fd4-29281303/cypress.zip
npm install https://cdn.cypress.io/beta/binary/3.7.1/win32-ia32/appveyor-develop-74006acf66597eac84ceff31eb3af90152315fd4-29281303/cypress.zip

Please sign in to comment.