Skip to content

Commit

Permalink
chore: Wrap native methods in secondary domain (#17303)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbreiding authored Jul 14, 2021
1 parent 66d947d commit 700636a
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 93 deletions.
5 changes: 3 additions & 2 deletions packages/driver/cypress/fixtures/multidomain-secondary.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
<head>
</head>
<body>
<p></p>
<p data-cy="dom-check">From a secondary domain</p>
<p data-cy="cypress-check"></p>

<script>
if (window.Cypress) {
document.querySelector('p').innerText = 'From a secondary domain with window.Cypress'
document.querySelector('[data-cy="cypress-check"]').innerText = 'Has window.Cypress'
}
</script>
</body>
Expand Down
61 changes: 43 additions & 18 deletions packages/driver/cypress/integration/e2e/multidomain_spec.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,48 @@
// FIXME: Skip this for now since it's flaky
it.skip('verifies initial implementation of sibling iframe and switchToDomain', (done) => {
top.addEventListener('message', (event) => {
if (event.data && event.data.textFromParagraph !== undefined) {
expect(event.data.host).to.equal('127.0.0.1:3501')
expect(event.data.textFromParagraph).to.equal('From a secondary domain with window.Cypress')
done()
// FIXME: Skip these for now since they're flaky
describe.skip('multidomain', () => {
const expectTextMessage = (text, done) => {
const onMessage = (event) => {
if (event.data && event.data.queriedText !== undefined) {
expect(event.data.host).to.equal('127.0.0.1:3501')
expect(event.data.queriedText).to.equal(text)

top.removeEventListener('message', onMessage)

done()
}
}
}, false)

cy.viewport(900, 300)
cy.visit('/fixtures/multidomain.html')
// @ts-ignore
cy.anticipateMultidomain()
cy.get('a').click()
// @ts-ignore
cy.switchToDomain('127.0.0.1:3501', () => {

top.addEventListener('message', onMessage, false)
}

beforeEach(() => {
cy.visit('/fixtures/multidomain.html')
// @ts-ignore
cy.anticipateMultidomain()
cy.get('a').click()
})

it('runs synchronous commands in secondary domain', (done) => {
expectTextMessage('From a secondary domain', done)

// @ts-ignore
cy.switchToDomain('127.0.0.1:3501', () => {
// @ts-ignore
cy.now('get', '[data-cy="dom-check"]').then(($el) => {
top.postMessage({ host: location.host, queriedText: $el.text() }, '*')
})
})
})

it('sets up window.Cypress in secondary domain', (done) => {
expectTextMessage('Has window.Cypress', done)

// @ts-ignore
cy.now('get', 'p').then(($el) => {
top.postMessage({ host: location.host, textFromParagraph: $el.text() }, '*')
cy.switchToDomain('127.0.0.1:3501', () => {
// @ts-ignore
cy.now('get', '[data-cy="cypress-check"]').then(($el) => {
top.postMessage({ host: location.host, queriedText: $el.text() }, '*')
})
})
})
})
10 changes: 10 additions & 0 deletions packages/driver/src/cy/jquery.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,17 @@ const create = function (state) {
return state('jQuery') || state('window').$
}

const $$ = function (selector, context) {
if (context == null) {
context = state('document')
}

return $dom.query(selector, context)
}

return {
$$,

getRemotejQueryInstance (subject) {
// we make assumptions that you cannot have
// an array of mixed types, so we only look at
Expand Down
66 changes: 66 additions & 0 deletions packages/driver/src/cy/overrides.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const { registerFetch } = require('unfetch')
const $selection = require('../dom/selection')

export const create = (state, config, focused, snapshots) => {
const wrapNativeMethods = function (contentWindow) {
try {
// return null to trick contentWindow into thinking
// its not been iframed if modifyObstructiveCode is true
if (config('modifyObstructiveCode')) {
Object.defineProperty(contentWindow, 'frameElement', {
get () {
return null
},
})
}

contentWindow.HTMLElement.prototype.focus = function (focusOption) {
return focused.interceptFocus(this, contentWindow, focusOption)
}

contentWindow.HTMLElement.prototype.blur = function () {
return focused.interceptBlur(this)
}

contentWindow.SVGElement.prototype.focus = function (focusOption) {
return focused.interceptFocus(this, contentWindow, focusOption)
}

contentWindow.SVGElement.prototype.blur = function () {
return focused.interceptBlur(this)
}

contentWindow.HTMLInputElement.prototype.select = function () {
return $selection.interceptSelect.call(this)
}

contentWindow.document.hasFocus = function () {
return focused.documentHasFocus.call(this)
}

const cssModificationSpy = function (original, ...args) {
snapshots.onCssModified(this.href)

return original.apply(this, args)
}

const { insertRule } = contentWindow.CSSStyleSheet.prototype
const { deleteRule } = contentWindow.CSSStyleSheet.prototype

contentWindow.CSSStyleSheet.prototype.insertRule = _.wrap(insertRule, cssModificationSpy)
contentWindow.CSSStyleSheet.prototype.deleteRule = _.wrap(deleteRule, cssModificationSpy)

if (config('experimentalFetchPolyfill')) {
// drop "fetch" polyfill that replaces it with XMLHttpRequest
// from the app iframe that we wrap for network stubbing
contentWindow.fetch = registerFetch(contentWindow)
// flag the polyfill to test this experimental feature easier
state('fetchPolyfilled', true)
}
} catch (error) {} // eslint-disable-line no-empty
}

return {
wrapNativeMethods,
}
}
77 changes: 5 additions & 72 deletions packages/driver/src/cypress/cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,12 @@ const $Timers = require('../cy/timers')
const $Timeouts = require('../cy/timeouts')
const $Retries = require('../cy/retries')
const $Stability = require('../cy/stability')
const $selection = require('../dom/selection')
const $Overrides = require('../cy/overrides')
const $Snapshots = require('../cy/snapshots')
const $CommandQueue = require('./command_queue')
const $VideoRecorder = require('../cy/video-recorder')
const $TestConfigOverrides = require('../cy/testConfigOverrides')

const { registerFetch } = require('unfetch')

const noArgsAreAFunction = (args) => {
return !_.some(args, _.isFunction)
}
Expand Down Expand Up @@ -194,14 +192,6 @@ const create = function (specWindow, Cypress, Cookies, state, config, log) {
})
}

const $$ = function (selector, context) {
if (context == null) {
context = state('document')
}

return $dom.query(selector, context)
}

const queue = $CommandQueue.create()

$VideoRecorder.create(Cypress)
Expand All @@ -224,8 +214,9 @@ const create = function (specWindow, Cypress, Cookies, state, config, log) {

const ensures = $Ensures.create(state, expect)

const snapshots = $Snapshots.create($$, state)
const snapshots = $Snapshots.create(jquery.$$, state)
const testConfigOverrides = $TestConfigOverrides.create()
const overrides = $Overrides.create(state, config, focused, snapshots)

const isCy = (val) => {
return (val === cy) || $utils.isInstanceOf(val, $Chainer)
Expand Down Expand Up @@ -300,64 +291,6 @@ const create = function (specWindow, Cypress, Cookies, state, config, log) {
})
}

const wrapNativeMethods = function (contentWindow) {
try {
// return null to trick contentWindow into thinking
// its not been iframed if modifyObstructiveCode is true
if (config('modifyObstructiveCode')) {
Object.defineProperty(contentWindow, 'frameElement', {
get () {
return null
},
})
}

contentWindow.HTMLElement.prototype.focus = function (focusOption) {
return focused.interceptFocus(this, contentWindow, focusOption)
}

contentWindow.HTMLElement.prototype.blur = function () {
return focused.interceptBlur(this)
}

contentWindow.SVGElement.prototype.focus = function (focusOption) {
return focused.interceptFocus(this, contentWindow, focusOption)
}

contentWindow.SVGElement.prototype.blur = function () {
return focused.interceptBlur(this)
}

contentWindow.HTMLInputElement.prototype.select = function () {
return $selection.interceptSelect.call(this)
}

contentWindow.document.hasFocus = function () {
return focused.documentHasFocus.call(this)
}

const cssModificationSpy = function (original, ...args) {
snapshots.onCssModified(this.href)

return original.apply(this, args)
}

const { insertRule } = contentWindow.CSSStyleSheet.prototype
const { deleteRule } = contentWindow.CSSStyleSheet.prototype

contentWindow.CSSStyleSheet.prototype.insertRule = _.wrap(insertRule, cssModificationSpy)
contentWindow.CSSStyleSheet.prototype.deleteRule = _.wrap(deleteRule, cssModificationSpy)

if (config('experimentalFetchPolyfill')) {
// drop "fetch" polyfill that replaces it with XMLHttpRequest
// from the app iframe that we wrap for network stubbing
contentWindow.fetch = registerFetch(contentWindow)
// flag the polyfill to test this experimental feature easier
state('fetchPolyfilled', true)
}
} catch (error) {} // eslint-disable-line no-empty
}

const enqueue = function (obj) {
// if we have a nestedIndex it means we're processing
// nested commands and need to splice them into the
Expand Down Expand Up @@ -917,7 +850,7 @@ const create = function (specWindow, Cypress, Cookies, state, config, log) {
id: _.uniqueId('cy'),

// synchrounous querying
$$,
$$: jquery.$$,

state,

Expand Down Expand Up @@ -1308,7 +1241,7 @@ const create = function (specWindow, Cypress, Cookies, state, config, log) {

contentWindowListeners(contentWindow)

wrapNativeMethods(contentWindow)
overrides.wrapNativeMethods(contentWindow)

snapshots.onBeforeWindowLoad()
},
Expand Down
15 changes: 14 additions & 1 deletion packages/driver/src/multidomain/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import $Cypress from '../cypress'
import $Cy from '../cypress/cy'
import $Commands from '../cypress/commands'
import $Log from '../cypress/log'
import $Focused from '../cy/focused'
import $jQuery from '../cy/jquery'
import $Snapshots from '../cy/snapshots'
import { create as createOverrides } from '../cy/overrides'

const onBeforeAppWindowLoad = (autWindow) => {
const specWindow = {
Expand Down Expand Up @@ -40,7 +44,14 @@ const onBeforeAppWindowLoad = (autWindow) => {
timeout () {},
})

$Commands.create(Cypress, cy, Cypress.state, Cypress.config)
const { state, config } = Cypress
const jquery = $jQuery.create(state)
const focused = $Focused.create(state)
const snapshots = $Snapshots.create(jquery.$$, state)

const overrides = createOverrides(state, config, focused, snapshots)

$Commands.create(Cypress, cy, state, config)

window.addEventListener('message', (event) => {
if (event.data && event.data.message === 'run:in:domain') {
Expand All @@ -53,6 +64,8 @@ const onBeforeAppWindowLoad = (autWindow) => {
autWindow.Cypress = Cypress
autWindow.cy = cy

overrides.wrapNativeMethods(autWindow)

top.postMessage('cross:domain:window:before:load', '*')
}

Expand Down

0 comments on commit 700636a

Please sign in to comment.