diff --git a/packages/driver/cypress/fixtures/multidomain-secondary.html b/packages/driver/cypress/fixtures/multidomain-secondary.html index 96c47fdce12d..44ae9267cdf6 100644 --- a/packages/driver/cypress/fixtures/multidomain-secondary.html +++ b/packages/driver/cypress/fixtures/multidomain-secondary.html @@ -3,11 +3,12 @@ -

+

From a secondary domain

+

diff --git a/packages/driver/cypress/integration/e2e/multidomain_spec.ts b/packages/driver/cypress/integration/e2e/multidomain_spec.ts index 5581dbb69255..0e5eae215517 100644 --- a/packages/driver/cypress/integration/e2e/multidomain_spec.ts +++ b/packages/driver/cypress/integration/e2e/multidomain_spec.ts @@ -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() }, '*') + }) }) }) }) diff --git a/packages/driver/src/cy/jquery.js b/packages/driver/src/cy/jquery.js index a79f296e93f0..bcb07e9f9d34 100644 --- a/packages/driver/src/cy/jquery.js +++ b/packages/driver/src/cy/jquery.js @@ -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 diff --git a/packages/driver/src/cy/overrides.ts b/packages/driver/src/cy/overrides.ts new file mode 100644 index 000000000000..8fb596268de0 --- /dev/null +++ b/packages/driver/src/cy/overrides.ts @@ -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, + } +} diff --git a/packages/driver/src/cypress/cy.js b/packages/driver/src/cypress/cy.js index aa8eb17928c9..1f215b4e8080 100644 --- a/packages/driver/src/cypress/cy.js +++ b/packages/driver/src/cypress/cy.js @@ -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) } @@ -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) @@ -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) @@ -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 @@ -917,7 +850,7 @@ const create = function (specWindow, Cypress, Cookies, state, config, log) { id: _.uniqueId('cy'), // synchrounous querying - $$, + $$: jquery.$$, state, @@ -1308,7 +1241,7 @@ const create = function (specWindow, Cypress, Cookies, state, config, log) { contentWindowListeners(contentWindow) - wrapNativeMethods(contentWindow) + overrides.wrapNativeMethods(contentWindow) snapshots.onBeforeWindowLoad() }, diff --git a/packages/driver/src/multidomain/index.js b/packages/driver/src/multidomain/index.js index 0b5ff8729ecc..cc73fb717c27 100644 --- a/packages/driver/src/multidomain/index.js +++ b/packages/driver/src/multidomain/index.js @@ -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 = { @@ -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') { @@ -53,6 +64,8 @@ const onBeforeAppWindowLoad = (autWindow) => { autWindow.Cypress = Cypress autWindow.cy = cy + overrides.wrapNativeMethods(autWindow) + top.postMessage('cross:domain:window:before:load', '*') }