Skip to content

Commit a3265cc

Browse files
flotwigbahmutovbrian-mannjennifer-shehane
committed
Control Chrome cookies through CDP (#5297)
* try connecting to chrome remote interface * linting * print CRI targets for better debugging * linting * load empty tab first when connecting to CRI * first load blank page, then navigate * Page.navigate is working * linting * remove title * add mocha banner * more banners * update some server unit tests * update integration test * document how to run single driver spec file * set the focus back on the page before navigating from blank chrome tab * update server unit test * do not store Chrome remote interface reference for now * record video of the Chrome tab using screencast API * use dynamic port to connect to Chrome remote interface * update unit tests * refactoring * wrap chrome remote interface in our interface, limit access to send * resolved merge * fix reference * passing run unit spec * stub canary search for CI to pass * add build step to packages/server * update chrome spec * do not build js on install for server * updated spec snapshots * update 6_visit_spec snapshot * update snapshot for 6_web_security_spec * update snapshot for 3_plugins_spec * update snapshot for 3_user_agent_spec * update snapshot for 5_stdout_spec * update snapshot for 2_browser_path_spec * do not git ignore js files, add note why * update several snapshots with video on Chrome * update visit performance snapshot * add chrome-remote-interface dependency * cleanup coffeescript conversion to JS, fix some type errors, make parallel override clearer * fix failing tests * Fix snapshot - now we do record in Chrome, so warning message is no longer there. * remove chrome warnings about not recording from snapshot * Remove performance tests from 6_visit_spec snapshot * Remove error from snapshot * Add newline back to cy_visit_performance_spec snapshot * Use CDP to control Chrome cookies + screenshot * Add devtools types * Cleanup * Cleanup * Add guards for minimum CDP version * Fix failing tests * Split cdp_automation_spec out of electron_spec * Move takeScreenshot to cri-client * Navigate to about:blank * look for blank page url * add note about avoiding Prettier * disable prettier a little more * call chrome remote interface close after each spec * return promise when starting screencast * update failing unit tests, add cri client close test * update integration test * Add verbose debug statements to cri-client * Use connect.createRetryingSocket for CDP connection * record video from chrome browsers * add method for validating browser family * update e2e spec snapshot * update 4_request_spec snapshot * update snapshot for spec 1_commands_outside_of_test_spec * update snapshot for 3_plugins_spec * update snapshot for spec 3_user_agent_spec * try: Always log video capturing errors * update snapshot for 2_browser_path_spec * update snapshot for 2_cookies_spec * better browser family test * update snapshot for 5_stdout_spec * update snapshot for 5_subdomain_spec * Add protocol_spec tests * do not capture video during performance test * Add test for VIDEO_POST_PROCESSING_FAILED warning * Add basic cookie validation in cy.setCookie * Update cdp_automation to throw on Network.setCookie failure code * Update tests 🎉 * Update snapshot * Fix test * Remove redundant logs, cleanup * Add cri-client_spec, fix some small bugs, improve errors * Update dep * use client.on to register screencast callback * use isCookieName * strict-cookie-parser@3.1.0 * cleanup prettier, extract some functions, switch to browser.family * moar cleanup and fixes * add logging to the cri-client so we can see every message sent + received to the cdp protocol * bump bluebird to 3.7.0 for .tapCatch addition * Fix unit tests * WIP: update e2e test to ensure that duration of the video matches what we expect * Test duration of recorded video * Run 6_video_compression in chrome + electron * Cleanup * finish ffmpeg duration verification * Update 8_reporters_spec snapshot * Fix cri-client test * Update CRI close logic to monkey-patch browser.kill * add isBrowserFamily back * make it possible for remote-debugging-port to get overridden * Make CDP timeout 5s; add unit, e2e tests for CDP failure; add user-friendly CDP failure error * Update tests * Use CYPRESS_REMOTE_DEBUGGING_PORT to set CDP port; update CDP error message * Change new Buffer to Buffer.from * Apply name validation on all cookie commands * Just throw on Chrome start if the CDP version is < 1.3 * Fix cypress_spec * Use CDP to set resolution + scale factor in Chrome e2e * Revert "Use CDP to set resolution + scale factor in Chrome e2e" This reverts commit a1b86d9. * use CYPRESS_FORCE_BROWSER_SCALE to force standard resolution * don't do --window-size --kiosk * Use CDP to set resolution + scale factor in Chrome e2e * Revert "use CYPRESS_FORCE_BROWSER_SCALE to force standard resolution" This reverts commit 22c5e78. * Use Page.captureScreenshot for Electron + Chrome, reduce logic * Use before() task to force device metrics in Chrome * Fix protocol_spec * Update 7_record_spec to allow for before() hook * Update 6_task_spec snapshot * Appease eslint * Update hooks in 5_spec_isolation snapshot * some general promisification and cleanup * feedback on pluginsfile * cdp_automation feedback * chrome.coff feedback * feedback * run e2e tests on port 4466, ensure no e2e test ever runs on 5566 to prevent conflicting with debugger port * accept new 'remote:debugger:protocol' automation command to control device metrics overrides * update web security e2e to run on electron + chrome * run web security tests in electorn, disable context isolation * pass disable-site-isolation-trials to Electron so webSecurity works * Fix errors in e2e tests caused by extra log item * fix cri-client unit tests * fancy arrows in log message Co-authored-by: Gleb Bahmutov <gleb.bahmutov@gmail.com> Co-authored-by: Brian Mann <brian.mann86@gmail.com> Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
1 parent fe937d3 commit a3265cc

30 files changed

+787
-332
lines changed

packages/driver/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"parse-domain": "2.0.0",
5656
"setimmediate": "1.0.5",
5757
"sinon": "3.3.0",
58+
"strict-cookie-parser": "3.1.0",
5859
"text-mask-addons": "3.8.0",
5960
"underscore": "1.9.1",
6061
"underscore.string": "3.3.5",

packages/driver/src/cy/commands/cookies.coffee

+31-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
_ = require("lodash")
2+
cookieParser = require("strict-cookie-parser")
23
Promise = require("bluebird")
34

45
$dom = require("../../dom")
@@ -33,6 +34,10 @@ mergeDefaults = (obj) ->
3334
else
3435
merge(obj)
3536

37+
validateCookieName = (cmd, name, onFail) ->
38+
if cookieParser.isCookieName(name) isnt true
39+
Cypress.utils.throwErrByPath("cookies.invalid_name", { args: { cmd, name }, onFail })
40+
3641
module.exports = (Commands, Cypress, cy, state, config) ->
3742
automateCookies = (event, obj = {}, log, timeout) ->
3843
automate = ->
@@ -99,8 +104,12 @@ module.exports = (Commands, Cypress, cy, state, config) ->
99104
obj
100105
})
101106

