Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

try 2: fix type when previous selection in input in some cases #5854

Merged
merged 6 commits into from
Dec 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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