diff --git a/packages/driver/src/cypress/keyboard.coffee b/packages/driver/src/cypress/keyboard.coffee index ccf1140942da..0f1e55e486ec 100644 --- a/packages/driver/src/cypress/keyboard.coffee +++ b/packages/driver/src/cypress/keyboard.coffee @@ -20,6 +20,8 @@ keyStandardMap = { "{leftarrow}": "ArrowLeft", "{rightarrow}": "ArrowRight", "{uparrow}": "ArrowUp", + "{home}": "Home", + "{end}": "End", "{alt}": "Alt", "{ctrl}": "Control", "{meta}": "Meta", @@ -211,6 +213,32 @@ $Keyboard = { options.setKey = "{downarrow}" @ensureKey el, null, options, -> $selection.moveCursorDown(el) + + ## charCode = 36 + ## no keyPress + ## no textInput + ## no input + "{home}": (el, options) -> + options.charCode = 36 + options.keypress = false + options.textInput = false + options.input = false + options.setKey = "{home}" + @ensureKey el, null, options, -> + $selection.moveCursorToLineStart(el) + + ## charCode = 35 + ## no keyPress + ## no textInput + ## no input + "{end}": (el, options) -> + options.charCode = 35 + options.keypress = false + options.textInput = false + options.input = false + options.setKey = "{end}" + @ensureKey el, null, options, -> + $selection.moveCursorToLineEnd(el) } modifierChars: { diff --git a/packages/driver/src/dom/selection.js b/packages/driver/src/dom/selection.js index bd1e47b7af2a..c0edef14fb7d 100644 --- a/packages/driver/src/dom/selection.js +++ b/packages/driver/src/dom/selection.js @@ -362,6 +362,24 @@ const _moveCursorUpOrDown = function (el, up) { } } +const moveCursorToLineStart = (el) => { + return _moveCursorToLineStartOrEnd(el, true) +} + +const moveCursorToLineEnd = (el) => { + return _moveCursorToLineStartOrEnd(el, false) +} + +const _moveCursorToLineStartOrEnd = function (el, toStart) { + if ($elements.isContentEditable(el) || $elements.isInput(el) || $elements.isTextarea(el)) { + const selection = _getSelectionByEl(el) + + // the selection.modify API is non-standard, may work differently in other browsers, and is not in IE11. + // https://developer.mozilla.org/en-US/docs/Web/API/Selection/modify + return $elements.callNativeMethod(selection, 'modify', 'move', toStart ? 'backward' : 'forward', 'lineboundary') + } +} + const isCollapsed = function (el) { if ($elements.isTextarea(el) || $elements.isInput(el)) { const { start, end } = getSelectionBounds(el) @@ -579,6 +597,8 @@ module.exports = { moveCursorRight, moveCursorUp, moveCursorDown, + moveCursorToLineStart, + moveCursorToLineEnd, replaceSelectionContents, isCollapsed, interceptSelect, diff --git a/packages/driver/test/cypress/integration/commands/actions/type_spec.coffee b/packages/driver/test/cypress/integration/commands/actions/type_spec.coffee index 573c0aa05c59..ca101ce878a8 100644 --- a/packages/driver/test/cypress/integration/commands/actions/type_spec.coffee +++ b/packages/driver/test/cypress/integration/commands/actions/type_spec.coffee @@ -1403,6 +1403,106 @@ describe "src/cy/commands/actions/type", -> expect($input).to.have.value("fodo") done() + context "{home}", -> + it "sets which and keyCode to 36 and does not fire keypress events", (done) -> + cy.$$("#comments").on "keypress", -> + done("should not have received keypress") + + cy.$$("#comments").on "keydown", (e) -> + expect(e.which).to.eq 36 + expect(e.keyCode).to.eq 36 + expect(e.key).to.eq "Home" + done() + + cy.get("#comments").type("{home}").then ($input) -> + done() + + it "does not fire textInput event", (done) -> + cy.$$("#comments").on "textInput", (e) -> + done("textInput should not have fired") + + cy.get("#comments").type("{home}").then -> done() + + it "does not fire input event", (done) -> + cy.$$("#comments").on "input", (e) -> + done("input should not have fired") + + cy.get("#comments").type("{home}").then -> done() + + it "can move the cursor to input start", -> + cy.get(":text:first").invoke("val", "bar").type("{home}n").then ($input) -> + expect($input).to.have.value("nbar") + + it "does not move the cursor if already at bounds 0", -> + cy.get(":text:first").invoke("val", "bar").type("{selectall}{leftarrow}{home}n").then ($input) -> + expect($input).to.have.value("nbar") + + it "should move the cursor to the start of each line in textarea", -> + cy.$$('textarea:first').get(0).value = 'foo\nbar\nbaz' + + cy.get("textarea:first") + .type("{home}11{uparrow}{home}22{uparrow}{home}33").should('have.value', "33foo\n22bar\n11baz") + + it "should move cursor to the start of each line in contenteditable", -> + cy.$$('[contenteditable]:first').get(0).innerHTML = + '