107+
onFail = options._log
108+
102109
if not _.isString(name)
103-
$utils.throwErrByPath("getCookie.invalid_argument", { onFail: options._log })
110+
$utils.throwErrByPath("getCookie.invalid_argument", { onFail })
111+
112+
validateCookieName('getCookie', name, onFail)
104113

105114
automateCookies("get:cookie", {name: name}, options._log, options.timeout)
106115
.then (resp) ->
@@ -161,14 +170,29 @@ module.exports = (Commands, Cypress, cy, state, config) ->
161170
obj
162171
})
163172

173+
onFail = options._log
174+
164175
if not _.isString(name) or not _.isString(value)
165-
$utils.throwErrByPath("setCookie.invalid_arguments", { onFail: options._log })
176+
Cypress.utils.throwErrByPath("setCookie.invalid_arguments", { onFail })
177+
178+
validateCookieName('setCookie', name, onFail)
179+
180+
if cookieParser.parseCookieValue(value) == null
181+
Cypress.utils.throwErrByPath("setCookie.invalid_value", { args: { value }, onFail })
166182

167183
automateCookies("set:cookie", cookie, options._log, options.timeout)
168184
.then (resp) ->
169185
options.cookie = resp
170186

171187
return resp
188+
.catch (err) ->
189+
Cypress.utils.throwErrByPath("setCookie.backend_error", {
190+
args: {
191+
browserDisplayName: Cypress.browser.displayName,
192+
errStack: err.stack
193+
}
194+
onFail
195+
})
172196

173197
clearCookie: (name, options = {}) ->
174198
_.defaults options, {
@@ -193,8 +217,12 @@ module.exports = (Commands, Cypress, cy, state, config) ->
193217
obj
194218
})
195219

220+
onFail = options._log
221+
196222
if not _.isString(name)
197-
$utils.throwErrByPath("clearCookie.invalid_argument", { onFail: options._log })
223+
$utils.throwErrByPath("clearCookie.invalid_argument", { onFail })
224+
225+
validateCookieName('clearCookie', name, onFail)
198226

199227
## TODO: prevent clearing a cypress namespace
200228
automateCookies("clear:cookie", {name: name}, options._log, options.timeout)

packages/driver/src/cypress/error_messages.coffee

