diff --git a/cli/lib/exec/spawn.js b/cli/lib/exec/spawn.js index 5e5aaa053d52..72efa898e1df 100644 --- a/cli/lib/exec/spawn.js +++ b/cli/lib/exec/spawn.js @@ -141,9 +141,22 @@ module.exports = { stdioOptions.env.DISPLAY = process.env.DISPLAY } + if (process.env.ELECTRON_RUN_AS_NODE === 'true') { + debug('passing ELECTRON_RUN_AS_NODE=true') + stdioOptions.env.ELECTRON_RUN_AS_NODE = 'true' + + // Replace electron specific args with path to js entry point. + electronArgs.splice( + 0, + // Electron's command line switches come before the "--" delimiter. + electronArgs.indexOf('--') + 1, + // Replace package path with entry point path + path.join(state.getBinaryPkgPath(path.dirname(executable)), '..', 'index.js'), + ) + } + debug('spawning Cypress with executable: %s', executable) debug('spawn args %o %o', electronArgs, _.omit(stdioOptions, 'env')) - const child = cp.spawn(executable, electronArgs, stdioOptions) function resolveOn (event) { diff --git a/cli/lib/exec/xvfb.js b/cli/lib/exec/xvfb.js index bbc3de9851e3..dec857ee379f 100644 --- a/cli/lib/exec/xvfb.js +++ b/cli/lib/exec/xvfb.js @@ -58,6 +58,10 @@ module.exports = { }, isNeeded () { + if (process.env.ELECTRON_RUN_AS_NODE === 'true') { + return false // xvfb required for electron processes only. + } + if (os.platform() !== 'linux') { return false } diff --git a/packages/server/lib/ipc/ipc.js b/packages/server/lib/ipc/ipc.js index 169cd771c923..5ddcb2dfed17 100644 --- a/packages/server/lib/ipc/ipc.js +++ b/packages/server/lib/ipc/ipc.js @@ -1,9 +1,15 @@ -const { - contextBridge, - ipcRenderer, -} = require('electron') +const debug = require('debug')('cypress:server:ipc') -contextBridge.exposeInMainWorld('ipc', { - on: (...args) => ipcRenderer.on(...args), - send: (...args) => ipcRenderer.send(...args), -}) +try { + const { + contextBridge, + ipcRenderer, + } = require('electron') + + contextBridge.exposeInMainWorld('ipc', { + on: (...args) => ipcRenderer.on(...args), + send: (...args) => ipcRenderer.send(...args), + }) +} catch { + debug('running as a node process, not an electron process') +} diff --git a/packages/server/lib/modes/run.js b/packages/server/lib/modes/run.js index e4ca65816ff6..f00cf5501df6 100644 --- a/packages/server/lib/modes/run.js +++ b/packages/server/lib/modes/run.js @@ -1,6 +1,5 @@ /* eslint-disable no-console, @cypress/dev/arrow-body-multiline-braces */ const _ = require('lodash') -const { app } = require('electron') const la = require('lazy-ass') const pkg = require('@packages/root') const path = require('path') @@ -1652,14 +1651,20 @@ module.exports = { }, async run (options) { - // electron >= 5.0.0 will exit the app if all browserwindows are closed, - // this is obviously undesirable in run mode - // https://github.com/cypress-io/cypress/pull/4720#issuecomment-514316695 - app.on('window-all-closed', () => { - debug('all BrowserWindows closed, not exiting') - }) + try { + const { app } = require('electron') + + // electron >= 5.0.0 will exit the app if all browserwindows are closed, + // this is obviously undesirable in run mode + // https://github.com/cypress-io/cypress/pull/4720#issuecomment-514316695 + app.on('window-all-closed', () => { + debug('all BrowserWindows closed, not exiting') + }) - await app.whenReady() + await app.whenReady() + } catch { + debug('running as a node process, not an electron process') + } return this.ready(options) }, diff --git a/packages/server/package.json b/packages/server/package.json index e79e412b907c..cd8def97f3d9 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -187,7 +187,7 @@ "webpack": "4.43.0", "ws": "5.2.3", "xvfb": "cypress-io/node-xvfb#22e3783c31d81ebe64d8c0df491ea00cdc74726a", - "xvfb-maybe": "0.2.1" + "xvfb-maybe": "^0.2.1" }, "files": [ "config", diff --git a/packages/server/test/e2e/5_headless_spec.ts b/packages/server/test/e2e/5_headless_spec.ts index 40849000ad06..595f58bd575a 100644 --- a/packages/server/test/e2e/5_headless_spec.ts +++ b/packages/server/test/e2e/5_headless_spec.ts @@ -4,6 +4,30 @@ import Fixtures from '../support/helpers/fixtures' describe('e2e headless', function () { e2e.setup() + describe('ELECTRON_RUN_AS_NODE', () => { + beforeEach(function () { + process.env.ELECTRON_RUN_AS_NODE = 'true' + }) + + // cypress run --headless + e2e.it('tests in headless mode pass', { + spec: 'headless_spec.js', + config: { + env: { + 'CI': process.env.CI, + 'EXPECT_HEADLESS': '1', + }, + }, + headed: false, + snapshot: true, + browser: 'chrome', + }) + + after(function () { + process.env.ELECTRON_RUN_AS_NODE = '' + }) + }) + // cypress run --headless e2e.it('tests in headless mode pass', { spec: 'headless_spec.js',