Skip to content

Commit

Permalink
Merge branch 'develop' into renovate/npm-underscore-vulnerability
Browse files Browse the repository at this point in the history
  • Loading branch information
jennifer-shehane authored May 10, 2021
2 parents 2a7e36f + 2b0add1 commit f88f07b
Show file tree
Hide file tree
Showing 29 changed files with 449 additions and 35 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
"lerna": "3.20.2",
"lint-staged": "9.4.1",
"listr": "0.14.3",
"lodash": "4.17.19",
"lodash": "4.17.21",
"make-empty-github-commit": "1.2.0",
"minimist": "1.2.5",
"mocha": "3.5.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop-gui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"fira": "cypress-io/fira#fb63362742eea8cdce0d90825ab9264d77719e3d",
"gravatar": "1.8.0",
"human-interval": "1.0.0",
"lodash": "4.17.19",
"lodash": "4.17.21",
"markdown-it": "11.0.0",
"mobx": "5.15.4",
"mobx-react": "6.1.8",
Expand Down
72 changes: 72 additions & 0 deletions packages/driver/cypress/fixtures/issue-1244.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<script>
let setCounter = 0
let getCounter = 0
const { getAttribute, setAttribute } = HTMLElement.prototype
HTMLElement.prototype.getAttribute = function() {
if (this.tagName === 'FORM' || this.tagName === 'A') {
getCounter++
}
return getAttribute.apply(this, arguments)
}
HTMLElement.prototype.setAttribute = function(name) {
if (this.tagName === 'FORM' || this.tagName === 'A' && !name.startsWith('data-cypress')) {
setCounter++
}
return setAttribute.apply(this, arguments)
}
function handleEventTarget(e) {
if (e.target.target !== '_top') {
e.target.target = '_top'
}
if (e.target.target !== '_top') {
throw new Error('target property must be top')
}
}
function handleEventAttribute(e) {
if (e.target.getAttribute('target') !== '_top') {
e.target.setAttribute('target', '_top')
}
if (e.target.getAttribute('target') !== '_top') {
throw new Error('Get Attribute must have top')
}
}
window.getCounters = () => {
return {
getCounter,
setCounter
}
}
</script>
<div>
<form method="GET" action="/fixtures/dom.html" onsubmit="handleEventTarget(event)">
<button class="setTarget" type="submit">Submit setTarget</button>
</form>
<form method="GET" action="/fixtures/dom.html" onsubmit="handleEventAttribute(event)">
<button class="setAttr" type="submit">Submit setAttribute</button>
</form>
<form target="_top" method="GET" action="/fixtures/dom.html">
<button class="inline_top" type="submit">Submit inline top</button>
</form>
<form target="_parent" method="GET" action="/fixtures/dom.html">
<button class="inline_parent" type="submit">Submit inline parent</button>
</form>
<form target="_self" method="GET" action="/fixtures/dom.html">
<button class="inline_self" type="submit">Submit inline self</button>
</form>
<form target="_blank" method="GET" action="/fixtures/dom.html">
<button class="inline_blank" type="submit">Submit blank</button>
</form>
<form target="invalid" method="GET" action="/fixtures/dom.html">
<button class="inline_invalid" type="submit">Submit invalid</button>
</form>
<div>
<a class="setTarget" href="/fixtures/dom.html" onclick="handleEventTarget(event)">Link setTarget</a>
<a class="setAttr" href="/fixtures/dom.html" onclick="handleEventAttribute(event)">Link setAttribute</a>
<a class="inline_top" href="/fixtures/dom.html" target="_top">Link top</a>
<a class="inline_parent" href="/fixtures/dom.html" target="_parent">Link parent</a>
<a class="inline_self" href="/fixtures/dom.html" target="_self">Link self</a>
<a class="inline_blank" href="/fixtures/dom.html" target="_blank">Link _blank</a>
<a class="inline_invalid" href="/fixtures/dom.html" target="invalid">Link invalid</a>
</div>
<iframe src="issue-1244.html">
</div>
142 changes: 142 additions & 0 deletions packages/driver/cypress/integration/commands/net_stubbing_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,16 @@ describe('network stubbing', { retries: { runMode: 2, openMode: 0 } }, function
// @ts-ignore
cy.intercept({ wrong: true })
})