+7
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ module.exports = {
141141
length_option: "#{cmd('contains')} cannot be passed a length option because it will only ever return 1 element."
142142

143143
cookies:
144+
invalid_name: "#{cmd('{{cmd}}')} must be passed an RFC-6265-compliant cookie name. You passed:\n\n`{{name}}`"
144145
removed_method: """
145146
The Cypress.Cookies.{{method}}() method has been removed.
146147
@@ -812,7 +813,13 @@ module.exports = {
812813
unavailable: "The XHR server is unavailable or missing. This should never happen and likely is a bug. Open an issue if you see this message."
813814

814815
setCookie:
816+
backend_error: """
817+
#{cmd('setCookie')} had an unexpected error setting the requested cookie in {{browserDisplayName}}.
818+
819+
{{errStack}}
820+
"""
815821
invalid_arguments: "#{cmd('setCookie')} must be passed two string arguments for name and value."
822+
invalid_value: "#{cmd('setCookie')} must be passed an RFC-6265-compliant cookie value. You passed:\n\n`{{value}}`"
816823

817824
spread:
818825
invalid_type: "#{cmd('spread')} requires the existing subject be array-like."

packages/driver/test/cypress/integration/commands/cookies_spec.coffee

+62-5
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,15 @@ describe "src/cy/commands/cookies", ->
292292

293293
cy.getCookie(123)
294294

295+
it "throws an error if the cookie name is invalid", (done) ->
296+
cy.on "fail", (err) =>
297+
expect(err.message).to.include("cy.getCookie() must be passed an RFC-6265-compliant cookie name.")
298+
expect(err.message).to.include('You passed:\n\n`m=m`')
299+
300+
done()
301+
302+
cy.getCookie("m=m")
303+
295304
describe ".log", ->
296305
beforeEach ->
297306
cy.on "log:added", (attrs, log) =>
@@ -427,17 +436,16 @@ describe "src/cy/commands/cookies", ->
427436
it "logs once on error", (done) ->
428437
error = new Error("some err message")
429438
error.name = "foo"
430-
error.stack = "stack"
431439

432440
Cypress.automation.rejects(error)
433441

434442
cy.on "fail", (err) =>
435443
lastLog = @lastLog
436444

437445
expect(@logs.length).to.eq(1)
438-
expect(lastLog.get("error").message).to.eq "some err message"
439-
expect(lastLog.get("error").name).to.eq "foo"
440-
expect(lastLog.get("error").stack).to.eq error.stack
446+
expect(lastLog.get("error").message).to.include "some err message"
447+
expect(lastLog.get("error").name).to.eq "CypressError"
448+
expect(lastLog.get("error").stack).to.include error.stack
441449
done()
442450

443451
cy.setCookie("foo", "bar")
@@ -453,7 +461,7 @@ describe "src/cy/commands/cookies", ->
453461
expect(lastLog.get("state")).to.eq("failed")
454462
expect(lastLog.get("name")).to.eq("setCookie")
455463
expect(lastLog.get("message")).to.eq("foo, bar")
456-
expect(err.message).to.eq("cy.setCookie() timed out waiting '50ms' to complete.")
464+
expect(err.message).to.include("cy.setCookie() timed out waiting '50ms' to complete.")
457465
done()
458466

459467
cy.setCookie("foo", "bar", {timeout: 50})
@@ -480,6 +488,46 @@ describe "src/cy/commands/cookies", ->
480488

481489
cy.setCookie("foo", 123)
482490

491+
context "when setting an invalid cookie", ->
492+
it "throws an error if the cookie name is invalid", (done) ->
493+
cy.on "fail", (err) =>
494+
expect(err.message).to.include("cy.setCookie() must be passed an RFC-6265-compliant cookie name.")
495+
expect(err.message).to.include('You passed:\n\n`m=m`')
496+
497+
done()
498+
499+
## cookie names may not contain =
500+
## https://stackoverflow.com/a/6109881/3474615
501+
cy.setCookie("m=m", "foo")
502+
503+
it "throws an error if the cookie value is invalid", (done) ->
504+
cy.on "fail", (err) =>
505+
expect(err.message).to.include('must be passed an RFC-6265-compliant cookie value.')
506+
expect(err.message).to.include('You passed:\n\n` bar`')
507+
508+
done()
509+
510+
## cookies may not contain unquoted whitespace
511+
cy.setCookie("foo", " bar")
512+
513+
it "throws an error if the backend responds with an error", (done) ->
514+
cy.on "fail", (err) =>
515+
expect(skipErrStub).to.be.calledOnce
516+
expect(errStub).to.be.calledTwice
517+
expect(err.message).to.contain('unexpected error setting the requested cookie')
518+
done()
519+
520+
errStub = cy.stub(Cypress.utils, "throwErrByPath")
521+
errStub.callThrough()
522+
523+
## stub cookie validation so this invalid cookie can make it to the backend
524+
skipErrStub = errStub
525+
.withArgs("setCookie.invalid_value")
526+
.returns()
527+
528+
## browser backend should yell since this is invalid
529+
cy.setCookie("foo", " bar")
530+
483531
describe ".log", ->
484532
beforeEach ->
485533
cy.on "log:added", (attrs, log) =>
@@ -624,6 +672,15 @@ describe "src/cy/commands/cookies", ->
624672

625673
cy.clearCookie(123)
626674

675+
it "throws an error if the cookie name is invalid", (done) ->
676+
cy.on "fail", (err) =>
677+
expect(err.message).to.include("cy.clearCookie() must be passed an RFC-6265-compliant cookie name.")
678+
expect(err.message).to.include('You passed:\n\n`m=m`')
679+
680+
done()
681+
682+
cy.clearCookie("m=m")
683+
627684
describe ".log", ->
628685
beforeEach ->
629686
cy.on "log:added", (attrs, log) =>

0 commit comments

Comments
 (0)