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..28ef1155efe2 100644 --- a/packages/driver/cypress/integration/commands/actions/type_spec.js +++ b/packages/driver/cypress/integration/commands/actions/type_spec.js @@ -557,6 +557,53 @@ 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}`).focus() + cy.get(`#target-${targetId}`).type('{enter}') + + 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') + }) + }) + }) + + describe('does not trigger', () => { + const targets = [ + 'input-checkbox', + 'input-radio', + ] + + targets.forEach((targetId) => { + it(`${targetId}`, () => { + cy.get(`#target-${targetId}`).focus() + cy.get(`#target-${targetId}`).type('{enter}') + + 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') + }) + }) + }) + }) + 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..4e3e8a3a0322 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 sendClickEvent = 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 (sendClickEvent) { + // 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)