mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-06 06:29:45 -06:00
fix(breaking_change): default to headless and 1280x720 for cypress run in all browsers (#17309)
Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
@@ -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(() => {
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
@@ -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?) => {
|
||||
@@ -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)\
|
||||
`,
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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')
|
||||
@@ -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
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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({
|
||||
@@ -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,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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',
|
||||
])
|
||||
})
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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
yarn.lock
10
yarn.lock
@@ -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"
|
||||
@@ -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" "*"
|
||||
@@ -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" "*"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user