diff --git a/packages/driver/cypress/fixtures/multidomain-aut.html b/packages/driver/cypress/fixtures/multidomain-aut.html new file mode 100644 index 000000000000..4a0c7d8838ce --- /dev/null +++ b/packages/driver/cypress/fixtures/multidomain-aut.html @@ -0,0 +1,14 @@ + + + + + +

Multidomain AUT

+

Some text in the cross-domain AUT

+ + + diff --git a/packages/driver/cypress/fixtures/multidomain-sibling.html b/packages/driver/cypress/fixtures/multidomain-sibling.html new file mode 100644 index 000000000000..d10ec54b3eb0 --- /dev/null +++ b/packages/driver/cypress/fixtures/multidomain-sibling.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/packages/driver/cypress/fixtures/multidomain.html b/packages/driver/cypress/fixtures/multidomain.html new file mode 100644 index 000000000000..f5a1107a98b0 --- /dev/null +++ b/packages/driver/cypress/fixtures/multidomain.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/driver/cypress/integration/e2e/multidomain_spec.ts b/packages/driver/cypress/integration/e2e/multidomain_spec.ts new file mode 100644 index 000000000000..81c4d8b4aaba --- /dev/null +++ b/packages/driver/cypress/integration/e2e/multidomain_spec.ts @@ -0,0 +1,5 @@ +// NOTE: this test only exists for manual verification as the +// multidomain bundle is a very incomplete work-in-progress +it('loads multidomain playground', () => { + cy.visit('/fixtures/multidomain.html') +}) diff --git a/packages/driver/cypress/plugins/server.js b/packages/driver/cypress/plugins/server.js index 03f810960682..565971fe09db 100644 --- a/packages/driver/cypress/plugins/server.js +++ b/packages/driver/cypress/plugins/server.js @@ -8,6 +8,7 @@ const path = require('path') const Promise = require('bluebird') const PATH_TO_SERVER_PKG = path.dirname(require.resolve('@packages/server')) +const PATH_TO_RUNNER_PKG = path.dirname(require.resolve('@packages/runner')) const httpPorts = [3500, 3501] const httpsPort = 3502 @@ -156,6 +157,13 @@ const createApp = (port) => { .send('server error') }) + app.get('/cypress_multidomain_runner.js', (req, res) => { + res.type('application/javascript') + res.sendFile(path.join('dist', 'cypress_multidomain_runner.js'), { + root: path.join(PATH_TO_RUNNER_PKG, '..'), + }) + }) + let _var = '' app.get('/set-var', (req, res) => { diff --git a/packages/driver/src/cypress/cy.js b/packages/driver/src/cypress/cy.js index 939fac2b1597..cd9924054565 100644 --- a/packages/driver/src/cypress/cy.js +++ b/packages/driver/src/cypress/cy.js @@ -75,6 +75,14 @@ const setTopOnError = function (Cypress, cy) { curCy = cy + try { + // this will throw if AUT is cross-domain and we don't need to worry + // about top's error handler in that case anyway + top.__alreadySetErrorHandlers__ + } catch (err) { + return + } + // prevent overriding top.onerror twice when loading more than one // instance of test runner. if (top.__alreadySetErrorHandlers__) { diff --git a/packages/driver/src/multidomain/index.js b/packages/driver/src/multidomain/index.js new file mode 100644 index 000000000000..5737213f923c --- /dev/null +++ b/packages/driver/src/multidomain/index.js @@ -0,0 +1,48 @@ +import $Cypress from '../cypress' +import $Cy from '../cypress/cy' +import $Commands from '../cypress/commands' +import $Log from '../cypress/log' + +export const initialize = (autWindow) => { + const specWindow = { + Error, + } + const Cypress = $Cypress.create({ + browser: { + channel: 'stable', + displayName: 'Chrome', + family: 'chromium', + isChosen: true, + isHeaded: true, + isHeadless: false, + majorVersion: 90, + name: 'chrome', + path: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', + version: '90.0.4430.212', + }, + }) + const log = (...args) => { + return Cypress.log.apply(Cypress, args) + } + const cy = $Cy.create(specWindow, Cypress, Cypress.Cookies, Cypress.state, Cypress.config, log) + + Cypress.log = $Log.create(Cypress, cy, Cypress.state, Cypress.config) + Cypress.runner = { + addLog () {}, + } + + Cypress.state('window', autWindow) + Cypress.state('document', autWindow.document) + Cypress.state('runnable', { + ctx: {}, + clearTimeout () {}, + resetTimeout () {}, + timeout () {}, + }) + + $Commands.create(Cypress, cy, Cypress.state) + + return { + cy, + } +} diff --git a/packages/runner/multidomain/.eslintrc.json b/packages/runner/multidomain/.eslintrc.json new file mode 100644 index 000000000000..0d7b491c8f62 --- /dev/null +++ b/packages/runner/multidomain/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "globals": { + "window": true + } +} diff --git a/packages/runner/multidomain/index.js b/packages/runner/multidomain/index.js new file mode 100644 index 000000000000..e72284fc337e --- /dev/null +++ b/packages/runner/multidomain/index.js @@ -0,0 +1,33 @@ +import { initialize } from '@packages/driver/src/multidomain' + +const autWindow = window.parent.frames[0] + +const { cy } = initialize(autWindow) + +autWindow.onReady = () => { + cy.now('get', 'p').then(($el) => { + // eslint-disable-next-line no-console + console.log('got the paragaph with text:', $el.text()) + }) +} + +/* + +Need: +- Cypress +- cy, with + - built-in commands + - user-defined commands + +Commands need: +- state +- config +- events + +Don't need: +- UI components +- spec runner +- mocha +- wasm / source map utils + +*/ diff --git a/packages/runner/webpack.config.ts b/packages/runner/webpack.config.ts index 6803ea617aca..89d061e89a3b 100644 --- a/packages/runner/webpack.config.ts +++ b/packages/runner/webpack.config.ts @@ -91,4 +91,17 @@ const injectionConfig: webpack.Configuration = { }, } -export default [mainConfig, injectionConfig] +// @ts-ignore +const multiDomainConfig: webpack.Configuration = { + mode: 'development', + ...getSimpleConfig(), + entry: { + cypress_multidomain_runner: [path.resolve(__dirname, 'multidomain/index.js')], + }, + output: { + path: path.resolve(__dirname, 'dist'), + filename: '[name].js', + }, +} + +export default [mainConfig, injectionConfig, multiDomainConfig] diff --git a/packages/web-config/webpack.config.base.ts b/packages/web-config/webpack.config.base.ts index 51486df217f6..746e8a9fa85a 100644 --- a/packages/web-config/webpack.config.base.ts +++ b/packages/web-config/webpack.config.base.ts @@ -97,6 +97,12 @@ function makeSassLoaders ({ modules }): RuleSetRule { } } +// the chrome version should be synced with +// npm/webpack-batteries-included-preprocessor/index.js and +// packages/server/lib/browsers/chrome.ts +const babelPresetEnvConfig = [require.resolve('@babel/preset-env'), { targets: { 'chrome': '64' } }] +const babelPresetTypeScriptConfig = [require.resolve('@babel/preset-typescript'), { allowNamespaces: true }] + export const getCommonConfig = () => { const commonConfig: Configuration = { mode: 'none', @@ -128,12 +134,9 @@ export const getCommonConfig = () => { [require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }], ], presets: [ - // the chrome version should be synced with - // npm/webpack-batteries-included-preprocessor/index.js and - // packages/server/lib/browsers/chrome.ts - [require.resolve('@babel/preset-env'), { targets: { 'chrome': '64' } }], + babelPresetEnvConfig, require.resolve('@babel/preset-react'), - [require.resolve('@babel/preset-typescript'), { allowNamespaces: true }], + babelPresetTypeScriptConfig, ], babelrc: false, }, @@ -216,8 +219,15 @@ export const getCommonConfig = () => { // eslint-disable-next-line @cypress/dev/arrow-body-multiline-braces export const getSimpleConfig = () => ({ + node: { + fs: 'empty', + child_process: 'empty', + net: 'empty', + tls: 'empty', + module: 'empty', + }, resolve: { - extensions: ['.js'], + extensions: ['.js', '.ts', '.json'], }, stats, @@ -228,18 +238,37 @@ export const getSimpleConfig = () => ({ module: { rules: [ { - test: /\.(js)$/, + test: /\.(js|ts)$/, exclude: /node_modules/, use: { loader: require.resolve('babel-loader'), options: { + plugins: [ + [require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }], + ], presets: [ - [require.resolve('@babel/preset-env'), { targets: { 'chrome': 63 } }], + babelPresetEnvConfig, + babelPresetTypeScriptConfig, ], babelrc: false, }, }, }, + // FIXME: we don't actually want or need wasm support in the + // multidomain bundle that uses this config, but we need to refactor + // the driver so that it doesn't load the wasm code in + // packages/driver/src/cypress/source_map_utils.js when creating + // the multidomain bundle. for now, this is necessary so the build + // doesn't fail + { + test: /\.wasm$/, + type: 'javascript/auto', + use: [ + { + loader: require.resolve('arraybuffer-loader'), + }, + ], + }, ], },