it('times must be a positive integer', function (done) {
cy.on('fail', function (err) {
expect(err.message).to.include('`times` must be a positive integer.')
done()
})

cy
.intercept({ times: 9.75 })
})
})

context('with invalid handler', function () {
Expand Down Expand Up @@ -919,6 +929,101 @@ describe('network stubbing', { retries: { runMode: 2, openMode: 0 } }, function
})
})
})

// https://github.com/cypress-io/cypress/issues/15050
describe('cors expose header', () => {
// a different domain from the page own domain
const corsUrl = 'http://diff.foobar.com:3501/cors'

before(() => {
cy.visit('http://127.0.0.1:3500/fixtures/dom.html')
})

it('headers option is not set => no expose-header', () => {
cy
.intercept('/cors*', {})
.as('corsRequest')
.then(() => {
return $.get(corsUrl)
})

cy.wait('@corsRequest').then((res) => {
let headers = res.response?.headers

expect(headers).to.not.have.property('access-control-expose-headers')
})
})

it('headers option only has the accessible headers from the cors request => no expose-header', () => {
cy
.intercept('/cors*', {
body: { success: true },
headers: {
'cache-control': 'no-cache',
'content-language': 'en-US',
'content-type': 'text/html',
'Expires': 'Wed, 21 Oct 2015 07:28:00 GMT',
'Last-Modified': 'Wed, 21 Oct 2015 07:28:00 GMT',
'Pragma': 'no-cache',
},
})
.as('corsRequest')
.then(() => {
return $.get(corsUrl)
})

cy.wait('@corsRequest').then((res) => {
let headers = res.response?.headers

expect(headers).to.not.have.property('access-control-expose-headers')
})
})

it('headers option does not have the accessible header => include expose-header', () => {
cy
.intercept('/cors*', {
body: { success: true },
headers: {
'cache-control': 'no-cache',
'content-language': 'en-US',
'x-token': 'token',
},
})
.as('corsRequest')
.then(() => {
return $.get(corsUrl)
})

cy.wait('@corsRequest').then((res) => {
let headers = res.response?.headers

expect(headers!['access-control-expose-headers']).to.eq('*')
expect(headers!['x-token']).to.eq('token')
})
})

it('headers option has access-control-expose-headers => does not override', () => {
cy
.intercept('/cors*', {
body: { success: true },
headers: {
'access-control-expose-headers': 'x-token',
'x-token': 'token',
},
})
.as('corsRequest')
.then(() => {
return $.get(corsUrl)
})

cy.wait('@corsRequest').then((res) => {
let headers = res.response?.headers

expect(headers!['access-control-expose-headers']).to.eq('x-token')
expect(headers!['x-token']).to.eq('token')
})
})
})
})

context('stubbing with static responses', function () {
Expand Down Expand Up @@ -1588,6 +1693,43 @@ describe('network stubbing', { retries: { runMode: 2, openMode: 0 } }, function
})
})
})

context('with `times`', function () {
it('only uses each handler N times', function () {
const third = sinon.stub()

cy
.intercept({ url: '/foo*', times: 3 }, 'fourth').as('4')
.intercept({ url: '/foo*', times: 2 }, third).as('3')
.intercept({ url: '/foo*', times: 2 }, 'second').as('2')
.intercept({ url: '/foo*', times: 1 }, 'first').as('1')
.then(async () => {
const expectGet = (expected) => $.get('/foo').then((res) => expect(res).to.eq(expected))

await Promise.mapSeries([
'first',
'second',
'second',
'fourth',
'fourth',
'fourth',
], expectGet)

expect(third).to.be.calledTwice

// now that matches are exhausted, it should fall through
await $.get('/foo').catch((xhr) => {
expect(xhr).to.include({
status: 404,
})
})
})
.get('@1.all').should('have.length', 1)
.get('@2.all').should('have.length', 2)
.get('@3.all').should('have.length', 2)
.get('@4.all').should('have.length', 3)
})
})
})

