From 80991e98ca83489ffd71454aadcf71a37f916cde Mon Sep 17 00:00:00 2001 From: Ben Kucera <14625260+Bkucera@users.noreply.github.com> Date: Wed, 23 Oct 2019 11:50:10 -0400 Subject: [PATCH 1/4] fix force typing in disabled input, selection on non-selectionrange inputs --- packages/driver/src/cy/keyboard.ts | 20 ++++++----- packages/driver/src/dom/selection.ts | 8 +++-- .../integration/commands/actions/type_spec.js | 33 +++++++++---------- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/packages/driver/src/cy/keyboard.ts b/packages/driver/src/cy/keyboard.ts index 198cfaea1f89..8fa079074ce7 100644 --- a/packages/driver/src/cy/keyboard.ts +++ b/packages/driver/src/cy/keyboard.ts @@ -280,7 +280,8 @@ const validateTyping = ( keys: KeyDetails[], currentIndex: number, onFail: Function, - skipCheckUntilIndex?: number, + skipCheckUntilIndex: number | undefined, + force: boolean ) => { const chars = joinKeyArrayToString(keys.slice(currentIndex)) const allChars = joinKeyArrayToString(keys) @@ -328,15 +329,17 @@ const validateTyping = ( return {} } - if (!isFocusable) { + if (!isFocusable && isTextLike && !force) { const node = $dom.stringify($el) - if (isTextLike) { - $utils.throwErrByPath('type.not_actionable_textlike', { - onFail, - args: { node }, - }) - } + $utils.throwErrByPath('type.not_actionable_textlike', { + onFail, + args: { node }, + }) + } + + if (!isFocusable && !isTextLike) { + const node = $dom.stringify($el) $utils.throwErrByPath('type.not_on_typeable_element', { onFail, @@ -660,6 +663,7 @@ export class Keyboard { currentKeyIndex, options.onFail, _skipCheckUntilIndex, + options.force ) _skipCheckUntilIndex = skipCheckUntilIndex diff --git a/packages/driver/src/dom/selection.ts b/packages/driver/src/dom/selection.ts index 5f210a3e9679..945dde65a1a1 100644 --- a/packages/driver/src/dom/selection.ts +++ b/packages/driver/src/dom/selection.ts @@ -154,6 +154,10 @@ const setSelectionRange = function (el, start, end) { $elements.callNativeMethod(el, 'setSelectionRange', start, end) } +const isSelectionCollapsed = function (selection: Selection) { + return !selection.toString() +} + const deleteRightOfCursor = function (el) { if ($elements.canSetSelectionRangeElement(el)) { const { start, end } = getSelectionBounds(el) @@ -167,7 +171,7 @@ const deleteRightOfCursor = function (el) { const selection = _getSelectionByEl(el) - if (selection.isCollapsed) { + if (isSelectionCollapsed(selection)) { $elements.callNativeMethod( selection, 'modify', @@ -195,7 +199,7 @@ const deleteLeftOfCursor = function (el) { const selection = _getSelectionByEl(el) - if (selection.isCollapsed) { + if (isSelectionCollapsed(selection)) { $elements.callNativeMethod( selection, 'modify', diff --git a/packages/driver/test/cypress/integration/commands/actions/type_spec.js b/packages/driver/test/cypress/integration/commands/actions/type_spec.js index 0fa74a8cf242..8445495d5754 100644 --- a/packages/driver/test/cypress/integration/commands/actions/type_spec.js +++ b/packages/driver/test/cypress/integration/commands/actions/type_spec.js @@ -199,6 +199,12 @@ describe('src/cy/commands/actions/type', () => { }) }) + it('can force click when element is disabled', function () { + cy.$$('input:text:first').prop('disabled', true) + cy.get('input:text:first').type('foo', { force: true }) + .should('have.value', 'foo') + }) + it('can forcibly click even when being covered by another element', () => { const $input = $('') .attr('id', 'input-covered-in-span') @@ -1810,6 +1816,13 @@ describe('src/cy/commands/actions/type', () => { }) }) + it('can delete all with {selectall}{backspace} in non-selectionrange element', () => { + cy.get('input[type=email]:first') + .should(($el) => $el.val('sdfsdf')) + .type('{selectall}{backspace}') + .should('have.value', '') + }) + it('can backspace a selection range of characters', () => { // select the 'ar' characters cy @@ -4415,22 +4428,6 @@ describe('src/cy/commands/actions/type', () => { cy.get('input:text:first').type('foo') }) - it('throws when subject is disabled and force:true', function (done) { - cy.timeout(200) - - cy.$$('input:text:first').prop('disabled', true) - - cy.on('fail', (err) => { - // get + type logs - expect(this.logs.length).eq(2) - expect(err.message).to.include('cy.type() failed because it targeted a disabled element.') - - done() - }) - - cy.get('input:text:first').type('foo', { force: true }) - }) - it('throws when submitting within nested forms') it('logs once when not dom subject', function (done) { @@ -4532,11 +4529,11 @@ https://on.cypress.io/type`) }) }) - describe('naughtly strings', () => { + describe('naughty strings', () => { _.each(['Ω≈ç√∫˜µ≤≥÷', '2.2250738585072011e-308', '田中さんにあげて下さい', '', '⁰⁴⁵₀₁₂', '🐵 🙈 🙉 🙊', '', '$USER'], (val) => { - it(`allows typing some naughtly strings (${val})`, () => { + it(`allows typing some naughty strings (${val})`, () => { cy .get(':text:first').type(val) .should('have.value', val) From c2f2a9a400608da66ad2855def552f2a66f336c3 Mon Sep 17 00:00:00 2001 From: Ben Kucera <14625260+Bkucera@users.noreply.github.com> Date: Wed, 23 Oct 2019 11:56:43 -0400 Subject: [PATCH 2/4] rename clearer error --- packages/driver/src/cy/keyboard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/driver/src/cy/keyboard.ts b/packages/driver/src/cy/keyboard.ts index 8fa079074ce7..5959cf6394fd 100644 --- a/packages/driver/src/cy/keyboard.ts +++ b/packages/driver/src/cy/keyboard.ts @@ -211,7 +211,7 @@ const getKeyDetails = (onKeyNotFound) => { onKeyNotFound(key, _.keys(getKeymap()).join(', ')) - throw Error(`Not a valid key: ${key}`) + throw new Error('this can never happen') } } From c863af2bd27ce56bad54ad2bc3b7ca80a4109ee8 Mon Sep 17 00:00:00 2001 From: Ben Kucera <14625260+Bkucera@users.noreply.github.com> Date: Wed, 23 Oct 2019 12:13:44 -0400 Subject: [PATCH 3/4] add comments --- packages/driver/src/cy/keyboard.ts | 3 +++ packages/driver/src/dom/selection.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/packages/driver/src/cy/keyboard.ts b/packages/driver/src/cy/keyboard.ts index 5959cf6394fd..588204d0c785 100644 --- a/packages/driver/src/cy/keyboard.ts +++ b/packages/driver/src/cy/keyboard.ts @@ -329,6 +329,8 @@ const validateTyping = ( return {} } + // throw error if element, which is normally typeable, is disabled for some reason + // don't throw if force: true if (!isFocusable && isTextLike && !force) { const node = $dom.stringify($el) @@ -338,6 +340,7 @@ const validateTyping = ( }) } + // throw error if element cannot receive keyboard events under any conditions if (!isFocusable && !isTextLike) { const node = $dom.stringify($el) diff --git a/packages/driver/src/dom/selection.ts b/packages/driver/src/dom/selection.ts index 945dde65a1a1..bf70d7522d6c 100644 --- a/packages/driver/src/dom/selection.ts +++ b/packages/driver/src/dom/selection.ts @@ -154,6 +154,9 @@ const setSelectionRange = function (el, start, end) { $elements.callNativeMethod(el, 'setSelectionRange', start, end) } +// Whether or not the selection contains any text +// since Selection.isCollapsed will be true when selection +// is inside non-selectionRange input (e.g. input[type=email]) const isSelectionCollapsed = function (selection: Selection) { return !selection.toString() } From d0f6dd0a168f6fbe7fde065f4bba42cf812c5f0f Mon Sep 17 00:00:00 2001 From: Ben Kucera <14625260+Bkucera@users.noreply.github.com> Date: Wed, 23 Oct 2019 12:37:47 -0400 Subject: [PATCH 4/4] fix test names --- .../test/cypress/integration/commands/actions/type_spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/driver/test/cypress/integration/commands/actions/type_spec.js b/packages/driver/test/cypress/integration/commands/actions/type_spec.js index 8445495d5754..0b518ecd7a05 100644 --- a/packages/driver/test/cypress/integration/commands/actions/type_spec.js +++ b/packages/driver/test/cypress/integration/commands/actions/type_spec.js @@ -183,7 +183,7 @@ describe('src/cy/commands/actions/type', () => { }) describe('actionability', () => { - it('can forcibly click even when element is invisible', () => { + it('can forcibly type + click even when element is invisible', () => { const $txt = cy.$$(':text:first').hide() expect($txt).not.to.have.value('foo') @@ -199,13 +199,13 @@ describe('src/cy/commands/actions/type', () => { }) }) - it('can force click when element is disabled', function () { + it('can force type when element is disabled', function () { cy.$$('input:text:first').prop('disabled', true) cy.get('input:text:first').type('foo', { force: true }) .should('have.value', 'foo') }) - it('can forcibly click even when being covered by another element', () => { + it('can forcibly type + click even when being covered by another element', () => { const $input = $('') .attr('id', 'input-covered-in-span') .css({