Skip to content

Commit

Permalink
Merge branch 'GROW-183' of github.com:cypress-io/cypress into GROW-183
Browse files Browse the repository at this point in the history
  • Loading branch information
panzarino committed Jun 25, 2021
2 parents 3c38408 + 15a9de8 commit 8903c8c
Show file tree
Hide file tree
Showing 22 changed files with 355 additions and 370 deletions.
2 changes: 1 addition & 1 deletion cli/types/cypress.d.ts
Expand Up @@ -60,7 +60,7 @@ declare namespace Cypress {
*/
displayName: string
version: string
majorVersion: number
majorVersion: number | string
path: string
isHeaded: boolean
isHeadless: boolean
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "cypress",
"version": "7.5.0",
"version": "7.6.0",
"description": "Cypress.io end to end testing tool",
"private": true,
"scripts": {
Expand Down
6 changes: 3 additions & 3 deletions packages/desktop-gui/src/runs/runs-list.jsx
Expand Up @@ -41,11 +41,11 @@ class RunsList extends Component {
componentDidMount () {
this._pingApiServer()
this._handlePolling()
this._getKey()
this._getRecordKeys()
}

componentDidUpdate () {
this._getKey()
this._getRecordKeys()
this._handlePolling()
}

Expand Down Expand Up @@ -106,7 +106,7 @@ class RunsList extends Component {
runsApi.stopPollingRuns()
}

_getKey () {
_getRecordKeys () {
if (this._needsKey()) {
projectsApi.getRecordKeys().then((keys = []) => {
if (keys.length) {
Expand Down
211 changes: 211 additions & 0 deletions packages/driver/cypress/integration/commands/net_stubbing_spec.ts
Expand Up @@ -1679,6 +1679,217 @@ describe('network stubbing', { retries: { runMode: 2, openMode: 0 } }, function
cy.visit('/fixtures/utf8-post.html')
})

// https://github.com/cypress-io/cypress/issues/16327
context('request url querystring', () => {
// In the code below, cy.window() is used instead of $.get.
// It's because XHR is not sent on Firefox and it's flaky on Chrome.
it('parse query correctly', () => {
cy.intercept({ url: '/users*' }, (req) => {
expect(req.query.someKey).to.deep.equal('someValue')
expect(req.query).to.deep.equal({ someKey: 'someValue' })
}).as('getUrl')

cy.window().then((win) => {
const xhr = new win.XMLHttpRequest()

xhr.open('GET', '/users?someKey=someValue')
xhr.send()
})

cy.wait('@getUrl')
})

context('reconcile changes', () => {
it('by assigning a new query parameter obj', () => {
cy.intercept({ url: '/users*' }, (req) => {
req.query = {
a: 'b',
}

expect(req.url).to.eq('http://localhost:3500/users?a=b')
}).as('getUrl')

cy.window().then((win) => {
const xhr = new win.XMLHttpRequest()

xhr.open('GET', '/users?someKey=someValue')
xhr.send()
})

cy.wait('@getUrl')
})

it('by setting new properties', () => {
cy.intercept({ url: '/users*' }, (req) => {
expect(req.query.a).to.eq('b')
req.query.c = 'd'

expect(req.url).to.eq('http://localhost:3500/users?a=b&c=d')
}).as('getUrl')

cy.window().then((win) => {
const xhr = new win.XMLHttpRequest()

xhr.open('GET', '/users?a=b')
xhr.send()
})

cy.wait('@getUrl')
})

it('by doing both', () => {
cy.intercept({ url: '/users*' }, (req) => {
req.query = {
a: 'b',
}

expect(req.query.a).to.eq('b')
req.query.c = 'd'

expect(req.url).to.eq('http://localhost:3500/users?a=b&c=d')
}).as('getUrl')

cy.window().then((win) => {
const xhr = new win.XMLHttpRequest()

xhr.open('GET', '/users?someKey=someValue')
xhr.send()
})

cy.wait('@getUrl')
})

it('by deleting query member', () => {
cy.intercept({ url: '/users*' }, (req) => {
req.query = {
a: 'b',
c: 'd',
}

delete req.query.c

expect(req.url).to.eq('http://localhost:3500/users?a=b')
}).as('getUrl')

cy.window().then((win) => {
const xhr = new win.XMLHttpRequest()

xhr.open('GET', '/users?someKey=someValue')
xhr.send()
})

cy.wait('@getUrl')
})

context('by setting new url', () => {
it('absolute path', () => {
cy.intercept({ url: '/users*' }, (req) => {
req.url = 'http://localhost:3500/users?a=b'

expect(req.query).to.deep.eq({ a: 'b' })
}).as('getUrl')

cy.window().then((win) => {
const xhr = new win.XMLHttpRequest()

xhr.open('GET', '/users?someKey=someValue')
xhr.send()
})

cy.wait('@getUrl')
})

it('relative path', () => {
cy.intercept({ url: '/users*' }, (req) => {
req.url = '/users?a=b'

expect(req.query).to.deep.eq({ a: 'b' })
expect(req.url).to.eq('http://localhost:3500/users?a=b')
}).as('getUrl')

cy.window().then((win) => {
const xhr = new win.XMLHttpRequest()

xhr.open('GET', '/users?someKey=someValue')
xhr.send()
})

cy.wait('@getUrl')
})

it('empty string', () => {
cy.intercept({ url: '/users*' }, (req) => {
req.url = ''

expect(req.query).to.deep.eq({})
}).as('getUrl')

cy.window().then((win) => {
const xhr = new win.XMLHttpRequest()

xhr.open('GET', '/users?someKey=someValue')
xhr.send()
})

cy.wait('@getUrl')
})
})

context('throwing errors correctly', () => {
it('defineproperty', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.eq('`defineProperty()` is not allowed.')

done()
})

cy.intercept({ url: '/users*' }, (req) => {
Object.defineProperty(req.query, 'key', {
enumerable: false,
configurable: false,
writable: false,
value: 'static',
})

expect(req.query).to.deep.eq({})
}).as('getUrl')

cy.window().then((win) => {
const xhr = new win.XMLHttpRequest()

xhr.open('GET', '/users?someKey=someValue')
xhr.send()
})

cy.wait('@getUrl')
})

it('setPrototypeOf', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.eq('`setPrototypeOf()` is not allowed.')

done()
})

cy.intercept({ url: '/users*' }, (req) => {
Object.setPrototypeOf(req.query, null)

expect(req.query).to.deep.eq({})
}).as('getUrl')

cy.window().then((win) => {
const xhr = new win.XMLHttpRequest()

xhr.open('GET', '/users?someKey=someValue')
xhr.send()
})

cy.wait('@getUrl')
})
})
})
})

context('request events', function () {
context('can end response', () => {
for (const eventName of ['before:response', 'response']) {
Expand Down
82 changes: 82 additions & 0 deletions packages/driver/src/cy/net-stubbing/events/before-request.ts
Expand Up @@ -119,8 +119,90 @@ export const onBeforeRequest: HandlerFn<CyHttpMessages.IncomingRequest> = (Cypre
let resolved = false
let handlerCompleted = false

const createQueryObject = () => {
try {
if (/^(?:[a-z]+:)?\/\//i.test(req.url) === false) {
const { protocol, hostname, port } = window.location

req.url = `${protocol}//${hostname}${port ? `:${port}` : ''}${req.url}`
}

const url = new URL(req.url)
const urlSearchParams = new URLSearchParams(url.search)
const result = {}

for (let pair of urlSearchParams.entries()) {
result[pair[0]] = pair[1]
}

return result
} catch { // avoid when url is "".
return {}
}
}

const updateUrlParams = (paramsObj) => {
const url = new URL(req.url)
const urlSearchParams = new URLSearchParams(paramsObj)

url.search = urlSearchParams.toString()
req.url = url.toString()
}

const createQueryProxy = (obj) => {
return new Proxy(obj, {
set (target, key, value) {
target[key] = value

updateUrlParams(target)

return true
},

deleteProperty (target, key) {
delete target[key]

updateUrlParams(target)

return true
},

defineProperty () {
$errUtils.throwErrByPath('net_stubbing.request_handling.defineproperty_is_not_allowed')

return false
},

setPrototypeOf () {
$errUtils.throwErrByPath('net_stubbing.request_handling.setprototypeof_is_not_allowed')

return false
},
})
}

let queryObj = createQueryObject()
let queryProxy = createQueryProxy(queryObj)

const userReq: CyHttpMessages.IncomingHttpRequest = {
...req,
get query () {
return queryProxy
},
set query (userQuery) {
updateUrlParams(userQuery)
queryProxy = createQueryProxy(userQuery)
},
get url () {
return req.url
},
set url (userUrl) {
req.url = userUrl

// reset query variables
queryObj = createQueryObject()
queryProxy = createQueryProxy(queryObj)
},
on (eventName, handler) {
if (!validEvents.includes(eventName)) {
return $errUtils.throwErrByPath('net_stubbing.request_handling.unknown_event', {
Expand Down
2 changes: 2 additions & 0 deletions packages/driver/src/cypress/error_messages.js
Expand Up @@ -1001,6 +1001,8 @@ module.exports = {
You passed: ${format(eventName)}`, 10)
},
event_needs_handler: `\`req.on()\` requires the second parameter to be a function.`,
defineproperty_is_not_allowed: `\`defineProperty()\` is not allowed.`,
setprototypeof_is_not_allowed: `\`setPrototypeOf()\` is not allowed.`,
},
request_error: {
network_error: ({ innerErr, req, route }) => {
Expand Down
6 changes: 1 addition & 5 deletions packages/launcher/lib/detect.ts
Expand Up @@ -26,17 +26,13 @@ export const setMajorVersion = <T extends HasVersion>(browser: T): T => {
let majorVersion = browser.majorVersion

if (browser.version) {
majorVersion = browser.version.split('.')[0]
majorVersion = parseInt(browser.version.split('.')[0]) || browser.version
log(
'browser %s version %s major version %s',
browser.name,
browser.version,
majorVersion,
)

if (majorVersion) {
majorVersion = parseInt(majorVersion)
}
}

return extend({}, browser, { majorVersion })
Expand Down

0 comments on commit 8903c8c

Please sign in to comment.