From 43718fde9c467b1080bd1bcf0a4c9712201130e5 Mon Sep 17 00:00:00 2001 From: KHeo Date: Mon, 17 Jan 2022 11:26:21 +0900 Subject: [PATCH 1/2] fix --- .../driver/cypress/fixtures/type-enter.html | 75 +++++++++++++++++++ .../integration/commands/actions/type_spec.js | 49 ++++++++++++ .../driver/src/cy/commands/actions/type.ts | 22 +++++- packages/driver/src/cy/keyboard.ts | 2 +- 4 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 packages/driver/cypress/fixtures/type-enter.html diff --git a/packages/driver/cypress/fixtures/type-enter.html b/packages/driver/cypress/fixtures/type-enter.html new file mode 100644 index 000000000000..421e2683d19d --- /dev/null +++ b/packages/driver/cypress/fixtures/type-enter.html @@ -0,0 +1,75 @@ + + + + type('{enter}') + + + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/packages/driver/cypress/integration/commands/actions/type_spec.js b/packages/driver/cypress/integration/commands/actions/type_spec.js index 138dde30501c..d0c10709a0e7 100644 --- a/packages/driver/cypress/integration/commands/actions/type_spec.js +++ b/packages/driver/cypress/integration/commands/actions/type_spec.js @@ -557,6 +557,55 @@ describe('src/cy/commands/actions/type - #type', () => { }) }) + // https://github.com/cypress-io/cypress/issues/19541 + describe(`type('{enter}') and click event on button-like elements`, () => { + beforeEach(() => { + cy.visit('fixtures/type-enter.html') + }) + + describe('triggers', () => { + const targets = [ + 'button-tag', + 'input-button', + 'input-image', + 'input-reset', + 'input-submit', + ] + + targets.forEach((targetId) => { + it(`${targetId}`, () => { + cy.get(`#target-${targetId}`).click() + cy.get(`#target-${targetId}`).type('{enter}') + + cy.get('li').eq(0).should('have.text', 'click') + cy.get('li').eq(1).should('have.text', 'keydown') + cy.get('li').eq(2).should('have.text', 'keypress') + cy.get('li').eq(3).should('have.text', 'click') + cy.get('li').eq(4).should('have.text', 'keyup') + }) + }) + }) + + describe('does not trigger', () => { + const targets = [ + 'input-checkbox', + 'input-radio', + ] + + targets.forEach((targetId) => { + it(`${targetId}`, () => { + cy.get(`#target-${targetId}`).click() + cy.get(`#target-${targetId}`).type('{enter}') + + cy.get('li').eq(0).should('have.text', 'click') + cy.get('li').eq(1).should('have.text', 'keydown') + cy.get('li').eq(2).should('have.text', 'keypress') + cy.get('li').eq(3).should('have.text', 'keyup') + }) + }) + }) + }) + describe('tabindex', () => { beforeEach(function () { this.$div = cy.$$('#tabindex') diff --git a/packages/driver/src/cy/commands/actions/type.ts b/packages/driver/src/cy/commands/actions/type.ts index d6d18c9445de..243c9c93fba3 100644 --- a/packages/driver/src/cy/commands/actions/type.ts +++ b/packages/driver/src/cy/commands/actions/type.ts @@ -272,6 +272,11 @@ export default function (Commands, Cypress, cy, state, config) { const isContentEditable = $elements.isContentEditable(options.$el.get(0)) const isTextarea = $elements.isTextarea(options.$el.get(0)) + // click event is only fired on button, image, submit, reset elements. + // That's why we cannot use $elements.isButtonLike() here. + const type = (type) => $elements.isInputType(options.$el.get(0), type) + const isButtonLike = type('button') || type('image') || type('submit') || type('reset') + return keyboard.type({ $el: options.$el, chars, @@ -347,22 +352,33 @@ export default function (Commands, Cypress, cy, state, config) { }) }, - onEnterPressed (id) { + onEnterPressed (el) { // dont dispatch change events or handle // submit event if we've pressed enter into // a textarea or contenteditable - if (isTextarea || isContentEditable) { return } + // https://github.com/cypress-io/cypress/issues/19541 + // Send click event on type('{enter}') + if (isButtonLike) { + // Firefox sends a click event automatically. + if (!Cypress.isBrowser('firefox')) { + const ctor = $dom.getDocumentFromElement(el).defaultView?.PointerEvent + const event = new ctor('click') + + el.dispatchEvent(event) + } + } + // if our value has changed since our // element was activated we need to // fire a change event immediately const changeEvent = state('changeEvent') if (changeEvent) { - changeEvent(id) + changeEvent() } // handle submit event handler here diff --git a/packages/driver/src/cy/keyboard.ts b/packages/driver/src/cy/keyboard.ts index 5e28c5329f56..f03306b6fa21 100644 --- a/packages/driver/src/cy/keyboard.ts +++ b/packages/driver/src/cy/keyboard.ts @@ -582,7 +582,7 @@ const simulatedDefaultKeyMap: { [key: string]: SimulatedDefault } = { $selection.replaceSelectionContents(el, '\n') } - options.onEnterPressed && options.onEnterPressed() + options.onEnterPressed && options.onEnterPressed(el) }, Delete: (el, key) => { key.events.input = $selection.deleteRightOfCursor(el) From 887185cf98de9c185f78131fb8689cbe087a9a1b Mon Sep 17 00:00:00 2001 From: KHeo Date: Wed, 26 Jan 2022 10:46:37 +0900 Subject: [PATCH 2/2] feedback --- .../integration/commands/actions/type_spec.js | 20 +++++++++---------- .../driver/src/cy/commands/actions/type.ts | 4 ++-- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/driver/cypress/integration/commands/actions/type_spec.js b/packages/driver/cypress/integration/commands/actions/type_spec.js index d0c10709a0e7..28ef1155efe2 100644 --- a/packages/driver/cypress/integration/commands/actions/type_spec.js +++ b/packages/driver/cypress/integration/commands/actions/type_spec.js @@ -574,14 +574,13 @@ describe('src/cy/commands/actions/type - #type', () => { targets.forEach((targetId) => { it(`${targetId}`, () => { - cy.get(`#target-${targetId}`).click() + cy.get(`#target-${targetId}`).focus() cy.get(`#target-${targetId}`).type('{enter}') - cy.get('li').eq(0).should('have.text', 'click') - cy.get('li').eq(1).should('have.text', 'keydown') - cy.get('li').eq(2).should('have.text', 'keypress') - cy.get('li').eq(3).should('have.text', 'click') - cy.get('li').eq(4).should('have.text', 'keyup') + cy.get('li').eq(0).should('have.text', 'keydown') + cy.get('li').eq(1).should('have.text', 'keypress') + cy.get('li').eq(2).should('have.text', 'click') + cy.get('li').eq(3).should('have.text', 'keyup') }) }) }) @@ -594,13 +593,12 @@ describe('src/cy/commands/actions/type - #type', () => { targets.forEach((targetId) => { it(`${targetId}`, () => { - cy.get(`#target-${targetId}`).click() + cy.get(`#target-${targetId}`).focus() cy.get(`#target-${targetId}`).type('{enter}') - cy.get('li').eq(0).should('have.text', 'click') - cy.get('li').eq(1).should('have.text', 'keydown') - cy.get('li').eq(2).should('have.text', 'keypress') - cy.get('li').eq(3).should('have.text', 'keyup') + cy.get('li').eq(0).should('have.text', 'keydown') + cy.get('li').eq(1).should('have.text', 'keypress') + cy.get('li').eq(2).should('have.text', 'keyup') }) }) }) diff --git a/packages/driver/src/cy/commands/actions/type.ts b/packages/driver/src/cy/commands/actions/type.ts index 243c9c93fba3..4e3e8a3a0322 100644 --- a/packages/driver/src/cy/commands/actions/type.ts +++ b/packages/driver/src/cy/commands/actions/type.ts @@ -275,7 +275,7 @@ export default function (Commands, Cypress, cy, state, config) { // click event is only fired on button, image, submit, reset elements. // That's why we cannot use $elements.isButtonLike() here. const type = (type) => $elements.isInputType(options.$el.get(0), type) - const isButtonLike = type('button') || type('image') || type('submit') || type('reset') + const sendClickEvent = type('button') || type('image') || type('submit') || type('reset') return keyboard.type({ $el: options.$el, @@ -362,7 +362,7 @@ export default function (Commands, Cypress, cy, state, config) { // https://github.com/cypress-io/cypress/issues/19541 // Send click event on type('{enter}') - if (isButtonLike) { + if (sendClickEvent) { // Firefox sends a click event automatically. if (!Cypress.isBrowser('firefox')) { const ctor = $dom.getDocumentFromElement(el).defaultView?.PointerEvent