Skip to content

Commit

Permalink
fix(breaking_change): default to headless and 1280x720 for cypress ru…
Browse files Browse the repository at this point in the history
…n in all browsers (#17309)

Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
  • Loading branch information
kuceb and jennifer-shehane authored Jul 19, 2021
1 parent 8cef948 commit 8a48488
Show file tree
Hide file tree
Showing 17 changed files with 90 additions and 57 deletions.
4 changes: 2 additions & 2 deletions cli/__snapshots__/cli_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ exports['shows help for run --foo 1'] = `
-e, --env <env> sets environment variables. separate multiple values with a comma. overrides any value in cypress.json or cypress.env.json
--group <name> a named group for recorded runs in the Cypress Dashboard
-k, --key <record-key> your secret Record Key. you can omit this if you set a CYPRESS_RECORD_KEY environment variable.
--headed displays the browser instead of running headlessly (defaults to true for Firefox and Chromium-family browsers)
--headless hide the browser instead of running headed (defaults to true for Electron)
--headed displays the browser instead of running headlessly
--headless hide the browser instead of running headed (default for cypress run)
--no-exit keep the browser open after tests finish
--parallel enables concurrent runs and automatic load balancing of specs across multiple machines or processes
-p, --port <port> runs Cypress on a specific port. overrides any value in cypress.json.
Expand Down
4 changes: 2 additions & 2 deletions cli/lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ const descriptions = {
forceInstall: 'force install the Cypress binary',
global: 'force Cypress into global mode as if its globally installed',
group: 'a named group for recorded runs in the Cypress Dashboard',
headed: 'displays the browser instead of running headlessly (defaults to true for Firefox and Chromium-family browsers)',
headless: 'hide the browser instead of running headed (defaults to true for Electron)',
headed: 'displays the browser instead of running headlessly',
headless: 'hide the browser instead of running headed (default for cypress run)',
key: 'your secret Record Key. you can omit this if you set a CYPRESS_RECORD_KEY environment variable.',
parallel: 'enables concurrent runs and automatic load balancing of specs across multiple machines or processes',
port: 'runs Cypress on a specific port. overrides any value in cypress.json.',
Expand Down
25 changes: 14 additions & 11 deletions packages/driver/cypress/integration/issues/1939_1940_2190_spec.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
beforeEach(() => {
Cypress.config('retries', 0)
})

const autIframeHasFocus = () => Object.getOwnPropertyDescriptor(top.Document.prototype, 'hasFocus').value.call(top.frames[1].document)

// https://github.com/cypress-io/cypress/issues/1939
it('has focus when running headlessly in electron', (done) => {
it('has focus when running headlessly', () => {
if (Cypress.browser.isHeadless) {
// top (aka Cypress frame) should always be in focus
// when running headlessly. if we aren't running headlessly
// it may not be in focus if the user has clicked away.
// we don't want this test to potentially fail in that case
expect(top.document.hasFocus()).to.be.true
done()
} else {
// else done and making sure only 2 path options are here
done()
}
})

Expand All @@ -19,6 +21,10 @@ it('sets the AUT document.hasFocus to top.document.hasFocus', () => {
// the top does.
cy.visit('/timeout')
.then(() => {
if (Cypress.browser.isHeadless) {
return cy.document().invoke('hasFocus').should('be.true')
}

if (top.document.hasFocus()) {
return cy.document().invoke('hasFocus').should('be.true')
}
Expand All @@ -27,7 +33,7 @@ it('sets the AUT document.hasFocus to top.document.hasFocus', () => {
})
})

it('continues to have focus through top navigations', (done) => {
it('continues to have focus through top navigation', () => {
cy
.visit('http://localhost:3501/fixtures/generic.html')
.then(() => {
Expand All @@ -36,11 +42,8 @@ it('continues to have focus through top navigations', (done) => {
// when running headlessly. if we aren't running headlessly
// it may not be in focus if the user has clicked away.
// we don't want this test to potentially fail in that case
expect(top.document.hasFocus()).to.be.true
done()
} else {
// else done and making sure only 2 path options are here
done()
// it's OK if the autIframe has focus too, since that means the window still has focus
expect(top.document.hasFocus() || autIframeHasFocus()).to.be.true
}
})
})
13 changes: 8 additions & 5 deletions packages/driver/cypress/support/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ const { $ } = Cypress

let isActuallyInteractive

isActuallyInteractive = Cypress.config('isInteractive')
if (!isActuallyInteractive) {
// we want to only enable retries in runMode
// and because we set `isInteractive` above
// we have to set retries here
Cypress.config('retries', 2)
}

beforeEach(() => {
isActuallyInteractive = Cypress.config('isInteractive')

Expand All @@ -14,11 +22,6 @@ beforeEach(() => {
// necessary or else snapshots will not be taken
// and we can't test them
Cypress.config('numTestsKeptInMemory', 1)

// we want to only enable retries in runMode
// and because we set `isInteractive` above
// we have to set retries here
Cypress.config('retries', 2)
}

// remove all event listeners
Expand Down
13 changes: 13 additions & 0 deletions packages/driver/src/cypress.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,19 @@ class $Cypress {
}
}))
})
.then(() => {
// in order to utilize focusmanager.testingmode and trick browser into being in focus even when not focused
// this is critical for headless mode since otherwise the browser never gains focus
if (this.browser.isHeadless && this.isBrowser({ family: 'firefox' })) {
window.addEventListener('blur', () => {
this.backend('firefox:window:focus')
})

if (!document.hasFocus()) {
return this.backend('firefox:window:focus')
}
}
})
.then(() => {
this.cy.initialize(this.$autIframe)

Expand Down
2 changes: 1 addition & 1 deletion packages/server/__snapshots__/3_config_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ exports['e2e config applies defaultCommandTimeout globally 1'] = `
(Screenshots)
- /XXX/XXX/XXX/cypress/screenshots/dom_times_out_spec.js/short defaultCommandTimeo (1920x1080)
- /XXX/XXX/XXX/cypress/screenshots/dom_times_out_spec.js/short defaultCommandTimeo (1280x720)
ut -- times out looking for a missing element (failed).png
Expand Down
4 changes: 2 additions & 2 deletions packages/server/lib/browsers/chrome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,9 +426,9 @@ export = {
if (isHeadless) {
args.push('--headless')

// set default headless size to 1920x1080
// set default headless size to 1280x720
// https://github.com/cypress-io/cypress/issues/6210
args.push('--window-size=1920,1080')
args.push('--window-size=1280,720')

// set default headless DPR to 1
// https://github.com/cypress-io/cypress/issues/17375
Expand Down
27 changes: 22 additions & 5 deletions packages/server/lib/browsers/firefox-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ let timings = {
collections: [] as any[],
}

let driver

const sendMarionette = (data) => {
return driver.send(new Command(data))
}

const getTabId = (tab) => {
return _.get(tab, 'browsingContextID')
}
Expand Down Expand Up @@ -254,15 +260,11 @@ export default {
getDelayMsForRetry,
})

const driver = new Marionette.Drivers.Promises({
driver = new Marionette.Drivers.Promises({
port,
tries: 1, // marionette-client has its own retry logic which we want to avoid
})

const sendMarionette = (data) => {
return driver.send(new Command(data))
}

debug('firefox: navigating page with webdriver')

const onError = (from, reject?) => {
Expand Down Expand Up @@ -315,4 +317,19 @@ export default {
// even though Marionette is not used past this point, we have to keep the session open
// or else `acceptInsecureCerts` will cease to apply and SSL validation prompts will appear.
},

async windowFocus () {
// in order to utilize focusmanager.testingmode and trick browser into being in focus even when not focused
// this is critical for headless mode since otherwise the browser never gains focus
return sendMarionette({
name: 'WebDriver:ExecuteScript',
parameters: {
'args': [],
'script': `return (() => {
top.focus()
}).apply(null, arguments)\
`,
},
})
},
}
6 changes: 3 additions & 3 deletions packages/server/lib/browsers/firefox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,10 +503,10 @@ export async function open (browser: Browser, url, options: any = {}, automation
debug('launch in firefox', { url, args: launchOptions.args })

const browserInstance = await launch(browser, 'about:blank', launchOptions.args, {
// sets headless resolution to 1920x1080 by default
// sets headless resolution to 1280x720 by default
// user can overwrite this default with these env vars or --height, --width arguments
MOZ_HEADLESS_WIDTH: '1920',
MOZ_HEADLESS_HEIGHT: '1081',
MOZ_HEADLESS_WIDTH: '1280',
MOZ_HEADLESS_HEIGHT: '721',
})

try {
Expand Down
9 changes: 2 additions & 7 deletions packages/server/lib/modes/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -543,8 +543,8 @@ const getChromeProps = (writeVideoFrame) => {
const getElectronProps = (isHeaded, writeVideoFrame, onError) => {
return _
.chain({
width: 1920,
height: 1080,
width: 1280,
height: 720,
show: isHeaded,
onCrashed () {
const err = errors.get('RENDERER_CRASHED')
Expand Down Expand Up @@ -1259,11 +1259,6 @@ module.exports = {
},

runSpecs (options = {}) {
_.defaults(options, {
// only non-Electron browsers run headed by default
headed: options.browser.name !== 'electron',
})

const { config, browser, sys, headed, outputPath, specs, specPattern, beforeSpecRun, afterSpecRun, runUrl, parallel, group, tag, testingType } = options

const isHeadless = !headed
Expand Down
2 changes: 2 additions & 0 deletions packages/server/lib/socket-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@ export class SocketBase {
return firefoxUtil.log()
case 'firefox:force:gc':
return firefoxUtil.collectGarbage()
case 'firefox:window:focus':
return firefoxUtil.windowFocus()
case 'get:fixture':
return getFixture(args[0], args[1])
case 'read:file':
Expand Down
4 changes: 2 additions & 2 deletions packages/server/test/integration/cypress_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1048,8 +1048,8 @@ describe('lib/cypress', () => {
// when we work with the browsers we set a few extra flags
const chrome = _.find(TYPICAL_BROWSERS, { name: 'chrome' })
const launchedChrome = R.merge(chrome, {
isHeadless: false,
isHeaded: true,
isHeadless: true,
isHeaded: false,
})

expect(args[0], 'found and used Chrome').to.deep.eq(launchedChrome)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe('windowSize', () => {
it('spawns with correct default size', () => {
// assert the browser was spawned at 1920x1080 and is full size
// assert the browser was spawned at 1280x720 and is full size
// normally e2e tests spawn at fixed size, but this spec should be spawned without passing any width/height arguments in plugins file.
// TODO: look into fixing screen/available height and width
expect({
Expand All @@ -11,12 +11,12 @@ describe('windowSize', () => {
// availWidth: top.screen.availWidth,
// availHeight: top.screen.availHeight,
}).deep.eq({
innerWidth: 1920,
innerHeight: 1080,
// screenWidth: 1920,
// screenHeight: 1080,
// availWidth: 1920,
// availHeight: 1080,
innerWidth: 1280,
innerHeight: 720,
// screenWidth: 1280,
// screenHeight: 720,
// availWidth: 1280,
// availHeight: 720,
})
})
})
2 changes: 1 addition & 1 deletion packages/server/test/unit/browsers/chrome_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ describe('lib/browsers/chrome', () => {

expect(args).to.include.members([
'--headless',
'--window-size=1920,1080',
'--window-size=1280,720',
'--force-device-scale-factor=1',
])
})
Expand Down
4 changes: 2 additions & 2 deletions packages/server/test/unit/modes/run_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ describe('lib/modes/run', () => {
it('sets width and height', () => {
const props = runMode.getElectronProps()

expect(props.width).to.eq(1920)
expect(props.width).to.eq(1280)

expect(props.height).to.eq(1080)
expect(props.height).to.eq(720)
})

it('sets show to boolean', () => {
Expand Down
4 changes: 2 additions & 2 deletions scripts/ensure-dependencies.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/bin/bash
if [ $SKIP_DEPCHECK ]; then exit 0; fi

yarn check --integrity

if [ $? -ne 0 ];
then
if [ $? -ne 0 ]; then
echo 'Your dependencies are out of date; installing the correct dependencies...'
yarn
fi
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3431,7 +3431,7 @@

"@jest/types@^26.3.0", "@jest/types@^26.6.2":
version "26.6.2"
resolved "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e"
resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e"
integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==
dependencies:
"@types/istanbul-lib-coverage" "^2.0.0"
Expand Down Expand Up @@ -7614,7 +7614,7 @@

"@types/cheerio@*", "@types/cheerio@0.22.21":
version "0.22.21"
resolved "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.21.tgz#5e37887de309ba11b2e19a6e14cad7874b31a8a3"
resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.21.tgz#5e37887de309ba11b2e19a6e14cad7874b31a8a3"
integrity sha512-aGI3DfswwqgKPiEOTaiHV2ZPC9KEhprpgEbJnv0fZl3SGX0cGgEva1126dGrMC6AJM6v/aihlUgJn9M5DbDZ/Q==
dependencies:
"@types/node" "*"
Expand Down Expand Up @@ -7709,7 +7709,7 @@

"@types/enzyme@*", "@types/enzyme@3.10.5":
version "3.10.5"
resolved "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.5.tgz#fe7eeba3550369eed20e7fb565bfb74eec44f1f0"
resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.5.tgz#fe7eeba3550369eed20e7fb565bfb74eec44f1f0"
integrity sha512-R+phe509UuUYy9Tk0YlSbipRpfVtIzb/9BHn5pTEtjJTF5LXvUjrIQcZvNyANNEyFrd2YGs196PniNT1fgvOQA==
dependencies:
"@types/cheerio" "*"
Expand Down Expand Up @@ -31042,7 +31042,7 @@ pretty-error@^2.0.2, pretty-error@^2.1.1:

pretty-format@26.4.0, pretty-format@^24.9.0, pretty-format@^26.6.2:
version "26.4.0"
resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.0.tgz#c08073f531429e9e5024049446f42ecc9f933a3b"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.4.0.tgz#c08073f531429e9e5024049446f42ecc9f933a3b"
integrity sha512-mEEwwpCseqrUtuMbrJG4b824877pM5xald3AkilJ47Po2YLr97/siejYQHqj2oDQBeJNbu+Q0qUuekJ8F0NAPg==
dependencies:
"@jest/types" "^26.3.0"
Expand Down Expand Up @@ -35057,7 +35057,7 @@ socket.io-client@4.0.1:

socket.io-parser@4.0.4, socket.io-parser@~3.3.0, socket.io-parser@~3.4.0, socket.io-parser@~4.0.3, socket.io-parser@~4.0.4:
version "4.0.4"
resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz#9ea21b0d61508d18196ef04a2c6b9ab630f4c2b0"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.0.4.tgz#9ea21b0d61508d18196ef04a2c6b9ab630f4c2b0"
integrity sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==
dependencies:
"@types/component-emitter" "^1.2.10"
Expand Down

0 comments on commit 8a48488

Please sign in to comment.