From 2122320881ce2dd906a05a994e4cca86e5786129 Mon Sep 17 00:00:00 2001 From: Silviu Alexandru Avram Date: Thu, 14 Mar 2019 21:32:51 -0700 Subject: [PATCH] Add {home} and {end} to cy.type() (#3071) * implemented fix draft * removed an extra comment * fixed two colliding variable names * actual behaviour for home/end is begin/end of line, not paragraph * refactored the selection method to use only selection.modify * added some unit tests for {home} * added more tests for home and end * disclaimer for selection.modify API --- packages/driver/src/cypress/keyboard.coffee | 28 +++++ packages/driver/src/dom/selection.js | 20 ++++ .../commands/actions/type_spec.coffee | 100 ++++++++++++++++++ 3 files changed, 148 insertions(+) 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 = + '
foo
' + + '
bar
' + + '
baz
' + + cy.get("[contenteditable]:first") + .type("{home}11{uparrow}{home}22{uparrow}{home}33").then ($div) -> + expect($div.get(0).innerText).to.eql("33foo\n22bar\n11baz\n") + + context "{end}", -> + it "sets which and keyCode to 35 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 35 + expect(e.keyCode).to.eq 35 + expect(e.key).to.eq "End" + done() + + cy.get("#comments").type("{end}").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("{end}").then -> done() + + it "does not fire input event", (done) -> + cy.$$("#comments").on "input", (e) -> + done("input should not have fired") + + cy.get("#comments").type("{end}").then -> done() + + it "can move the cursor to input end", -> + cy.get(":text:first").invoke("val", "bar").type("{selectall}{leftarrow}{end}n").then ($input) -> + expect($input).to.have.value("barn") + + it "does not move the cursor if already at end of bounds", -> + cy.get(":text:first").invoke("val", "bar").type("{selectall}{rightarrow}{end}n").then ($input) -> + expect($input).to.have.value("barn") + + it "should move the cursor to the end of each line in textarea", -> + cy.$$('textarea:first').get(0).value = 'foo\nbar\nbaz' + + cy.get("textarea:first") + .type("{end}11{uparrow}{end}22{uparrow}{end}33").should('have.value', "foo33\nbar22\nbaz11") + + it "should move cursor to the end of each line in contenteditable", -> + cy.$$('[contenteditable]:first').get(0).innerHTML = + '
foo
' + + '
bar
' + + '
baz
' + + cy.get("[contenteditable]:first") + .type("{end}11{uparrow}{end}22{uparrow}{end}33").then ($div) -> + expect($div.get(0).innerText).to.eql("foo33\nbar22\nbaz11\n") + context "{uparrow}", -> beforeEach -> cy.$$("#comments").val("foo\nbar\nbaz")