context('with StaticResponse shorthand', function () {
Expand Down
82 changes: 82 additions & 0 deletions packages/driver/cypress/integration/issues/1244_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
describe('issue 1244', () => {
beforeEach(() => {
cy.visit('/fixtures/issue-1244.html').then(() => {
cy.on('window:before:unload', (e) => {
const win = cy.state('window')

expect(win.getCounters()).to.deep.equal({ getCounter: 0, setCounter: 0 })
})
})
})

for (const [el, target, action] of [['button', 'form', 'submit'], ['a', 'a', 'click']]) {
// <form> submit, <a> click
describe(`<${target}> ${action}`, () => {
it('correctly redirects when target=_top with target.target =', () => {
cy.get(`${el}.setTarget`).click()
cy.get('#dom').should('contain', 'DOM')
cy.url().should('include', 'dom.html')
})

it('correctly redirects when target=_top with setAttribute', () => {
cy.get(`${el}.setAttr`).click()
cy.get('#dom').should('contain', 'DOM')
cy.url().should('include', 'dom.html')
})

it('correctly redirects when target=_top inline in dom', () => {
cy.get(`${el}.inline_top`).click()
cy.get('#dom').should('contain', 'DOM')
cy.url().should('include', 'dom.html')
})

it('correctly redirects when target=_parent inline in dom', () => {
cy.get(`${el}.inline_parent`).click()
cy.get('#dom').should('contain', 'DOM')
cy.url().should('include', 'dom.html')
})

it('maintains behavior when target=_self', () => {
cy.get(`${el}.inline_self`).click()
cy.get('#dom').should('contain', 'DOM')
cy.url().should('include', 'dom.html')
})

it('maintains behavior when target=_blank,invalid', () => {
cy.get(`${el}.inline_blank`).click().then(() => {
cy.url().should('not.include', 'dom.html')
})

cy.get(`${el}.inline_invalid`).click().then(() => {
cy.url().should('not.include', 'dom.html')
})
})
})
}

describe('nested frame', () => {
it('does not strip form _parent', () => {
cy.get('iframe').then(($iframe) => {
const $el = $iframe.contents().find('button.inline_parent')

expect($el.closest('form')).to.have.attr('target', '_parent')

$el.trigger('click')
})

cy.get('#dom').should('contain', 'DOM')
cy.url().should('include', 'dom.html')
})

it('does not strip link _parent', () => {
cy.get('iframe').then(($iframe) => {
const $el = $iframe.contents().find('a.inline_parent')

$el[0].click()
})

cy.get('#dom').should('contain', 'DOM')
cy.url().should('include', 'dom.html')
})
})
})
2 changes: 1 addition & 1 deletion packages/driver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"jquery.scrollto": "2.1.2",
"js-cookie": "2.2.1",
"jsdom": "14.1.0",
"lodash": "4.17.19",
"lodash": "4.17.21",
"md5": "2.3.0",
"method-override": "3.0.0",
"methods": "1.1.2",
Expand Down
16 changes: 11 additions & 5 deletions packages/driver/src/cy/listeners.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const _ = require('lodash')
const { handleInvalidEventTarget, handleInvalidAnchorTarget } = require('./top_attr_guards')

const HISTORY_ATTRS = 'pushState replaceState'.split(' ')

Expand All @@ -9,9 +10,9 @@ const removeAllListeners = () => {
listenersAdded = false

for (let e of events) {
const [win, event, cb] = e
const [win, event, cb, capture] = e

win.removeEventListener(event, cb)
win.removeEventListener(event, cb, capture)
}

// reset all the events
Expand All @@ -20,10 +21,10 @@ const removeAllListeners = () => {
return null
}

const addListener = (win, event, fn) => {
events.push([win, event, fn])
const addListener = (win, event, fn, capture) => {
events.push([win, event, fn, capture])

win.addEventListener(event, fn)
win.addEventListener(event, fn, capture)
}

const eventHasReturnValue = (e) => {
Expand Down Expand Up @@ -101,6 +102,11 @@ module.exports = {
return callbacks.onSubmit(e)
})

// Handling the situation where "_top" is set on the <form> / <a> element, either in
// html or dynamically, by tapping in at the capture phase of the events
addListener(contentWindow, 'submit', handleInvalidEventTarget, true)
addListener(contentWindow, 'click', handleInvalidAnchorTarget, true)

contentWindow.alert = callbacks.onAlert
contentWindow.confirm = callbacks.onConfirm
},
Expand Down
Loading

0 comments on commit f88f07b

Please sign in to comment.