mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-21 22:49:16 -05:00
Add support for sameSite in cookie-related commands (#6828)
* add experimental feature for sameSite * allow experimental descriptions to render markdown * sameSite support mostly working * also strip sameSite from setCookie yielded value * don't use `unspecified` - let browser set default * add tests * decaffeinate: Rename cdp_automation_spec.coffee from .coffee to .js * decaffeinate: Convert cdp_automation_spec.coffee to JS * decaffeinate: Run post-processing cleanups on cdp_automation_spec.coffee * cleanup cdp_automation_spec.ts * update unit tests * update settings_spec to not render as markdown * user-friendly error for insecure SameSite=None * fix styling * fix markdown renderer * update types + schema * use renderInline * update experiment summary * bind renderFn to md
This commit is contained in:
@@ -210,6 +210,11 @@
|
||||
],
|
||||
"default": "bundled",
|
||||
"description": "If set to 'system', Cypress will try to find a Node.js executable on your path to use when executing your plugins. Otherwise, Cypress will use the Node version bundled with Cypress."
|
||||
},
|
||||
"experimentalGetCookiesSameSite": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "If `true`, Cypress will add `sameSite` values to the objects yielded from `cy.setCookie()`, `cy.getCookie()`, and `cy.getCookies()`. This will become the default behavior in Cypress 5.0."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+10
@@ -2450,6 +2450,12 @@ declare namespace Cypress {
|
||||
* @default { runMode: 1, openMode: null }
|
||||
*/
|
||||
firefoxGcInterval: Nullable<number | { runMode: Nullable<number>, openMode: Nullable<number> }>
|
||||
/**
|
||||
* If `true`, Cypress will add `sameSite` values to the objects yielded from `cy.setCookie()`,
|
||||
* `cy.getCookie()`, and `cy.getCookies()`. This will become the default behavior in Cypress 5.0.
|
||||
* @default false
|
||||
*/
|
||||
experimentalGetCookiesSameSite: boolean
|
||||
}
|
||||
|
||||
interface PluginConfigOptions extends ConfigOptions {
|
||||
@@ -2594,12 +2600,15 @@ declare namespace Cypress {
|
||||
onAnyAbort(route: RouteOptions, proxy: any): void
|
||||
}
|
||||
|
||||
type SameSiteStatus = 'no_restriction' | 'strict' | 'lax'
|
||||
|
||||
interface SetCookieOptions extends Loggable, Timeoutable {
|
||||
path: string
|
||||
domain: string
|
||||
secure: boolean
|
||||
httpOnly: boolean
|
||||
expiry: number
|
||||
sameSite: SameSiteStatus
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4701,6 +4710,7 @@ declare namespace Cypress {
|
||||
httpOnly: boolean
|
||||
secure: boolean
|
||||
expiry?: string
|
||||
sameSite?: SameSiteStatus
|
||||
}
|
||||
|
||||
interface EnqueuedCommand {
|
||||
|
||||
@@ -687,9 +687,7 @@ describe('Settings', () => {
|
||||
// do not overwrite the shared object reference -
|
||||
// because it is used by the app's code.
|
||||
this.win.experimental.names.experimentalCoolFeature = 'Cool Feature'
|
||||
this.win.experimental.summaries.experimentalCoolFeature = `
|
||||
Enables super cool feature from Cypress where you can see the cool feature
|
||||
`
|
||||
this.win.experimental.summaries.experimentalCoolFeature = 'Enables super cool feature from Cypress where you can see the cool feature'
|
||||
})
|
||||
|
||||
const hasLearnMoreLink = () => {
|
||||
|
||||
@@ -26,10 +26,17 @@ export default class MarkdownRenderer extends React.PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
let renderFn = md.render
|
||||
|
||||
if (this.props.noParagraphWrapper) {
|
||||
// prevent markdown-it from wrapping the output in a <p> tag
|
||||
renderFn = md.renderInline
|
||||
}
|
||||
|
||||
return (
|
||||
<span ref={(node) => this.node = node}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: md.render(this.props.markdown),
|
||||
__html: renderFn.call(md, this.props.markdown),
|
||||
}}>
|
||||
</span>
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@ import React from 'react'
|
||||
import { observer } from 'mobx-react'
|
||||
import ipc from '../lib/ipc'
|
||||
import { getExperiments } from '@packages/server/lib/experiments'
|
||||
import MarkdownRenderer from '../lib/markdown-renderer'
|
||||
|
||||
const openHelp = (e) => {
|
||||
e.preventDefault()
|
||||
@@ -27,14 +28,14 @@ const Experiments = observer(({ project }) => {
|
||||
_.map(experiments, (experiment, i) => (
|
||||
<li className='experiment' key={i}>
|
||||
<h5>
|
||||
{experiment.name}
|
||||
<MarkdownRenderer markdown={experiment.name} noParagraphWrapper/>
|
||||
<span className={`experiment-status-sign ${experiment.enabled ? 'enabled' : ''}`}>
|
||||
{experiment.enabled ? 'ON' : 'OFF'}
|
||||
</span>
|
||||
</h5>
|
||||
<div className='experiment-desc'>
|
||||
<p className="text-muted">
|
||||
{experiment.summary}
|
||||
<MarkdownRenderer markdown={experiment.summary} noParagraphWrapper/>
|
||||
</p>
|
||||
<div className='experiment-status'>
|
||||
<code>{experiment.key}</code>
|
||||
|
||||
@@ -247,7 +247,7 @@
|
||||
|
||||
.experiment-intro {
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 0px;
|
||||
margin-bottom: 0px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@
|
||||
|
||||
p {
|
||||
width: 80%;
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.experiment-status {
|
||||
@@ -273,7 +273,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.experiment-status-sign {
|
||||
margin-left: 10px;
|
||||
color: #9d9ea9;
|
||||
|
||||
@@ -5,7 +5,7 @@ const $utils = require('../../cypress/utils')
|
||||
const $errUtils = require('../../cypress/error_utils')
|
||||
const $Location = require('../../cypress/location')
|
||||
|
||||
const COOKIE_PROPS = 'name value path secure httpOnly expiry domain'.split(' ')
|
||||
const COOKIE_PROPS = 'name value path secure httpOnly expiry domain sameSite'.split(' ')
|
||||
|
||||
const commandNameRe = /(:)(\w)/
|
||||
|
||||
@@ -33,7 +33,37 @@ const mergeDefaults = function (obj) {
|
||||
return merge(obj)
|
||||
}
|
||||
|
||||
// from https://developer.chrome.com/extensions/cookies#type-SameSiteStatus
|
||||
// note that `unspecified` is purposely omitted - Firefox and Chrome set
|
||||
// different defaults, and Firefox lacks support for `unspecified`, so
|
||||
// `undefined` is used in lieu of `unspecified`
|
||||
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=1624668
|
||||
const VALID_SAMESITE_VALUES = ['no_restriction', 'lax', 'strict']
|
||||
|
||||
const normalizeSameSite = (sameSite) => {
|
||||
if (_.isUndefined(sameSite)) {
|
||||
return sameSite
|
||||
}
|
||||
|
||||
if (_.isString(sameSite)) {
|
||||
sameSite = sameSite.toLowerCase()
|
||||
}
|
||||
|
||||
if (sameSite === 'none') {
|
||||
// "None" is the value sent in the header for `no_restriction`, so allow it here for convenience
|
||||
sameSite = 'no_restriction'
|
||||
}
|
||||
|
||||
return sameSite
|
||||
}
|
||||
|
||||
module.exports = function (Commands, Cypress, cy, state, config) {
|
||||
const maybeStripSameSiteProp = (cookie) => {
|
||||
if (cookie && !Cypress.config('experimentalGetCookiesSameSite')) {
|
||||
delete cookie.sameSite
|
||||
}
|
||||
}
|
||||
|
||||
const automateCookies = function (event, obj = {}, log, timeout) {
|
||||
const automate = () => {
|
||||
return Cypress.automation(event, mergeDefaults(obj))
|
||||
@@ -146,6 +176,8 @@ module.exports = function (Commands, Cypress, cy, state, config) {
|
||||
|
||||
return automateCookies('get:cookie', { name }, options._log, options.timeout)
|
||||
.then((resp) => {
|
||||
maybeStripSameSiteProp(resp)
|
||||
|
||||
options.cookie = resp
|
||||
|
||||
return resp
|
||||
@@ -181,6 +213,10 @@ module.exports = function (Commands, Cypress, cy, state, config) {
|
||||
|
||||
return automateCookies('get:cookies', _.pick(options, 'domain'), options._log, options.timeout)
|
||||
.then((resp) => {
|
||||
if (Array.isArray(resp)) {
|
||||
resp.forEach(maybeStripSameSiteProp)
|
||||
}
|
||||
|
||||
options.cookies = resp
|
||||
|
||||
return resp
|
||||
@@ -225,12 +261,37 @@ module.exports = function (Commands, Cypress, cy, state, config) {
|
||||
|
||||
const onFail = options._log
|
||||
|
||||
cookie.sameSite = normalizeSameSite(cookie.sameSite)
|
||||
|
||||
if (!_.isUndefined(cookie.sameSite) && !VALID_SAMESITE_VALUES.includes(cookie.sameSite)) {
|
||||
$errUtils.throwErrByPath('setCookie.invalid_samesite', {
|
||||
onFail,
|
||||
args: {
|
||||
value: options.sameSite, // for clarity, throw the error with the user's unnormalized option
|
||||
validValues: VALID_SAMESITE_VALUES,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// cookies with SameSite=None must also set Secure
|
||||
// @see https://web.dev/samesite-cookies-explained/#changes-to-the-default-behavior-without-samesite
|
||||
if (cookie.sameSite === 'no_restriction' && cookie.secure !== true) {
|
||||
$errUtils.throwErrByPath('setCookie.secure_not_set_with_samesite_none', {
|
||||
onFail,
|
||||
args: {
|
||||
value: options.sameSite, // for clarity, throw the error with the user's unnormalized option
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (!_.isString(name) || !_.isString(value)) {
|
||||
$errUtils.throwErrByPath('setCookie.invalid_arguments', { onFail })
|
||||
}
|
||||
|
||||
return automateCookies('set:cookie', cookie, options._log, options.timeout)
|
||||
.then((resp) => {
|
||||
maybeStripSameSiteProp(resp)
|
||||
|
||||
options.cookie = resp
|
||||
|
||||
return resp
|
||||
|
||||
@@ -1186,6 +1186,23 @@ module.exports = {
|
||||
message: "#{cmd('setCookie')} must be passed an RFC-6265-compliant cookie value. You passed:\n\n`{{value}}`"
|
||||
docsUrl: "https://on.cypress.io/setcookie"
|
||||
}
|
||||
invalid_samesite: ({ validValues, value }) => {
|
||||
message: """
|
||||
If a `sameSite` value is supplied to #{cmd('setCookie')}, it must be a string from the following list:
|
||||
> #{validValues.join(', ')}
|
||||
You passed:
|
||||
> #{format(value)}
|
||||
"""
|
||||
docsUrl: "https://on.cypress.io/setcookie"
|
||||
}
|
||||
secure_not_set_with_samesite_none: ({ validValues, value }) => {
|
||||
message: """
|
||||
Only cookies with the `secure` flag set to `true` can use `sameSite: '{{value}}'`.
|
||||
|
||||
Pass `secure: true` to #{cmd('setCookie')} to set a cookie with `sameSite: '{{value}}'`.
|
||||
"""
|
||||
docsUrl: "https://on.cypress.io/setcookie"
|
||||
}
|
||||
|
||||
should:
|
||||
chainer_not_found: "The chainer `{{chainer}}` was not found. Could not build assertion."
|
||||
|
||||
@@ -371,7 +371,7 @@ describe "src/cy/commands/cookies", ->
|
||||
}).then ->
|
||||
expect(Cypress.automation).to.be.calledWith(
|
||||
"set:cookie",
|
||||
{ domain: "localhost", name: "foo", value: "bar", path: "/", secure: false, httpOnly: false, expiry: 12345 }
|
||||
{ domain: "localhost", name: "foo", value: "bar", path: "/", secure: false, httpOnly: false, expiry: 12345, sameSite: undefined }
|
||||
)
|
||||
|
||||
it "can change options", ->
|
||||
@@ -384,7 +384,7 @@ describe "src/cy/commands/cookies", ->
|
||||
}).then ->
|
||||
expect(Cypress.automation).to.be.calledWith(
|
||||
"set:cookie",
|
||||
{ domain: "brian.dev.local", name: "foo", value: "bar", path: "/foo", secure: true, httpOnly: true, expiry: 987 }
|
||||
{ domain: "brian.dev.local", name: "foo", value: "bar", path: "/foo", secure: true, httpOnly: true, expiry: 987, sameSite: undefined }
|
||||
)
|
||||
|
||||
it "does not mutate options", ->
|
||||
@@ -394,6 +394,33 @@ describe "src/cy/commands/cookies", ->
|
||||
cy.setCookie("foo", "bar", {}).then ->
|
||||
expect(options).deep.eq({})
|
||||
|
||||
it "can set cookies with sameSite", ->
|
||||
Cypress.automation.restore()
|
||||
Cypress.utils.addTwentyYears.restore()
|
||||
|
||||
Cypress.sinon.stub(Cypress, 'config').callThrough()
|
||||
.withArgs('experimentalGetCookiesSameSite').returns(true)
|
||||
|
||||
cy.setCookie('one', 'bar', { sameSite: 'none', secure: true })
|
||||
cy.getCookie('one').should('include', { sameSite: 'no_restriction' })
|
||||
|
||||
cy.setCookie('two', 'bar', { sameSite: 'no_restriction', secure: true })
|
||||
cy.getCookie('two').should('include', { sameSite: 'no_restriction' })
|
||||
|
||||
cy.setCookie('three', 'bar', { sameSite: 'Lax' })
|
||||
cy.getCookie('three').should('include', { sameSite: 'lax' })
|
||||
|
||||
cy.setCookie('four', 'bar', { sameSite: 'Strict' })
|
||||
cy.getCookie('four').should('include', { sameSite: 'strict' })
|
||||
|
||||
cy.setCookie('five', 'bar')
|
||||
|
||||
## @see https://bugzilla.mozilla.org/show_bug.cgi?id=1624668
|
||||
if Cypress.isBrowser('firefox')
|
||||
cy.getCookie('five').should('include', { sameSite: 'no_restriction' })
|
||||
else
|
||||
cy.getCookie('five').should('not.have.property', 'sameSite')
|
||||
|
||||
describe "timeout", ->
|
||||
it "sets timeout to Cypress.config(responseTimeout)", ->
|
||||
Cypress.config("responseTimeout", 2500)
|
||||
@@ -497,6 +524,39 @@ describe "src/cy/commands/cookies", ->
|
||||
|
||||
cy.setCookie("foo", 123)
|
||||
|
||||
it "when an invalid samesite prop is supplied", (done) ->
|
||||
cy.on "fail", (err) =>
|
||||
lastLog = @lastLog
|
||||
|
||||
expect(@logs.length).to.eq(1)
|
||||
expect(lastLog.get("error").message).to.eq """
|
||||
If a `sameSite` value is supplied to `cy.setCookie()`, it must be a string from the following list:
|
||||
> no_restriction, lax, strict
|
||||
You passed:
|
||||
> bad
|
||||
"""
|
||||
expect(lastLog.get("error").docsUrl).to.eq "https://on.cypress.io/setcookie"
|
||||
expect(lastLog.get("error")).to.eq(err)
|
||||
done()
|
||||
|
||||
cy.setCookie('foo', 'bar', { sameSite: 'bad' })
|
||||
|
||||
it "when samesite=none is supplied and secure is not set", (done) ->
|
||||
cy.on "fail", (err) =>
|
||||
lastLog = @lastLog
|
||||
|
||||
expect(@logs.length).to.eq(1)
|
||||
expect(lastLog.get("error").message).to.eq """
|
||||
Only cookies with the `secure` flag set to `true` can use `sameSite: 'None'`.
|
||||
|
||||
Pass `secure: true` to `cy.setCookie()` to set a cookie with `sameSite: 'None'`.
|
||||
"""
|
||||
expect(lastLog.get("error").docsUrl).to.eq "https://on.cypress.io/setcookie"
|
||||
expect(lastLog.get("error")).to.eq(err)
|
||||
done()
|
||||
|
||||
cy.setCookie('foo', 'bar', { sameSite: 'None' })
|
||||
|
||||
context "when setting an invalid cookie", ->
|
||||
it "throws an error if the backend responds with an error", (done) ->
|
||||
err = new Error("backend could not set cookie")
|
||||
@@ -520,7 +580,7 @@ describe "src/cy/commands/cookies", ->
|
||||
|
||||
Cypress.automation
|
||||
.withArgs("set:cookie", {
|
||||
domain: "localhost", name: "foo", value: "bar", path: "/", secure: false, httpOnly: false, expiry: 12345
|
||||
domain: "localhost", name: "foo", value: "bar", path: "/", secure: false, httpOnly: false, expiry: 12345, sameSite: undefined
|
||||
})
|
||||
.resolves({
|
||||
name: "foo", value: "bar", domain: "localhost", path: "/", secure: true, httpOnly: false
|
||||
|
||||
@@ -6,7 +6,7 @@ const browser = require('webextension-polyfill')
|
||||
const client = require('./client')
|
||||
const { getCookieUrl } = require('../lib/util')
|
||||
|
||||
const COOKIE_PROPS = ['url', 'name', 'path', 'secure', 'domain']
|
||||
const COOKIE_PROPS = ['url', 'name', 'path', 'secure', 'domain', 'sameSite']
|
||||
const GET_ALL_PROPS = COOKIE_PROPS.concat(['session', 'storeId'])
|
||||
const SET_PROPS = COOKIE_PROPS.concat(['value', 'httpOnly', 'expirationDate'])
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ const debug = require('debug')('cypress:server:cookies')
|
||||
|
||||
// match the w3c webdriver spec on return cookies
|
||||
// https://w3c.github.io/webdriver/webdriver-spec.html#cookies
|
||||
const COOKIE_PROPERTIES = 'name value path domain secure httpOnly expiry hostOnly'.split(' ')
|
||||
const COOKIE_PROPERTIES = 'name value path domain secure httpOnly expiry hostOnly sameSite'.split(' ')
|
||||
|
||||
const normalizeCookies = (cookies) => {
|
||||
return _.map(cookies, normalizeCookieProps)
|
||||
|
||||
@@ -6,15 +6,26 @@ import debugModule from 'debug'
|
||||
|
||||
const debugVerbose = debugModule('cypress-verbose:server:browsers:cdp_automation')
|
||||
|
||||
export interface CyCookie {
|
||||
name: string
|
||||
value: string
|
||||
expirationDate: number
|
||||
hostOnly: boolean
|
||||
domain: string
|
||||
path: string
|
||||
secure: boolean
|
||||
httpOnly: boolean
|
||||
export type CyCookie = Pick<chrome.cookies.Cookie, 'name' | 'value' | 'expirationDate' | 'hostOnly' | 'domain' | 'path' | 'secure' | 'httpOnly' | 'sameSite'>
|
||||
|
||||
function convertSameSiteExtensionToCdp (str: chrome.cookies.SameSiteStatus): Optional<cdp.Network.CookieSameSite> {
|
||||
return ({
|
||||
'no_restriction': 'None',
|
||||
'lax': 'Lax',
|
||||
'strict': 'Strict',
|
||||
})[str]
|
||||
}
|
||||
|
||||
function convertSameSiteCdpToExtension (str: cdp.Network.CookieSameSite): chrome.cookies.SameSiteStatus {
|
||||
if (_.isUndefined(str)) {
|
||||
return str
|
||||
}
|
||||
|
||||
if (str === 'None') {
|
||||
return 'no_restriction'
|
||||
}
|
||||
|
||||
return str.toLowerCase() as chrome.cookies.SameSiteStatus
|
||||
}
|
||||
|
||||
// Cypress uses the webextension-style filtering
|
||||
@@ -52,6 +63,9 @@ export const CdpAutomation = (sendDebuggerCommandFn: SendDebuggerCommand) => {
|
||||
delete cookie.expires
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
cookie.sameSite = convertSameSiteCdpToExtension(cookie.sameSite)
|
||||
|
||||
// @ts-ignore
|
||||
cookie.expirationDate = cookie.expires
|
||||
delete cookie.expires
|
||||
@@ -76,6 +90,8 @@ export const CdpAutomation = (sendDebuggerCommandFn: SendDebuggerCommand) => {
|
||||
|
||||
// @ts-ignore
|
||||
cookie.expires = cookie.expirationDate
|
||||
// @ts-ignore
|
||||
cookie.sameSite = convertSameSiteExtensionToCdp(cookie.sameSite)
|
||||
|
||||
// without this logic, a cookie being set on 'foo.com' will only be set for 'foo.com', not other subdomains
|
||||
if (!cookie.hostOnly && cookie.domain[0] !== '.') {
|
||||
@@ -93,7 +109,7 @@ export const CdpAutomation = (sendDebuggerCommandFn: SendDebuggerCommand) => {
|
||||
delete cookie.hostOnly
|
||||
delete cookie.expirationDate
|
||||
|
||||
return cookie
|
||||
return cookie as any
|
||||
}
|
||||
|
||||
const getAllCookies = (filter: CyCookieFilter) => {
|
||||
|
||||
@@ -94,7 +94,7 @@ systemConfigKeys = toWords """
|
||||
# Know experimental flags / values
|
||||
# each should start with "experimental" and be camel cased
|
||||
# example: experimentalComponentTesting
|
||||
experimentalConfigKeys = []
|
||||
experimentalConfigKeys = ['experimentalGetCookiesSameSite']
|
||||
|
||||
CONFIG_DEFAULTS = {
|
||||
port: null
|
||||
@@ -154,6 +154,7 @@ CONFIG_DEFAULTS = {
|
||||
## experimental keys (should all start with "experimental" prefix)
|
||||
# example for component testing with subkeys
|
||||
# experimentalComponentTesting: { componentFolder: 'cypress/component' }
|
||||
experimentalGetCookiesSameSite: false
|
||||
}
|
||||
|
||||
validationRules = {
|
||||
@@ -194,6 +195,7 @@ validationRules = {
|
||||
watchForFileChanges: v.isBoolean
|
||||
firefoxGcInterval: v.isValidFirefoxGcInterval
|
||||
# experimental flag validation below
|
||||
experimentalGetCookiesSameSite: v.isBoolean
|
||||
}
|
||||
|
||||
convertRelativeToAbsolutePaths = (projectRoot, obj, defaults = {}) ->
|
||||
|
||||
@@ -50,7 +50,9 @@ interface StringValues {
|
||||
}
|
||||
```
|
||||
*/
|
||||
const _summaries: StringValues = {}
|
||||
const _summaries: StringValues = {
|
||||
experimentalGetCookiesSameSite: 'Adds `sameSite` values to the objects yielded from `cy.setCookie()`, `cy.getCookie()`, and `cy.getCookies()`. This will become the default behavior in Cypress 5.0.',
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps short names for experiments. When adding new experiments, add a short name.
|
||||
@@ -62,7 +64,9 @@ const _summaries: StringValues = {}
|
||||
}
|
||||
```
|
||||
*/
|
||||
const _names: StringValues = {}
|
||||
const _names: StringValues = {
|
||||
experimentalGetCookiesSameSite: 'Set `sameSite` property when retrieving cookies',
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this object for easy stubbing from end-to-end tests.
|
||||
|
||||
@@ -19,6 +19,13 @@ TLS_VERSION_ERROR_RE = /TLSV1_ALERT_PROTOCOL_VERSION|UNSUPPORTED_PROTOCOL/
|
||||
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
|
||||
|
||||
## sameSite from tough-cookie is slightly different from webextension API
|
||||
convertSameSiteToughToExtension = (str) =>
|
||||
if str is "none"
|
||||
return "no_restriction"
|
||||
|
||||
return str
|
||||
|
||||
getOriginalHeaders = (req = {}) ->
|
||||
## the request instance holds an instance
|
||||
## of the original ClientRequest
|
||||
@@ -492,6 +499,9 @@ module.exports = (options = {}) ->
|
||||
|
||||
if expiry <= 0
|
||||
return automationFn('clear:cookie', cookie)
|
||||
|
||||
cookie.sameSite = convertSameSiteToughToExtension(cookie.sameSite)
|
||||
|
||||
automationFn('set:cookie', cookie)
|
||||
|
||||
sendStream: (headers, automationFn, options = {}) ->
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
"@packages/static": "*",
|
||||
"@packages/ts": "*",
|
||||
"@types/chai-as-promised": "7.1.2",
|
||||
"@types/chrome": "0.0.91",
|
||||
"@types/chrome": "0.0.101",
|
||||
"@types/node": "8.10.55",
|
||||
"babel-plugin-add-module-exports": "1.0.2",
|
||||
"babelify": "10.0.0",
|
||||
|
||||
@@ -95,8 +95,8 @@ context('lib/browsers/cdp_automation', () => {
|
||||
return this.onRequest('get:cookies', { domain: 'localhost' })
|
||||
.then((resp) => {
|
||||
expect(resp).to.deep.eq([
|
||||
{ name: 'foo', value: 'f', path: '/', domain: 'localhost', secure: true, httpOnly: true, expirationDate: 123 },
|
||||
{ name: 'bar', value: 'b', path: '/', domain: 'localhost', secure: false, httpOnly: false, expirationDate: 456 },
|
||||
{ name: 'foo', value: 'f', path: '/', domain: 'localhost', secure: true, httpOnly: true, expirationDate: 123, sameSite: undefined },
|
||||
{ name: 'bar', value: 'b', path: '/', domain: 'localhost', secure: false, httpOnly: false, expirationDate: 456, sameSite: undefined },
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -115,7 +115,7 @@ context('lib/browsers/cdp_automation', () => {
|
||||
it('returns a specific cookie by name', function () {
|
||||
return this.onRequest('get:cookie', { domain: 'google.com', name: 'session' })
|
||||
.then((resp) => {
|
||||
expect(resp).to.deep.eq({ name: 'session', value: 'key', path: '/login', domain: 'google.com', secure: true, httpOnly: true, expirationDate: 123 })
|
||||
expect(resp).to.deep.eq({ name: 'session', value: 'key', path: '/login', domain: 'google.com', secure: true, httpOnly: true, expirationDate: 123, sameSite: undefined })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -129,9 +129,9 @@ context('lib/browsers/cdp_automation', () => {
|
||||
|
||||
describe('set:cookie', () => {
|
||||
beforeEach(function () {
|
||||
this.sendDebuggerCommand.withArgs('Network.setCookie', { domain: '.google.com', name: 'session', value: 'key', path: '/', expires: undefined })
|
||||
this.sendDebuggerCommand.withArgs('Network.setCookie', { domain: '.google.com', name: 'session', value: 'key', path: '/', expires: undefined, sameSite: undefined })
|
||||
.resolves({ success: true })
|
||||
.withArgs('Network.setCookie', { domain: 'foo', path: '/bar', name: '', value: '', expires: undefined })
|
||||
.withArgs('Network.setCookie', { domain: 'foo', path: '/bar', name: '', value: '', expires: undefined, sameSite: undefined })
|
||||
.rejects(new Error('some error'))
|
||||
.withArgs('Network.getAllCookies')
|
||||
.resolves({
|
||||
@@ -144,7 +144,7 @@ context('lib/browsers/cdp_automation', () => {
|
||||
it('resolves with the cookie props', function () {
|
||||
return this.onRequest('set:cookie', { domain: 'google.com', name: 'session', value: 'key', path: '/' })
|
||||
.then((resp) => {
|
||||
expect(resp).to.deep.eq({ domain: '.google.com', expirationDate: undefined, httpOnly: false, name: 'session', value: 'key', path: '/', secure: false })
|
||||
expect(resp).to.deep.eq({ domain: '.google.com', expirationDate: undefined, httpOnly: false, name: 'session', value: 'key', path: '/', secure: false, sameSite: undefined })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -178,7 +178,7 @@ context('lib/browsers/cdp_automation', () => {
|
||||
return this.onRequest('clear:cookie', { domain: 'google.com', name: 'session' })
|
||||
.then((resp) => {
|
||||
expect(resp).to.deep.eq(
|
||||
{ name: 'session', value: 'key', path: '/', domain: 'google.com', secure: true, httpOnly: true, expirationDate: 123 },
|
||||
{ name: 'session', value: 'key', path: '/', domain: 'google.com', secure: true, httpOnly: true, expirationDate: 123, sameSite: undefined },
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -205,7 +205,7 @@ context('lib/browsers/cdp_automation', () => {
|
||||
this.sendDebuggerCommand.withArgs('Browser.getVersion').resolves({ protocolVersion: '1.3' })
|
||||
this.sendDebuggerCommand.withArgs('Page.captureScreenshot').resolves({ data: 'foo' })
|
||||
|
||||
expect(this.onRequest('take:screenshot'))
|
||||
return expect(this.onRequest('take:screenshot'))
|
||||
.to.eventually.equal('data:image/png;base64,foo')
|
||||
})
|
||||
|
||||
@@ -213,7 +213,7 @@ context('lib/browsers/cdp_automation', () => {
|
||||
this.sendDebuggerCommand.withArgs('Browser.getVersion').resolves({ protocolVersion: '1.3' })
|
||||
this.sendDebuggerCommand.withArgs('Page.captureScreenshot').rejects()
|
||||
|
||||
expect(this.onRequest('take:screenshot'))
|
||||
return expect(this.onRequest('take:screenshot'))
|
||||
.to.be.rejectedWith('The browser responded with an error when Cypress attempted to take a screenshot.')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -797,6 +797,7 @@ describe "lib/config", ->
|
||||
requestTimeout: { value: 5000, from: "default" },
|
||||
responseTimeout: { value: 30000, from: "default" },
|
||||
execTimeout: { value: 60000, from: "default" },
|
||||
experimentalGetCookiesSameSite: { value: false, from: "default" },
|
||||
taskTimeout: { value: 60000, from: "default" },
|
||||
numTestsKeptInMemory: { value: 50, from: "default" },
|
||||
waitForAnimations: { value: true, from: "default" },
|
||||
@@ -866,6 +867,7 @@ describe "lib/config", ->
|
||||
requestTimeout: { value: 5000, from: "default" },
|
||||
responseTimeout: { value: 30000, from: "default" },
|
||||
execTimeout: { value: 60000, from: "default" },
|
||||
experimentalGetCookiesSameSite: { value: false, from: "default" },
|
||||
taskTimeout: { value: 60000, from: "default" },
|
||||
numTestsKeptInMemory: { value: 50, from: "default" },
|
||||
waitForAnimations: { value: true, from: "default" },
|
||||
|
||||
@@ -3496,10 +3496,10 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/chrome@0.0.91":
|
||||
version "0.0.91"
|
||||
resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.91.tgz#4b3996f55f344057e6a677c8366aa98080c6e380"
|
||||
integrity sha512-vNvo9lJkp1AvViWrUwe1bxhoMwr5dRZWlgr1DTuaNkz97LsG56lDX1sceWeZir2gRACJ5vdHtoRdVAvm8C75Ug==
|
||||
"@types/chrome@0.0.101":
|
||||
version "0.0.101"
|
||||
resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.101.tgz#8b6f7d4f1d4890ba7d950f8492725fa7ba9ab910"
|
||||
integrity sha512-9GpAt3fBVPdzEIwdgDrxqCaURyJqOVz+oIWB+iDE2FD0ZeGQhkMwTqJIW+Ok/lnie6tt6DwVMZkOS8x6QkmWsQ==
|
||||
dependencies:
|
||||
"@types/filesystem" "*"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user