From c62d329bc12bcdfde69be75aef8565ea77892c2e Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Tue, 23 May 2017 10:48:05 -0400 Subject: [PATCH] driver: add support for date inputs --- docs/source/api/commands/type.md | 4 ++ .../src/cy/commands/actions/text.coffee | 29 ++++++++- .../driver/src/cypress/error_messages.coffee | 1 + packages/driver/src/cypress/keyboard.coffee | 5 +- .../test/support/server/fixtures/dom.html | 26 +++++--- .../unit/cy/commands/actions/text_spec.coffee | 60 ++++++++++++++++--- 6 files changed, 103 insertions(+), 22 deletions(-) diff --git a/docs/source/api/commands/type.md b/docs/source/api/commands/type.md index f5d4bdf73d..7e36a7a718 100644 --- a/docs/source/api/commands/type.md +++ b/docs/source/api/commands/type.md @@ -190,6 +190,10 @@ cy.get("body").type("{uparrow}{uparrow}{downarrow}{downarrow}{leftarrow}{rightar cy.get("body").type("{shift}", {release: false}).get("li:first").click() ``` +# Date inputs + +Using `cy.type()` on a date input (``) requires specifying a valid date in the format `yyyy-MM-dd`, e.g. `"1999-12-31"`. This isn't exactly how a user would type into a date input, but is a workaround since date input support varies between browsers and the format varies based on locale. `yyyy-MM-dd` is the format required by [the W3 spec](https://www.w3.org/TR/html5/forms.html#date-state-(type=date)) and is what the input's `value` will be set to regardless of browser or locale. + # Known Issues ## Native `input[type=date,datetime,datetime-local,month,year,color]` diff --git a/packages/driver/src/cy/commands/actions/text.coffee b/packages/driver/src/cy/commands/actions/text.coffee index a21b00c085..a5e8e0efe1 100644 --- a/packages/driver/src/cy/commands/actions/text.coffee +++ b/packages/driver/src/cy/commands/actions/text.coffee @@ -1,6 +1,7 @@ _ = require("lodash") $ = require("jquery") Promise = require("bluebird") +moment = require("moment") { delay, waitForAnimations } = require("./utils") $Log = require("../../../cypress/log") @@ -9,6 +10,7 @@ utils = require("../../../cypress/utils") inputEvents = "textInput input".split(" ") textLike = "textarea,:text,[contenteditable],[type=password],[type=email],[type=number],[type=date],[type=week],[type=month],[type=time],[type=datetime],[type=datetime-local],[type=search],[type=url],[type=tel]" +dateRegex = /\d{4}-\d{2}-\d{2}/ module.exports = (Cypress, Commands) -> Cypress.on "test:before:run", -> @@ -73,6 +75,7 @@ module.exports = (Cypress, Commands) -> isBody = options.$el.is("body") isTextLike = options.$el.is(textLike) + isDate = options.$el.is("[type=date]") hasTabIndex = options.$el.is("[tabindex]") ## TODO: tabindex can't be -1 @@ -102,6 +105,16 @@ module.exports = (Cypress, Commands) -> if _.isBlank(chars) utils.throwErrByPath("type.empty_string", { onFail: options._log }) + if isDate and ( + not _.isString(chars) or + not dateRegex.test(chars) or + not moment(chars).isValid() + ) + utils.throwErrByPath("type.invalid_date", { + onFail: options._log + args: { chars } + }) + options.chars = "" + chars type = => @@ -166,13 +179,27 @@ module.exports = (Cypress, Commands) -> return dispatched + typed = "" + charsToType = if isDate + options.chars.replace("-", "") + else + options.chars + $Keyboard.type({ $el: options.$el - chars: options.chars + chars: charsToType delay: options.delay release: options.release window: @privateState("window") + updateValue: (rng, key) -> + if isDate + typed += key + if typed is charsToType + options.$el.val(options.chars) + else + rng.text(key, "end") + onBeforeType: (totalKeys) => ## for the total number of keys we're about to ## type, ensure we raise the timeout to account diff --git a/packages/driver/src/cypress/error_messages.coffee b/packages/driver/src/cypress/error_messages.coffee index 50933aa800..b254aaa71d 100644 --- a/packages/driver/src/cypress/error_messages.coffee +++ b/packages/driver/src/cypress/error_messages.coffee @@ -588,6 +588,7 @@ module.exports = { type: empty_string: "#{cmd('type')} cannot accept an empty String. You need to actually type something." invalid: "Special character sequence: '{{chars}}' is not recognized. Available sequences are: {{allChars}}" + invalid_date: "Typing into a date input with #{cmd('type')} requires a valid date with the format 'yyyy-MM-dd'. You passed: {{chars}}" multiple_elements: "#{cmd('type')} can only be called on a single textarea or :text. Your subject contained {{num}} elements." not_on_text_field: "#{cmd('type')} can only be called on textarea or :text. Your subject is a: {{node}}" tab: "{tab} isn't a supported character sequence. You'll want to use the command #{cmd('tab')}, which is not ready yet, but when it is done that's what you'll use." diff --git a/packages/driver/src/cypress/keyboard.coffee b/packages/driver/src/cypress/keyboard.coffee index 61f0f652a2..3fd1f586c5 100644 --- a/packages/driver/src/cypress/keyboard.coffee +++ b/packages/driver/src/cypress/keyboard.coffee @@ -475,9 +475,6 @@ $Keyboard = { return dispatched - updateValue: (rng, key) -> - rng.text(key, "end") - typeKey: (el, key, options) -> ## if we have an afterKey value it means ## we've typed in prior to this @@ -489,7 +486,7 @@ $Keyboard = { @moveCaretToEnd(options.rng) @ensureKey el, key, options, -> - @updateValue(options.rng, key) + options.updateValue(options.rng, key) ensureKey: (el, key, options, fn) -> options.id = _.uniqueId("char") diff --git a/packages/driver/test/support/server/fixtures/dom.html b/packages/driver/test/support/server/fixtures/dom.html index b2a62d513f..f416deb980 100644 --- a/packages/driver/test/support/server/fixtures/dom.html +++ b/packages/driver/test/support/server/fixtures/dom.html @@ -125,10 +125,6 @@ el with tabindex - - - -
@@ -257,11 +253,23 @@
- - - - - + + + + + + + + + + + + + + + + +
diff --git a/packages/driver/test/unit/cy/commands/actions/text_spec.coffee b/packages/driver/test/unit/cy/commands/actions/text_spec.coffee index 510befeb4d..c2f8ab69a4 100644 --- a/packages/driver/test/unit/cy/commands/actions/text_spec.coffee +++ b/packages/driver/test/unit/cy/commands/actions/text_spec.coffee @@ -568,6 +568,19 @@ describe "$Cypress.Cy Text Commands", -> @cy.get("#password-without-value").type("agent").then ($input) -> expect($input).to.have.value("agent") + describe "input[type=date]", -> + it "can change values", -> + @cy.get("#date-without-value").type("1959-09-13").then ($text) -> + expect($text).to.have.value("1959-09-13") + + it "overwrites existing value", -> + @cy.get("#date-with-value").type("1959-09-13").then ($text) -> + expect($text).to.have.value("1959-09-13") + + it "overwrites existing value input by invoking val", -> + @cy.get("#date-without-value").invoke("val", "2016-01-01").type("1959-09-13").then ($text) -> + expect($text).to.have.value("1959-09-13") + describe "[contenteditable]", -> it "can change values", -> @cy.get("#input-types [contenteditable]").type("foo").then ($div) -> @@ -577,14 +590,6 @@ describe "$Cypress.Cy Text Commands", -> @cy.get("#input-types [contenteditable]").invoke("text", "foo").type(" bar").then ($text) -> expect($text).to.have.text("foo bar") - # it "can change input[type=date] values", -> - # @cy.get("#input-types [type=date").type("1986-03-14").then ($text) -> - # expect($text).to.have.value("1986-03-14") - - # it "inserts text after existing text on input[type=date]", -> - # @cy.get("#input-types [type=date").invoke("val", "pass").type("word").then ($text) -> - # expect($text).to.have.value("date") - describe "specialChars", -> context "{{}", -> it "sets which and keyCode to 219", (done) -> @@ -2019,6 +2024,45 @@ describe "$Cypress.Cy Text Commands", -> @cy.get(":text:first").type("") + it "throws when type=date and chars is not a string", (done) -> + logs = [] + + @Cypress.on "log", (attrs, log) -> + logs.push(log) + + @cy.on "fail", (err) => + expect(logs.length).to.eq 2 + expect(err.message).to.eq "Typing into a date input with cy.type() requires a valid date with the format 'yyyy-MM-dd'. You passed: 1989" + done() + + @cy.get("#date-without-value").type(1989) + + it "throws when type=date and chars is invalid format", (done) -> + logs = [] + + @Cypress.on "log", (attrs, log) -> + logs.push(log) + + @cy.on "fail", (err) => + expect(logs.length).to.eq 2 + expect(err.message).to.eq "Typing into a date input with cy.type() requires a valid date with the format 'yyyy-MM-dd'. You passed: 01-01-1989" + done() + + @cy.get("#date-without-value").type("01-01-1989") + + it "throws when type=date and chars is invalid date", (done) -> + logs = [] + + @Cypress.on "log", (attrs, log) -> + logs.push(log) + + @cy.on "fail", (err) => + expect(logs.length).to.eq 2 + expect(err.message).to.eq "Typing into a date input with cy.type() requires a valid date with the format 'yyyy-MM-dd'. You passed: 1989-04-31" + done() + + @cy.get("#date-without-value").type("1989-04-31") + _.each [NaN, Infinity, [], {}, null, undefined], (val) => it "throws when trying to type: #{val}", (done) -> logs = []