From 5b6f7c3108ac09302743d92146fc40f0f5a2e376 Mon Sep 17 00:00:00 2001 From: Zach Bloomquist Date: Mon, 26 Aug 2019 12:42:13 -0400 Subject: [PATCH 1/6] allow passing qs param to cy.visit --- packages/driver/src/cy/commands/navigation.coffee | 4 ++-- .../integration/commands/navigation_spec.coffee | 12 ++++++++++-- packages/driver/test/support/server.coffee | 3 +++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/driver/src/cy/commands/navigation.coffee b/packages/driver/src/cy/commands/navigation.coffee index b4b22ca25ac..9886bd4f3bf 100644 --- a/packages/driver/src/cy/commands/navigation.coffee +++ b/packages/driver/src/cy/commands/navigation.coffee @@ -14,7 +14,7 @@ hasVisitedAboutBlank = null currentlyVisitingAboutBlank = null knownCommandCausedInstability = null -REQUEST_URL_OPTS = "auth failOnStatusCode retryOnNetworkFailure retryOnStatusCodeFailure method body headers" +REQUEST_URL_OPTS = "auth failOnStatusCode retryOnNetworkFailure retryOnStatusCodeFailure method body headers qs" .split(" ") VISIT_OPTS = "url log onBeforeLoad onLoad timeout requestTimeout" @@ -610,7 +610,7 @@ module.exports = (Commands, Cypress, cy, state, config) -> existing = $utils.locExisting() ## TODO: $Location.resolve(existing.origin, url) - + if $Location.isLocalFileUrl(url) return specifyFileByRelativePath(url, options._log) diff --git a/packages/driver/test/cypress/integration/commands/navigation_spec.coffee b/packages/driver/test/cypress/integration/commands/navigation_spec.coffee index 66790037148..201ca997d1d 100644 --- a/packages/driver/test/cypress/integration/commands/navigation_spec.coffee +++ b/packages/driver/test/cypress/integration/commands/navigation_spec.coffee @@ -543,11 +543,11 @@ describe "src/cy/commands/navigation", -> it "does not support file:// protocol", (done) -> Cypress.config("baseUrl", "") - + cy.on "fail", (err) -> expect(err.message).to.contain("cy.visit() failed because the 'file://...' protocol is not supported by Cypress.") done() - + cy.visit("file:///cypress/fixtures/generic.html") ## https://github.com/cypress-io/cypress/issues/1727 @@ -619,6 +619,14 @@ describe "src/cy/commands/navigation", -> }) cy.contains('"user-agent":"something special"') + it "can send querystring", -> + qs = { "foo bar": "baz quux" } + + cy + .visit("http://localhost:3500/dump-qs", { qs }) + .then -> + cy.contains(JSON.stringify(qs)) + describe "can send a POST request", -> it "automatically urlencoded using an object body", -> cy.visit("http://localhost:3500/post-only", { diff --git a/packages/driver/test/support/server.coffee b/packages/driver/test/support/server.coffee index e2641857c49..2ba2721df1a 100644 --- a/packages/driver/test/support/server.coffee +++ b/packages/driver/test/support/server.coffee @@ -78,6 +78,9 @@ niv.install("react-dom@15.6.1") app.all '/dump-method', (req, res) -> res.send("request method: #{req.method}") + app.all '/dump-qs', (req, res) -> + res.send("it worked!
request querystring:
#{JSON.stringify(req.query)}") + app.post '/post-only', (req, res) -> res.send("it worked!
request body:
#{JSON.stringify(req.body)}") From b99561243a1e73f899f8182b10211219bd76bf72 Mon Sep 17 00:00:00 2001 From: Zach Bloomquist Date: Mon, 26 Aug 2019 12:46:35 -0400 Subject: [PATCH 2/6] add types --- cli/types/index.d.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cli/types/index.d.ts b/cli/types/index.d.ts index 540db9629ac..47459fa94c2 100644 --- a/cli/types/index.d.ts +++ b/cli/types/index.d.ts @@ -2349,6 +2349,11 @@ declare namespace Cypress { * }) */ auth: Auth + + /** + * Query parameters to append to the `url` of the request. + */ + qs: object } /** From 00795903b2d3fd84abcd68a98d435fab04ef7d65 Mon Sep 17 00:00:00 2001 From: Zach Bloomquist Date: Mon, 26 Aug 2019 15:34:08 -0400 Subject: [PATCH 3/6] qs -> params, merge with url, throw errors on invalid type, add tests --- .../driver/src/cy/commands/navigation.coffee | 10 ++++++- .../driver/src/cypress/error_messages.coffee | 3 ++- packages/driver/src/cypress/location.coffee | 5 ++++ .../commands/navigation_spec.coffee | 26 ++++++++++++++++--- .../integration/cypress/location_spec.coffee | 15 +++++++++++ 5 files changed, 53 insertions(+), 6 deletions(-) diff --git a/packages/driver/src/cy/commands/navigation.coffee b/packages/driver/src/cy/commands/navigation.coffee index 9886bd4f3bf..8e6fa8ed4e3 100644 --- a/packages/driver/src/cy/commands/navigation.coffee +++ b/packages/driver/src/cy/commands/navigation.coffee @@ -14,7 +14,7 @@ hasVisitedAboutBlank = null currentlyVisitingAboutBlank = null knownCommandCausedInstability = null -REQUEST_URL_OPTS = "auth failOnStatusCode retryOnNetworkFailure retryOnStatusCodeFailure method body headers qs" +REQUEST_URL_OPTS = "auth failOnStatusCode retryOnNetworkFailure retryOnStatusCodeFailure method body headers" .split(" ") VISIT_OPTS = "url log onBeforeLoad onLoad timeout requestTimeout" @@ -47,6 +47,8 @@ timedOutWaitingForPageLoad = (ms, log) -> args: { ms } }) +mergeUrlWithParams = (url, params) -> + bothUrlsMatchAndRemoteHasHash = (current, remote) -> ## the remote has a hash ## or the last char of href @@ -522,6 +524,9 @@ module.exports = (Commands, Cypress, cy, state, config) -> onLoad: -> }) + if !_.isUndefined(options.params) and not _.isObject(options.params) + $utils.throwErrByPath("visit.invalid_params", { args: { params: String(options.params) }}) + if options.retryOnStatusCodeFailure and not options.failOnStatusCode $utils.throwErrByPath("visit.status_code_flags_invalid") @@ -550,6 +555,9 @@ module.exports = (Commands, Cypress, cy, state, config) -> if baseUrl = config("baseUrl") url = $Location.qualifyWithBaseUrl(baseUrl, url) + if params = options.params + url = $Location.mergeUrlWithParams(url, params) + cleanup = null ## clear the current timeout diff --git a/packages/driver/src/cypress/error_messages.coffee b/packages/driver/src/cypress/error_messages.coffee index 5288c1de442..d262babe185 100644 --- a/packages/driver/src/cypress/error_messages.coffee +++ b/packages/driver/src/cypress/error_messages.coffee @@ -998,6 +998,7 @@ module.exports = { invalid_1st_arg: "#{cmd('visit')} must be called with a URL or an options object containing a URL as its 1st argument" invalid_method: "#{cmd('visit')} was called with an invalid method: '{{method}}'. Method can only be GET or POST." invalid_headers: "#{cmd('visit')} requires the 'headers' option to be an object." + invalid_params: "#{cmd('visit')} requires the 'params' option to be an object, but received: '{{params}}'" no_duplicate_url: """ #{cmd('visit')} must be called with only one URL. You specified two URLs: @@ -1090,7 +1091,7 @@ module.exports = { #{cmd('request')} will automatically get and set cookies and enable you to parse responses. """ - + specify_file_by_relative_path: """ #{cmd('visit')} failed because the 'file://...' protocol is not supported by Cypress. diff --git a/packages/driver/src/cypress/location.coffee b/packages/driver/src/cypress/location.coffee index 8fa727511d9..78965e2dc38 100644 --- a/packages/driver/src/cypress/location.coffee +++ b/packages/driver/src/cypress/location.coffee @@ -155,6 +155,11 @@ class $Location url = new UrlParse(url, existing.origin) url.toString() + @mergeUrlWithParams = (url, params) -> + url = new UrlParse(url, null, true) + url.set("query", _.merge(url.query || {}, params)) + url.toString() + @normalize = (url) -> ## A properly formed URL will always have a trailing ## slash at the end of it diff --git a/packages/driver/test/cypress/integration/commands/navigation_spec.coffee b/packages/driver/test/cypress/integration/commands/navigation_spec.coffee index 201ca997d1d..d751343d518 100644 --- a/packages/driver/test/cypress/integration/commands/navigation_spec.coffee +++ b/packages/driver/test/cypress/integration/commands/navigation_spec.coffee @@ -619,13 +619,14 @@ describe "src/cy/commands/navigation", -> }) cy.contains('"user-agent":"something special"') - it "can send querystring", -> - qs = { "foo bar": "baz quux" } + it "can send querystring params", -> + params = { "foo bar": "baz quux" } cy - .visit("http://localhost:3500/dump-qs", { qs }) + .visit("http://localhost:3500/dump-qs", { params }) .then -> - cy.contains(JSON.stringify(qs)) + cy.contains(JSON.stringify(params)) + cy.url().should('eq', 'http://localhost:3500/dump-qs?foo%20bar=baz%20quux') describe "can send a POST request", -> it "automatically urlencoded using an object body", -> @@ -1068,6 +1069,23 @@ describe "src/cy/commands/navigation", -> headers: "quux" }) + [ + "foo", + null, + false, + ].forEach (params) => + str = String(params) + + it "throws when params is #{str}", (done) -> + cy.on "fail", (err) -> + expect(err.message).to.contain "cy.visit() requires the 'params' option to be an object, but received: '#{str}'" + done() + + cy.visit({ + url: "http://foobarbaz", + params + }) + it "throws when failOnStatusCode is false and retryOnStatusCodeFailure is true", (done) -> cy.on "fail", (err) -> expect(err.message).to.contain "cy.visit() was invoked with { failOnStatusCode: false, retryOnStatusCodeFailure: true }." diff --git a/packages/driver/test/cypress/integration/cypress/location_spec.coffee b/packages/driver/test/cypress/integration/cypress/location_spec.coffee index 016701d5d5e..78c53fd8810 100644 --- a/packages/driver/test/cypress/integration/cypress/location_spec.coffee +++ b/packages/driver/test/cypress/integration/cypress/location_spec.coffee @@ -183,6 +183,21 @@ describe "src/cypress/location", -> obj = Location.create(urls.signin) expect(obj.toString()).to.eq("http://localhost:2020/signin") + context ".mergeWithParams", -> + beforeEach -> + @url = (str, expected, params) -> + url = Location.mergeWithParams(str, params) + expect(url).to.eq(expected) + + it "merges params into a URL", -> + @url "http://example.com/a", "http://example.com/a?foo=bar", { foo: 'bar' } + + it "overrides existing queryparams", -> + @url "http://example.com/a?foo=quux", "http://example.com/a?foo=bar", { foo: 'bar' } + + it "appends and overrides existing queryparams", -> + @url "http://example.com/a?foo=quux", "http://example.com/a?foo=bar&baz=quuz", { foo: 'bar', baz: 'quuz' } + context ".normalize", -> beforeEach -> @url = (source, expected) -> From 9939327f6b8416eec6a728a8cc882c73c9c64eac Mon Sep 17 00:00:00 2001 From: Zach Bloomquist Date: Mon, 26 Aug 2019 15:42:07 -0400 Subject: [PATCH 4/6] params -> qs --- .../driver/src/cy/commands/navigation.coffee | 8 ++++---- .../driver/src/cypress/error_messages.coffee | 2 +- .../integration/commands/navigation_spec.coffee | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/driver/src/cy/commands/navigation.coffee b/packages/driver/src/cy/commands/navigation.coffee index 8e6fa8ed4e3..8aab65219b3 100644 --- a/packages/driver/src/cy/commands/navigation.coffee +++ b/packages/driver/src/cy/commands/navigation.coffee @@ -524,8 +524,8 @@ module.exports = (Commands, Cypress, cy, state, config) -> onLoad: -> }) - if !_.isUndefined(options.params) and not _.isObject(options.params) - $utils.throwErrByPath("visit.invalid_params", { args: { params: String(options.params) }}) + if !_.isUndefined(options.qs) and not _.isObject(options.qs) + $utils.throwErrByPath("visit.invalid_qs", { args: { qs: String(options.qs) }}) if options.retryOnStatusCodeFailure and not options.failOnStatusCode $utils.throwErrByPath("visit.status_code_flags_invalid") @@ -555,8 +555,8 @@ module.exports = (Commands, Cypress, cy, state, config) -> if baseUrl = config("baseUrl") url = $Location.qualifyWithBaseUrl(baseUrl, url) - if params = options.params - url = $Location.mergeUrlWithParams(url, params) + if qs = options.qs + url = $Location.mergeUrlWithqs(url, qs) cleanup = null diff --git a/packages/driver/src/cypress/error_messages.coffee b/packages/driver/src/cypress/error_messages.coffee index d262babe185..58740aed096 100644 --- a/packages/driver/src/cypress/error_messages.coffee +++ b/packages/driver/src/cypress/error_messages.coffee @@ -998,7 +998,7 @@ module.exports = { invalid_1st_arg: "#{cmd('visit')} must be called with a URL or an options object containing a URL as its 1st argument" invalid_method: "#{cmd('visit')} was called with an invalid method: '{{method}}'. Method can only be GET or POST." invalid_headers: "#{cmd('visit')} requires the 'headers' option to be an object." - invalid_params: "#{cmd('visit')} requires the 'params' option to be an object, but received: '{{params}}'" + invalid_qs: "#{cmd('visit')} requires the 'qs' option to be an object, but received: '{{qs}}'" no_duplicate_url: """ #{cmd('visit')} must be called with only one URL. You specified two URLs: diff --git a/packages/driver/test/cypress/integration/commands/navigation_spec.coffee b/packages/driver/test/cypress/integration/commands/navigation_spec.coffee index d751343d518..956d2be9a18 100644 --- a/packages/driver/test/cypress/integration/commands/navigation_spec.coffee +++ b/packages/driver/test/cypress/integration/commands/navigation_spec.coffee @@ -620,12 +620,12 @@ describe "src/cy/commands/navigation", -> cy.contains('"user-agent":"something special"') it "can send querystring params", -> - params = { "foo bar": "baz quux" } + qs = { "foo bar": "baz quux" } cy - .visit("http://localhost:3500/dump-qs", { params }) + .visit("http://localhost:3500/dump-qs", { qs }) .then -> - cy.contains(JSON.stringify(params)) + cy.contains(JSON.stringify(qs)) cy.url().should('eq', 'http://localhost:3500/dump-qs?foo%20bar=baz%20quux') describe "can send a POST request", -> @@ -1073,17 +1073,17 @@ describe "src/cy/commands/navigation", -> "foo", null, false, - ].forEach (params) => - str = String(params) + ].forEach (qs) => + str = String(qs) - it "throws when params is #{str}", (done) -> + it "throws when qs is #{str}", (done) -> cy.on "fail", (err) -> - expect(err.message).to.contain "cy.visit() requires the 'params' option to be an object, but received: '#{str}'" + expect(err.message).to.contain "cy.visit() requires the 'qs' option to be an object, but received: '#{str}'" done() cy.visit({ url: "http://foobarbaz", - params + qs }) it "throws when failOnStatusCode is false and retryOnStatusCodeFailure is true", (done) -> From 4c5b01adc7d637e092639b8887c69f3a49345a54 Mon Sep 17 00:00:00 2001 From: Zach Bloomquist Date: Mon, 26 Aug 2019 15:43:21 -0400 Subject: [PATCH 5/6] cleanup --- packages/driver/src/cy/commands/navigation.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/driver/src/cy/commands/navigation.coffee b/packages/driver/src/cy/commands/navigation.coffee index 8aab65219b3..ed2b8ef7ef3 100644 --- a/packages/driver/src/cy/commands/navigation.coffee +++ b/packages/driver/src/cy/commands/navigation.coffee @@ -47,8 +47,6 @@ timedOutWaitingForPageLoad = (ms, log) -> args: { ms } }) -mergeUrlWithParams = (url, params) -> - bothUrlsMatchAndRemoteHasHash = (current, remote) -> ## the remote has a hash ## or the last char of href From a3d8a2201082d3fa1a725a78f0224f39cfb604bd Mon Sep 17 00:00:00 2001 From: Zach Bloomquist Date: Mon, 26 Aug 2019 15:56:40 -0400 Subject: [PATCH 6/6] fix test --- packages/driver/src/cy/commands/navigation.coffee | 2 +- .../test/cypress/integration/cypress/location_spec.coffee | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/driver/src/cy/commands/navigation.coffee b/packages/driver/src/cy/commands/navigation.coffee index ed2b8ef7ef3..3b0dd037b50 100644 --- a/packages/driver/src/cy/commands/navigation.coffee +++ b/packages/driver/src/cy/commands/navigation.coffee @@ -554,7 +554,7 @@ module.exports = (Commands, Cypress, cy, state, config) -> url = $Location.qualifyWithBaseUrl(baseUrl, url) if qs = options.qs - url = $Location.mergeUrlWithqs(url, qs) + url = $Location.mergeUrlWithParams(url, qs) cleanup = null diff --git a/packages/driver/test/cypress/integration/cypress/location_spec.coffee b/packages/driver/test/cypress/integration/cypress/location_spec.coffee index 78c53fd8810..2034e5883dc 100644 --- a/packages/driver/test/cypress/integration/cypress/location_spec.coffee +++ b/packages/driver/test/cypress/integration/cypress/location_spec.coffee @@ -183,10 +183,10 @@ describe "src/cypress/location", -> obj = Location.create(urls.signin) expect(obj.toString()).to.eq("http://localhost:2020/signin") - context ".mergeWithParams", -> + context ".mergeUrlWithParams", -> beforeEach -> @url = (str, expected, params) -> - url = Location.mergeWithParams(str, params) + url = Location.mergeUrlWithParams(str, params) expect(url).to.eq(expected) it "merges params into a URL", ->