fix(rewriter): rewrite accesses to document.location (#7418)

This commit is contained in:
Zach Bloomquist
2020-05-19 12:26:40 -04:00
committed by GitHub
parent 4d5c30843a
commit 96c551cecf
12 changed files with 165 additions and 25 deletions

View File

@@ -14,8 +14,17 @@ export function resolveWindowReference (this: typeof $Cypress, currentWindow: Wi
const getTargetValue = () => {
const targetValue = accessedObject[accessedProp]
const accessingDocument = dom.isDocument(accessedObject)
const hasLocation = dom.isWindow(accessedObject) || accessingDocument
if (hasLocation && accessedProp === 'location') {
if (accessingDocument) {
// `document.location` is the same reference as `window.location`.
// use `window.location` so that the location Proxy can be cached in the same
// place on `window` regardless of if it is accessed via `document` or `window`
accessedObject = currentWindow
}
if (dom.isWindow(accessedObject) && accessedProp === 'location') {
const targetLocation = resolveLocationReference(accessedObject)
if (isValPassed) {

View File

@@ -24,7 +24,6 @@ describe('src/cypress/resolvers', function () {
// @ts-ignore
cy.spy(unboundFn, 'bind')
// @ts-ignore
const actual = Cypress.resolveWindowReference({}, unboundFnWindow, 'parent')
expect(actual).to.be.instanceOf(Function)
@@ -32,16 +31,26 @@ describe('src/cypress/resolvers', function () {
expect(unboundFn.bind).to.be.calledWith(unboundFnWindow)
})
it('returns proxied location object if prop is location', function () {
const contentWindow = Cypress.state('$autIframe')!.prop('contentWindow')
// @ts-ignore
const actual = Cypress.resolveWindowReference(contentWindow, contentWindow, 'location')
context('returns proxied location object', function () {
it('if prop is location and obj is a Window', function () {
const contentWindow = Cypress.state('$autIframe')!.prop('contentWindow')
const actual = Cypress.resolveWindowReference(contentWindow, contentWindow, 'location')
cy.stub(Cypress.dom, 'isWindow').withArgs(contentWindow).returns(true)
cy.stub(Cypress.dom, 'isJquery').withArgs(contentWindow).returns(false)
cy.stub(Cypress.dom, 'isWindow').withArgs(contentWindow).returns(true)
cy.stub(Cypress.dom, 'isJquery').withArgs(contentWindow).returns(false)
// @ts-ignore
expect(actual).to.eq(Cypress.resolveLocationReference(contentWindow))
expect(actual).to.eq(Cypress.resolveLocationReference(contentWindow))
})
it('if prop is location and obj is a Document', function () {
const contentWindow = Cypress.state('$autIframe')!.prop('contentWindow')
const { document } = contentWindow
const actual = Cypress.resolveWindowReference(contentWindow, document, 'location')
cy.stub(Cypress.dom, 'isDocument').withArgs(document).returns(true)
expect(actual).to.eq(Cypress.resolveLocationReference(contentWindow))
})
})
context('window reference selection', function () {
@@ -131,7 +140,6 @@ describe('src/cypress/resolvers', function () {
isJquery.withArgs(frame).returns(false)
})
// @ts-ignore
const actual = Cypress.resolveWindowReference(currentWindow, accessedObject, accessedProp)
expect(actual).to.eq(expected)
@@ -151,7 +159,6 @@ describe('src/cypress/resolvers', function () {
})
it('.href setter sets location.href with resolved URL', () => {
// @ts-ignore
const loc = Cypress.resolveLocationReference(fakeWindow)
loc.href = 'foo'
@@ -160,7 +167,6 @@ describe('src/cypress/resolvers', function () {
})
it('.assign() calls location.assign with resolved URL', () => {
// @ts-ignore
const loc = Cypress.resolveLocationReference(fakeWindow)
loc.assign('foo')
@@ -169,7 +175,6 @@ describe('src/cypress/resolvers', function () {
})
it('.replace() calls location.replace with resolved URL', () => {
// @ts-ignore
const loc = Cypress.resolveLocationReference(fakeWindow)
loc.replace('foo')
@@ -178,7 +183,6 @@ describe('src/cypress/resolvers', function () {
})
it('calls through to unintercepted functions', () => {
// @ts-ignore
const loc = Cypress.resolveLocationReference(fakeWindow)
loc.someFn('foo')
@@ -187,7 +191,6 @@ describe('src/cypress/resolvers', function () {
})
it('calls through to unintercepted setters + getters', () => {
// @ts-ignore
const loc = Cypress.resolveLocationReference(fakeWindow)
expect(loc.someProp).to.eq('foo')
@@ -199,9 +202,7 @@ describe('src/cypress/resolvers', function () {
})
it('returns the same object between calls', () => {
// @ts-ignore
const loc1 = Cypress.resolveLocationReference(fakeWindow)
// @ts-ignore
const loc2 = Cypress.resolveLocationReference(fakeWindow)
expect(loc1).to.eq(loc2)

View File

@@ -2,13 +2,15 @@
// TODO: find a better place for this
declare namespace Cypress {
interface Cypress {
// TODO: how to pull these from resolvers.ts? can't import in a d.ts file...
resolveWindowReference: any
resolveLocationReference: any
/**
* Access and set Cypress's internal state.
*/
state: State
}
interface State {

View File

@@ -138,6 +138,14 @@ describe('js rewriter', function () {
'window.location = "bar"',
`globalThis.top.Cypress.resolveWindowReference(globalThis, window, 'location', "bar")`,
],
[
'document.location.href = "bar"',
`${match('document', 'location')}.href = "bar"`,
],
[
'document.location = "bar"',
`globalThis.top.Cypress.resolveWindowReference(globalThis, document, 'location', "bar")`,
],
]
.forEach(([string, expected]) => {
if (!expected) {

View File

@@ -779,10 +779,16 @@ exports['e2e visit / low response timeout / passes with experimentalSourceRewrit
it can relative redirect in a settimeout
✓ with location.href
✓ with window.location.href
✓ with document.location.href
✓ with window.document.location.href
✓ with location.href = #hash
✓ with location.replace()
✓ with location.assign()
✓ with location = ...
✓ with window.location = ...
✓ with document.location = ...
✓ with window.document.location = ...
✓ with document.location = #hash
✓ with location.search
✓ with location.pathname
can load some well-known sites in a timely manner
@@ -793,15 +799,15 @@ exports['e2e visit / low response timeout / passes with experimentalSourceRewrit
- http://github.com
12 passing
18 passing
6 pending
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 18
│ Passing: 12
│ Tests: 24
│ Passing: 18
│ Failing: 0 │
│ Pending: 6 │
│ Skipped: 0 │
@@ -825,9 +831,9 @@ exports['e2e visit / low response timeout / passes with experimentalSourceRewrit
Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ✔ source_rewriting_spec.js XX:XX 18 12 - 6 - │
│ ✔ source_rewriting_spec.js XX:XX 24 18 - 6 - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
✔ All specs passed! XX:XX 18 12 - 6 -
✔ All specs passed! XX:XX 24 18 - 6 -
`

View File

@@ -27,6 +27,24 @@ describe('source rewriting spec', function () {
cy.location('pathname').should('eq', '/static/index.html')
})
it('with document.location.href', function () {
cy.visit('/static/settimeout_redirect_document_href.html')
cy.location('pathname').should('eq', '/static/index.html')
})
it('with window.document.location.href', function () {
cy.visit('/static/settimeout_redirect_window_document_href.html')
cy.location('pathname').should('eq', '/static/index.html')
})
it('with location.href = #hash', function () {
cy.visit('/static/settimeout_redirect_href_hash.html')
cy.location().should('include', {
pathname: '/static/settimeout_redirect_href_hash.html',
hash: '#foo',
})
})
it('with location.replace()', function () {
cy.visit('/static/settimeout_redirect_replace.html')
cy.location('pathname').should('eq', '/static/index.html')
@@ -47,6 +65,24 @@ describe('source rewriting spec', function () {
cy.location('pathname').should('eq', '/static/index.html')
})
it('with document.location = ...', function () {
cy.visit('/static/settimeout_redirect_set_document_location.html')
cy.location('pathname').should('eq', '/static/index.html')
})
it('with window.document.location = ...', function () {
cy.visit('/static/settimeout_redirect_set_window_document_location.html')
cy.location('pathname').should('eq', '/static/index.html')
})
it('with document.location = #hash', function () {
cy.visit('/static/settimeout_redirect_set_document_location_hash.html')
cy.location().should('include', {
pathname: '/static/settimeout_redirect_set_document_location_hash.html',
hash: '#foo',
})
})
it('with location.search', function () {
cy.visit('/static/settimeout_redirect_search.html')
cy.location().should('include', {

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<script type="text/javascript">
setTimeout(() => {
document.location.href = 'index.html';
}, 1)
</script>
</body>
</html>

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<script type="text/javascript">
setTimeout(() => {
location.href = '#foo';
}, 1)
</script>
</body>
</html>

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<script type="text/javascript">
setTimeout(() => {
document.location = 'index.html';
}, 1)
</script>
</body>
</html>

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<script type="text/javascript">
setTimeout(() => {
document.location = '#foo';
}, 1)
</script>
</body>
</html>

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<script type="text/javascript">
setTimeout(() => {
window.document.location = 'index.html';
}, 1)
</script>
</body>
</html>

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<script type="text/javascript">
setTimeout(() => {
window.document.location.href = 'index.html';
}, 1)
</script>
</body>
</html>