diff --git a/cli/__snapshots__/cli_spec.js b/cli/__snapshots__/cli_spec.js index 475d78d3e274..2bf70683b40c 100644 --- a/cli/__snapshots__/cli_spec.js +++ b/cli/__snapshots__/cli_spec.js @@ -69,8 +69,8 @@ exports['shows help for run --foo 1'] = ` -e, --env sets environment variables. separate multiple values with a comma. overrides any value in cypress.json or cypress.env.json --group a named group for recorded runs in the Cypress Dashboard -k, --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 runs Cypress on a specific port. overrides any value in cypress.json. diff --git a/cli/lib/cli.js b/cli/lib/cli.js index 5c9a4042037f..a0237380c5ae 100644 --- a/cli/lib/cli.js +++ b/cli/lib/cli.js @@ -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.', diff --git a/cli/types/cypress-npm-api.d.ts b/cli/types/cypress-npm-api.d.ts index 9d0ed04c970e..1c44e841de8f 100644 --- a/cli/types/cypress-npm-api.d.ts +++ b/cli/types/cypress-npm-api.d.ts @@ -138,7 +138,7 @@ declare namespace CypressCommandLine { /** * Specify configuration */ - config: Partial + config: Cypress.ConfigOptions /** * Path to the config file to be used. * diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 8cfbcca83e48..9cb941fbca7f 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -64,6 +64,22 @@ declare namespace Cypress { path: string isHeaded: boolean isHeadless: boolean + /** + * Informational text to accompany this browser. Shown in desktop-gui. + */ + info?: string + /** + * Warning text to accompany this browser. Shown in desktop-gui. + */ + warning?: string + /** + * The minimum majorVersion of this browser supported by Cypress. + */ + minSupportedVersion?: number + /** + * If `true`, this browser is too old to be supported by Cypress. + */ + unsupportedVersion?: boolean } interface LocalStorage { @@ -341,15 +357,6 @@ declare namespace Cypress { */ env(object: ObjectLike): void - /** - * Firefox only: Get the current number of tests that will run between forced garbage collections. - * - * Returns undefined if not in Firefox, returns a null or 0 if forced GC is disabled. - * - * @see https://on.cypress.io/firefox-gc-issue - */ - getFirefoxGcInterval(): number | null | undefined - /** * @returns the number of test retries currently enabled for the run */ @@ -1833,6 +1840,12 @@ declare namespace Cypress { * @see https://on.cypress.io/then */ then(options: Partial, fn: (this: ObjectLike, currentSubject: Subject) => PromiseLike): Chainable + /** + * Enables you to work with the subject yielded from the previous command / promise. + * + * @see https://on.cypress.io/then + */ + then(fn: (this: ObjectLike, currentSubject: Subject) => S): Chainable /** * Enables you to work with the subject yielded from the previous command / promise. * @@ -1850,7 +1863,7 @@ declare namespace Cypress { * * @see https://on.cypress.io/then */ - then(fn: (this: ObjectLike, currentSubject: Subject) => S): Chainable + then(fn: (this: ObjectLike, currentSubject: Subject) => S): Chainable /** * Enables you to work with the subject yielded from the previous command / promise. * @@ -2640,13 +2653,6 @@ declare namespace Cypress { * @default 'top' */ scrollBehavior: scrollBehaviorOptions - /** - * Firefox version 79 and below only: The number of tests that will run between forced garbage collections. - * If a number is supplied, it will apply to `run` mode and `open` mode. - * Set the interval to `null` or 0 to disable forced garbage collections. - * @default { runMode: 1, openMode: null } - */ - firefoxGcInterval: Nullable, openMode: Nullable }> /** * Allows listening to the `before:run`, `after:run`, `before:spec`, and `after:spec` events in the plugins file during interactive mode. * @default false @@ -2678,17 +2684,46 @@ declare namespace Cypress { */ includeShadowDom: boolean + /** + * The list of hosts to be blocked + */ + blockHosts: null | string | string[] + /** + * Path to folder containing component test files. + */ + componentFolder: false | string + /** + * A unique ID for the project used for recording + */ + projectId: null | string + /** + * Path to the support folder. + */ + supportFolder: string + /** + * Glob pattern to determine what test files to load. + */ + testFiles: string | string[] + /** + * The user agent the browser sends in all request headers. + */ + userAgent: null | string + /** + * Polyfills `window.fetch` to enable Network spying and stubbing + */ + experimentalFetchPolyfill: boolean + /** * Override default config options for Component Testing runner. * @default {} */ - component: ResolvedConfigOptions + component: Omit /** * Override default config options for E2E Testing runner. * @default {} */ - e2e: ResolvedConfigOptions + e2e: Omit } /** @@ -2701,10 +2736,6 @@ declare namespace Cypress { * @see https://nodejs.org/api/os.html#os_os_arch */ arch: string - /** - * The list of hosts to be blocked - */ - blockHosts: null | string | string[] /** * The browser Cypress is running on. */ @@ -2713,10 +2744,6 @@ declare namespace Cypress { * Available browsers found on your system. */ browsers: Browser[] - /** - * Path to folder containing component test files. - */ - componentFolder: string /** * Hosts mappings to IP addresses. */ @@ -2736,22 +2763,6 @@ declare namespace Cypress { * The platform Cypress is running on. */ platform: 'linux' | 'darwin' | 'win32' - /** - * A unique ID for the project used for recording - */ - projectId: null | string - /** - * Path to the support folder. - */ - supportFolder: string - /** - * Glob pattern to determine what test files to load. - */ - testFiles: string - /** - * The user agent the browser sends in all request headers. - */ - userAgent: null | string /** * The Cypress version being used. */ @@ -2798,7 +2809,8 @@ declare namespace Cypress { /** * All configuration items are optional. */ - type ConfigOptions = Partial + type CoreConfigOptions = Partial> + type ConfigOptions = CoreConfigOptions & {e2e?: CoreConfigOptions, component?: CoreConfigOptions } interface PluginConfigOptions extends ResolvedConfigOptions { /** diff --git a/cli/types/tests/cypress-tests.ts b/cli/types/tests/cypress-tests.ts index 0f43ad3e8b35..6421dc47cdc4 100644 --- a/cli/types/tests/cypress-tests.ts +++ b/cli/types/tests/cypress-tests.ts @@ -257,6 +257,15 @@ describe('then', () => { $p // $ExpectType JQuery }) }) + + // https://github.com/cypress-io/cypress/issues/16669 + it('any as default', () => { + cy.get('body') + .then(() => ({} as any)) + .then(v => { + v // $ExpectType any + }) + }) }) cy.wait(['@foo', '@bar']) diff --git a/packages/desktop-gui/cypress/fixtures/config.json b/packages/desktop-gui/cypress/fixtures/config.json index 601a2c7a7ecd..c7f349d5005f 100644 --- a/packages/desktop-gui/cypress/fixtures/config.json +++ b/packages/desktop-gui/cypress/fixtures/config.json @@ -83,7 +83,8 @@ "channel": "stable", "version": "69.0.1", "path": "/Applications/Firefox/Contents/MacOS/Firefox", - "majorVersion": "69" + "majorVersion": "69", + "unsupportedVersion": true }, { "name": "firefox", diff --git a/packages/desktop-gui/cypress/integration/project_nav_spec.js b/packages/desktop-gui/cypress/integration/project_nav_spec.js index ee27737b99cf..378ab6cece37 100644 --- a/packages/desktop-gui/cypress/integration/project_nav_spec.js +++ b/packages/desktop-gui/cypress/integration/project_nav_spec.js @@ -204,6 +204,20 @@ describe('Project Nav', function () { }) }) + it('has unsupportedVersions styled and unselectable', function () { + cy.get('.browsers-list .dropdown-chosen').click() + + cy.get('.browsers-list').find('.dropdown-menu') + .find('li').should('have.length', this.config.browsers.length - 1) + .contains('span', 'Firefox 69') + .should('have.class', 'unsupported-version') + .click() + + cy.get('.browsers-list .dropdown-menu').should('be.visible') + + cy.get('.browsers-list .dropdown-chosen').contains('Chromium') + }) + it('saves chosen browser in local storage', () => { expect(localStorage.getItem('chosenBrowser')).to.eq(JSON.stringify({ name: 'chromium', channel: 'stable' })) }) @@ -255,7 +269,7 @@ describe('Project Nav', function () { const browserArg = this.ipc.launchBrowser.getCall(0).args[0].browser expect(browserArg).to.have.keys([ - 'family', 'name', 'path', 'profilePath', 'version', 'majorVersion', 'displayName', 'info', 'isChosen', 'custom', 'warning', 'channel', + 'family', 'name', 'path', 'profilePath', 'version', 'majorVersion', 'displayName', 'info', 'isChosen', 'custom', 'warning', 'channel', 'unsupportedVersion', ]) expect(browserArg.path).to.include('/') @@ -364,6 +378,22 @@ describe('Project Nav', function () { }) }) + describe('when browser saved in local storage has an unsupported version', function () { + beforeEach(function () { + localStorage.setItem('chosenBrowser', JSON.stringify({ name: 'firefox', channel: 'stable' })) + + // sanity check: saved browser should be found in the config + expect(this.config.browsers.find((b) => b.name === 'firefox' && b.channel === 'stable' && b.unsupportedVersion)).to.exist + + this.openProject.resolve(this.config) + }) + + it('defaults to first browser', () => { + cy.get('.browsers-list .dropdown-chosen') + .should('contain', 'Chrome') + }) + }) + describe('only one browser available', function () { beforeEach(function () { this.config.browsers = [{ diff --git a/packages/desktop-gui/src/app/nav.scss b/packages/desktop-gui/src/app/nav.scss index 0f7e8f038df8..5c74a557e45d 100644 --- a/packages/desktop-gui/src/app/nav.scss +++ b/packages/desktop-gui/src/app/nav.scss @@ -236,6 +236,13 @@ li { padding: 9px 15px; white-space: nowrap; + + .unsupported-version { + color: #888; + img { + filter: opacity(0.5); + } + } } .dropdown-chosen { diff --git a/packages/desktop-gui/src/lib/browser-model.js b/packages/desktop-gui/src/lib/browser-model.js index 08483acf03b6..ab1fa5fd7cd9 100644 --- a/packages/desktop-gui/src/lib/browser-model.js +++ b/packages/desktop-gui/src/lib/browser-model.js @@ -12,6 +12,7 @@ export default class Browser { @observable info @observable custom @observable warning + @observable unsupportedVersion @observable isChosen = false constructor (browser) { @@ -26,5 +27,6 @@ export default class Browser { this.info = browser.info this.custom = browser.custom this.warning = browser.warning + this.unsupportedVersion = browser.unsupportedVersion } } diff --git a/packages/desktop-gui/src/project-nav/browsers.jsx b/packages/desktop-gui/src/project-nav/browsers.jsx index 359bda1d58ba..552ded691857 100644 --- a/packages/desktop-gui/src/project-nav/browsers.jsx +++ b/packages/desktop-gui/src/project-nav/browsers.jsx @@ -1,5 +1,6 @@ import React, { Component } from 'react' import { observer } from 'mobx-react' +import cs from 'classnames' import Tooltip from '@cypress/react-tooltip' import { BrowserIcon, Dropdown } from '@packages/ui-components' @@ -49,6 +50,8 @@ export default class Browsers extends Component { } _onSelect = (browser) => { + if (browser.unsupportedVersion) return true + this.props.project.setChosenBrowser(browser) } @@ -69,14 +72,14 @@ export default class Browsers extends Component { } return ( - <> + {icon}{' '} {prefixText}{' '} {browser.displayName}{' '} {browser.majorVersion} {this._info(browser)} {this._warn(browser)} - + ) } diff --git a/packages/desktop-gui/src/project/project-model.js b/packages/desktop-gui/src/project/project-model.js index c7732780bec2..a0f2cb233924 100644 --- a/packages/desktop-gui/src/project/project-model.js +++ b/packages/desktop-gui/src/project/project-model.js @@ -119,12 +119,16 @@ export default class Project { return _.filter(this.browsers, { isChosen: false }) } + @computed get supportedBrowsers () { + return _.filter(this.browsers, (browser) => !browser.unsupportedVersion) + } + @computed get chosenBrowser () { return _.find(this.browsers, { isChosen: true }) } @computed get defaultBrowser () { - return this.browsers[0] + return this.supportedBrowsers[0] } @computed get warnings () { @@ -173,7 +177,7 @@ export default class Project { // use a custom browser if one is supplied. or, if they already have // a browser chosen that's been saved in localStorage, then select that // otherwise just do the default. - const customBrowser = _.find(this.browsers, { custom: true }) + const customBrowser = _.find(this.supportedBrowsers, { custom: true }) if (customBrowser) { return this.setChosenBrowser(customBrowser, { save: false }) @@ -260,7 +264,7 @@ export default class Project { filter.name = ls } - const browser = _.find(this.browsers, filter) || this.defaultBrowser + const browser = _.find(this.supportedBrowsers, filter) || this.defaultBrowser this.setChosenBrowser(browser) } diff --git a/packages/driver/cypress/integration/commands/actions/click_spec.js b/packages/driver/cypress/integration/commands/actions/click_spec.js index a566f9544bdb..e2bbd0cc8c5c 100644 --- a/packages/driver/cypress/integration/commands/actions/click_spec.js +++ b/packages/driver/cypress/integration/commands/actions/click_spec.js @@ -4007,15 +4007,15 @@ describe('mouse state', () => { // TODO: add back assertion on Y values const coordsFirefox = { clientX: 494, - // clientY: 10, + clientY: 10, // layerX: 492, // layerY: 215, pageX: 494, pageY: 226, screenX: 494, - // screenY: 10, + screenY: 10, x: 494, - // y: 10, + y: 10, } let coords @@ -4030,8 +4030,7 @@ describe('mouse state', () => { } const mouseout = cy.stub().callsFake((e) => { - expect(_.toPlainObject(e)).to.containSubset({ - ...coords, + const exp = { altKey: false, bubbles: true, button: 0, @@ -4059,13 +4058,16 @@ describe('mouse state', () => { type: 'mouseout', view: cy.state('window'), // which: 0, - }) + } + + expect(_.pick(e, _.keys(exp))).to.containSubset(exp) + _.each(coords, (v, key) => expect(e[key], key).closeTo(v, 1)) e.target.removeEventListener('mouseout', mouseout) }).as('mouseout') + const mouseleave = cy.stub().callsFake((e) => { - expect(_.toPlainObject(e)).to.containSubset({ - ...coords, + const exp = { altKey: false, bubbles: false, button: 0, @@ -4094,13 +4096,16 @@ describe('mouse state', () => { type: 'mouseleave', view: cy.state('window'), // which: 0, - }) + } + + expect(_.pick(e, _.keys(exp))).to.containSubset(exp) + _.each(coords, (v, key) => expect(e[key], key).closeTo(v, 1)) e.target.removeEventListener('mouseleave', mouseleave) }).as('mouseleave') + const pointerout = cy.stub().callsFake((e) => { - expect(_.toPlainObject(e)).to.containSubset({ - ...coords, + const exp = { altKey: false, bubbles: true, button: -1, @@ -4129,13 +4134,15 @@ describe('mouse state', () => { type: 'pointerout', view: cy.state('window'), // which: 0, - }) + } + + expect(_.pick(e, _.keys(exp))).to.containSubset(exp) + _.each(coords, (v, key) => expect(e[key], key).closeTo(v, 1)) e.target.removeEventListener('pointerout', pointerout) }).as('pointerout') const pointerleave = cy.stub().callsFake((e) => { - expect(_.toPlainObject(e)).to.containSubset({ - ...coords, + const exp = { altKey: false, bubbles: false, button: -1, @@ -4164,13 +4171,15 @@ describe('mouse state', () => { type: 'pointerleave', view: cy.state('window'), // which: 0, - }) + } + + expect(_.pick(e, _.keys(exp))).to.containSubset(exp) + _.each(coords, (v, key) => expect(e[key], key).closeTo(v, 1)) e.target.removeEventListener('pointerleave', pointerleave) }).as('pointerleave') const mouseover = cy.stub().callsFake((e) => { - expect(_.toPlainObject(e)).to.containSubset({ - ...coords, + const exp = { altKey: false, bubbles: true, button: 0, @@ -4199,13 +4208,15 @@ describe('mouse state', () => { type: 'mouseover', view: cy.state('window'), // which: 0, - }) + } + + expect(_.pick(e, _.keys(exp))).to.containSubset(exp) + _.each(coords, (v, key) => expect(e[key], key).closeTo(v, 1)) e.target.removeEventListener('mouseover', mouseover) }).as('mouseover') const mouseenter = cy.stub().callsFake((e) => { - expect(_.toPlainObject(e)).to.containSubset({ - ...coords, + const exp = { altKey: false, bubbles: false, button: 0, @@ -4234,13 +4245,15 @@ describe('mouse state', () => { type: 'mouseenter', view: cy.state('window'), // which: 0, - }) + } + + expect(_.pick(e, _.keys(exp))).to.containSubset(exp) + _.each(coords, (v, key) => expect(e[key], key).closeTo(v, 1)) e.target.removeEventListener('mouseenter', mouseenter) }).as('mouseenter') const pointerover = cy.stub().callsFake((e) => { - expect(_.toPlainObject(e)).to.containSubset({ - ...coords, + const exp = { altKey: false, bubbles: true, button: -1, @@ -4269,13 +4282,15 @@ describe('mouse state', () => { type: 'pointerover', view: cy.state('window'), // which: 0, - }) + } + + expect(_.pick(e, _.keys(exp))).to.containSubset(exp) + _.each(coords, (v, key) => expect(e[key], key).closeTo(v, 1)) e.target.removeEventListener('pointerover', pointerover) }).as('pointerover') const pointerenter = cy.stub().callsFake((e) => { - expect(_.toPlainObject(e)).to.containSubset({ - ...coords, + const exp = { altKey: false, bubbles: false, button: -1, @@ -4304,7 +4319,10 @@ describe('mouse state', () => { type: 'pointerenter', view: cy.state('window'), // which: 0, - }) + } + + expect(_.pick(e, _.keys(exp))).to.containSubset(exp) + _.each(coords, (v, key) => expect(e[key], key).closeTo(v, 1)) e.target.removeEventListener('pointerenter', pointerenter) }).as('pointerenter') diff --git a/packages/driver/cypress/integration/dom/visibility_spec.ts b/packages/driver/cypress/integration/dom/visibility_spec.ts index 495efe3e06e3..9fc025fdbc5d 100644 --- a/packages/driver/cypress/integration/dom/visibility_spec.ts +++ b/packages/driver/cypress/integration/dom/visibility_spec.ts @@ -312,8 +312,8 @@ describe('src/cypress/dom/visibility', () => { `) this.$parentPointerEventsNone = add(`\ -
- parent pointer-events: none +
+ parent pointer-events: none
\ `) diff --git a/packages/driver/cypress/integration/issues/1939_1940_2190_spec.js b/packages/driver/cypress/integration/issues/1939_1940_2190_spec.js index f946173ca510..f8d99a121ddd 100644 --- a/packages/driver/cypress/integration/issues/1939_1940_2190_spec.js +++ b/packages/driver/cypress/integration/issues/1939_1940_2190_spec.js @@ -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 } }) }) diff --git a/packages/driver/cypress/integration/util/firefox_forced_gc_spec.ts b/packages/driver/cypress/integration/util/firefox_forced_gc_spec.ts deleted file mode 100644 index 7110e97b8c8c..000000000000 --- a/packages/driver/cypress/integration/util/firefox_forced_gc_spec.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { createIntervalGetter, install } from '../../../src/util/firefox_forced_gc' - -describe('driver/src/util/firefox_forced_gc', () => { - describe('#createIntervalGetter returns a function that', () => { - const run = (configObj) => { - const fakeCypress = { - config: (key) => { - return key ? configObj[key] : configObj - }, - browser: configObj.browser, - } - - // @ts-ignore - return createIntervalGetter(fakeCypress)() - } - - it('returns undefined if not in Firefox', () => { - expect(run({ - browser: { - family: 'chrome', - }, - })).to.be.undefined - }) - - it('returns a number if firefoxGcInterval is a plain number', () => { - expect(run({ - browser: { - family: 'firefox', - majorVersion: 79, - }, - firefoxGcInterval: 99, - })).to.eq(99) - }) - - it('returns null if firefoxGcInterval is null', () => { - expect(run({ - browser: { - family: 'firefox', - majorVersion: 79, - }, - firefoxGcInterval: null, - })).to.eq(null) - }) - - it('returns the appropriate interval for open mode', () => { - expect(run({ - browser: { - family: 'firefox', - majorVersion: 79, - }, - firefoxGcInterval: { - runMode: 10, - openMode: 20, - }, - isInteractive: true, - })).to.eq(20) - }) - - it('returns the appropriate interval for run mode', () => { - expect(run({ - browser: { - family: 'firefox', - majorVersion: 79, - }, - firefoxGcInterval: { - runMode: 10, - openMode: 20, - }, - isInteractive: false, - })).to.eq(10) - }) - - it('has been correctly mounted at Cypress.getFirefoxGcInterval', { - // @ts-ignore - firefoxGcInterval: 5, - }, () => { - const real = Cypress.getFirefoxGcInterval - const fake = createIntervalGetter(Cypress) - - // conditional, so it can pass in non-ff browsers - expect(real()).to.eq(fake()).and.eq(Cypress.isBrowser('firefox') && Cypress.browser.majorVersion < 80 ? 5 : undefined) - }) - }) - - describe('#install', () => { - let MockCypress: any - let commandStartFn: any - let testBeforeRunAsyncFn: any - - beforeEach(() => { - MockCypress = { - on: cy.stub().throws(), - emit: cy.stub().throws(), - browser: { - family: 'firefox', - majorVersion: 79, - }, - getFirefoxGcInterval: cy.stub().throws(), - backend: cy.stub().throws(), - } - - commandStartFn = testBeforeRunAsyncFn = undefined - - MockCypress.on.withArgs('command:start').callsFake((_, fn) => { - commandStartFn = fn - }) - - MockCypress.on.withArgs('test:before:run:async').callsFake((_, fn) => { - testBeforeRunAsyncFn = fn - }) - }) - - const fakeVisit = () => { - commandStartFn({ get: cy.stub().throws().withArgs('name').returns('visit') }) - } - - const fakeBeforeTestRun = (order) => { - return testBeforeRunAsyncFn({ order }) || Promise.resolve() - } - - it('registers no event handlers if not in Firefox', () => { - MockCypress.browser.family = 'chrome' - - install(MockCypress) - - expect(MockCypress.on).to.not.be.called - }) - - // @see https://github.com/cypress-io/cypress/issues/8241 - it('registers no event handlers if in Firefox >= 80', () => { - MockCypress.browser.majorVersion = 80 - - install(MockCypress) - - expect(MockCypress.on).to.not.be.called - }) - - it('triggers a forced GC correctly with interval = 1', () => { - MockCypress.getFirefoxGcInterval.returns(1) - - const forceGc = MockCypress.backend.withArgs('firefox:force:gc').resolves() - const emitBefore = MockCypress.emit.withArgs('before:firefox:force:gc').returns() - const emitAfter = MockCypress.emit.withArgs('after:firefox:force:gc').returns() - - install(MockCypress) - - return fakeBeforeTestRun(0).then(() => { - fakeVisit() - - return fakeBeforeTestRun(1) - }) - .then(() => { - expect(forceGc).to.be.calledOnce - expect(emitBefore).to.be.calledOnce - expect(emitAfter).to.be.calledOnce - }) - .then(() => { - return fakeBeforeTestRun(2) - }) - .then(() => { - return fakeBeforeTestRun(3) - }) - .then(() => { - expect(forceGc).to.be.calledOnce - expect(emitBefore).to.be.calledOnce - expect(emitAfter).to.be.calledOnce - - fakeVisit() - - return fakeBeforeTestRun(4) - }) - .then(() => { - expect(forceGc).to.be.calledTwice - expect(emitBefore).to.be.calledTwice - expect(emitAfter).to.be.calledTwice - }) - }) - - it('triggers a forced GC correctly with interval = 3', () => { - MockCypress.getFirefoxGcInterval.returns(3) - - const forceGc = MockCypress.backend.withArgs('firefox:force:gc').resolves() - const emitBefore = MockCypress.emit.withArgs('before:firefox:force:gc').returns() - const emitAfter = MockCypress.emit.withArgs('after:firefox:force:gc').returns() - - install(MockCypress) - - return fakeBeforeTestRun(0).then(() => { - return fakeBeforeTestRun(1) - }) - .then(() => { - expect(forceGc).to.not.be.called - expect(emitBefore).to.not.be.called - expect(emitAfter).to.not.be.called - - fakeVisit() - }) - .then(() => { - return fakeBeforeTestRun(2) - }) - .then(() => { - return fakeBeforeTestRun(3) - }) - .then(() => { - expect(forceGc).to.be.calledOnce - expect(emitBefore).to.be.calledOnce - expect(emitAfter).to.be.calledOnce - }) - }) - - it('does not trigger any forced GC with falsy interval', () => { - MockCypress.getFirefoxGcInterval.returns(false) - - const forceGc = MockCypress.backend.withArgs('firefox:force:gc').resolves() - const emitBefore = MockCypress.emit.withArgs('before:firefox:force:gc').returns() - const emitAfter = MockCypress.emit.withArgs('after:firefox:force:gc').returns() - - install(MockCypress) - - return fakeBeforeTestRun(0).then(() => { - return fakeBeforeTestRun(1) - }) - .then(() => { - expect(forceGc).to.not.be.called - expect(emitBefore).to.not.be.called - expect(emitAfter).to.not.be.called - - fakeVisit() - }) - .then(() => { - return fakeBeforeTestRun(2) - }) - .then(() => { - expect(forceGc).to.not.be.called - expect(emitBefore).to.not.be.called - expect(emitAfter).to.not.be.called - }) - }) - }) -}) diff --git a/packages/driver/cypress/support/defaults.js b/packages/driver/cypress/support/defaults.js index 0a6d725d77e3..7fdac3420dbe 100644 --- a/packages/driver/cypress/support/defaults.js +++ b/packages/driver/cypress/support/defaults.js @@ -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 diff --git a/packages/driver/src/cypress.js b/packages/driver/src/cypress.js index d29cd9f27d16..c3b479083c21 100644 --- a/packages/driver/src/cypress.js +++ b/packages/driver/src/cypress.js @@ -14,7 +14,6 @@ const $Commands = require('./cypress/commands') const $Cookies = require('./cypress/cookies') const $Cy = require('./cypress/cy') const $Events = require('./cypress/events') -const $FirefoxForcedGc = require('./util/firefox_forced_gc') const $Keyboard = require('./cy/keyboard') const $SetterGetter = require('./cypress/setter_getter') const $Log = require('./cypress/log') @@ -137,7 +136,6 @@ class $Cypress { this.originalConfig = _.cloneDeep(config) this.config = $SetterGetter.create(config) this.env = $SetterGetter.create(env) - this.getFirefoxGcInterval = $FirefoxForcedGc.createIntervalGetter(this) this.getTestRetries = function () { const testRetries = this.config('retries') @@ -213,8 +211,6 @@ class $Cypress { this.events.proxyTo(this.cy) - $FirefoxForcedGc.install(this) - $scriptUtils.runScripts(specWindow, scripts) .catch((error) => { this.runner.onSpecError('error')({ error }) @@ -230,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) diff --git a/packages/driver/src/util/firefox_forced_gc.ts b/packages/driver/src/util/firefox_forced_gc.ts deleted file mode 100644 index c8360d1d5c22..000000000000 --- a/packages/driver/src/util/firefox_forced_gc.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { isNumber, isNull } from 'lodash' - -function usingFirefoxWithGcBug (browser: Cypress.Browser) { - // @see https://github.com/cypress-io/cypress/issues/8241 - return browser.family === 'firefox' && browser.majorVersion < 80 -} - -export function createIntervalGetter (Cypress: Cypress.Cypress) { - return () => { - if (!usingFirefoxWithGcBug(Cypress.browser)) { - return undefined - } - - const intervals = Cypress.config('firefoxGcInterval') - - if (isNumber(intervals) || isNull(intervals)) { - return intervals - } - - // @ts-ignore - return intervals[Cypress.config('isInteractive') ? 'openMode' : 'runMode'] - } -} - -export function install (Cypress: Cypress.Cypress & EventEmitter) { - if (!usingFirefoxWithGcBug(Cypress.browser)) { - return - } - - let cyVisitedSinceLastGc = false - let testsSinceLastForcedGc = 0 - - Cypress.on('command:start', function (cmd) { - if (cmd.get('name') === 'visit') { - cyVisitedSinceLastGc = true - } - }) - - Cypress.on('test:before:run:async', function (testAttrs) { - const { order } = testAttrs - - testsSinceLastForcedGc++ - - // if this is the first test, or the last test didn't run a cy.visit... - if (order === 0 || !cyVisitedSinceLastGc) { - return - } - - const gcInterval = Cypress.getFirefoxGcInterval() - - cyVisitedSinceLastGc = false - - if (gcInterval && gcInterval > 0 && testsSinceLastForcedGc >= gcInterval) { - testsSinceLastForcedGc = 0 - Cypress.emit('before:firefox:force:gc', { gcInterval }) - - return Cypress.backend('firefox:force:gc').then(() => { - return Cypress.emit('after:firefox:force:gc', { gcInterval }) - }) - } - - return - }) -} diff --git a/packages/launcher/__snapshots__/browsers_spec.ts.js b/packages/launcher/__snapshots__/browsers_spec.ts.js index eace2c3c0871..605e5a08a07a 100644 --- a/packages/launcher/__snapshots__/browsers_spec.ts.js +++ b/packages/launcher/__snapshots__/browsers_spec.ts.js @@ -9,7 +9,8 @@ exports['browsers returns the expected list of browsers 1'] = [ "google-chrome", "chrome", "google-chrome-stable" - ] + ], + "minSupportedVersion": 64 }, { "name": "chromium", @@ -20,7 +21,8 @@ exports['browsers returns the expected list of browsers 1'] = [ "binary": [ "chromium-browser", "chromium" - ] + ], + "minSupportedVersion": 64 }, { "name": "chrome", @@ -28,7 +30,8 @@ exports['browsers returns the expected list of browsers 1'] = [ "channel": "beta", "displayName": "Chrome Beta", "versionRegex": {}, - "binary": "google-chrome-beta" + "binary": "google-chrome-beta", + "minSupportedVersion": 64 }, { "name": "chrome", @@ -36,7 +39,8 @@ exports['browsers returns the expected list of browsers 1'] = [ "channel": "canary", "displayName": "Canary", "versionRegex": {}, - "binary": "google-chrome-canary" + "binary": "google-chrome-canary", + "minSupportedVersion": 64 }, { "name": "firefox", @@ -44,7 +48,8 @@ exports['browsers returns the expected list of browsers 1'] = [ "channel": "stable", "displayName": "Firefox", "versionRegex": {}, - "binary": "firefox" + "binary": "firefox", + "minSupportedVersion": 86 }, { "name": "firefox", @@ -55,7 +60,8 @@ exports['browsers returns the expected list of browsers 1'] = [ "binary": [ "firefox-developer-edition", "firefox" - ] + ], + "minSupportedVersion": 86 }, { "name": "firefox", @@ -66,7 +72,8 @@ exports['browsers returns the expected list of browsers 1'] = [ "binary": [ "firefox-nightly", "firefox-trunk" - ] + ], + "minSupportedVersion": 86 }, { "name": "edge", @@ -77,7 +84,8 @@ exports['browsers returns the expected list of browsers 1'] = [ "binary": [ "edge", "microsoft-edge" - ] + ], + "minSupportedVersion": 79 }, { "name": "edge", @@ -85,7 +93,8 @@ exports['browsers returns the expected list of browsers 1'] = [ "channel": "canary", "displayName": "Edge Canary", "versionRegex": {}, - "binary": "edge-canary" + "binary": "edge-canary", + "minSupportedVersion": 79 }, { "name": "edge", @@ -93,7 +102,8 @@ exports['browsers returns the expected list of browsers 1'] = [ "channel": "beta", "displayName": "Edge Beta", "versionRegex": {}, - "binary": "edge-beta" + "binary": "edge-beta", + "minSupportedVersion": 79 }, { "name": "edge", @@ -104,6 +114,7 @@ exports['browsers returns the expected list of browsers 1'] = [ "binary": [ "edge-dev", "microsoft-edge-dev" - ] + ], + "minSupportedVersion": 79 } ] diff --git a/packages/launcher/__snapshots__/darwin_spec.ts.js b/packages/launcher/__snapshots__/darwin_spec.ts.js index 12ff0878dafa..9be90df64bf7 100644 --- a/packages/launcher/__snapshots__/darwin_spec.ts.js +++ b/packages/launcher/__snapshots__/darwin_spec.ts.js @@ -10,6 +10,7 @@ exports['darwin browser detection detects browsers as expected 1'] = [ "chrome", "google-chrome-stable" ], + "minSupportedVersion": 64, "path": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "version": "someVersion", "findAppParams": { @@ -29,6 +30,7 @@ exports['darwin browser detection detects browsers as expected 1'] = [ "chromium-browser", "chromium" ], + "minSupportedVersion": 64, "path": "/Applications/Chromium.app/Contents/MacOS/Chromium", "version": "someVersion", "findAppParams": { @@ -45,6 +47,7 @@ exports['darwin browser detection detects browsers as expected 1'] = [ "displayName": "Chrome Beta", "versionRegex": {}, "binary": "google-chrome-beta", + "minSupportedVersion": 64, "path": "/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta", "version": "someVersion", "findAppParams": { @@ -61,6 +64,7 @@ exports['darwin browser detection detects browsers as expected 1'] = [ "displayName": "Canary", "versionRegex": {}, "binary": "google-chrome-canary", + "minSupportedVersion": 64, "path": "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary", "version": "someVersion", "findAppParams": { @@ -77,6 +81,7 @@ exports['darwin browser detection detects browsers as expected 1'] = [ "displayName": "Firefox", "versionRegex": {}, "binary": "firefox", + "minSupportedVersion": 86, "path": "/Applications/Firefox.app/Contents/MacOS/firefox-bin", "version": "someVersion", "findAppParams": { @@ -96,6 +101,7 @@ exports['darwin browser detection detects browsers as expected 1'] = [ "firefox-developer-edition", "firefox" ], + "minSupportedVersion": 86, "path": "/Applications/Firefox Developer Edition.app/Contents/MacOS/firefox-bin", "version": "someVersion", "findAppParams": { @@ -115,6 +121,7 @@ exports['darwin browser detection detects browsers as expected 1'] = [ "firefox-nightly", "firefox-trunk" ], + "minSupportedVersion": 86, "path": "/Applications/Firefox Nightly.app/Contents/MacOS/firefox-bin", "version": "someVersion", "findAppParams": { @@ -134,6 +141,7 @@ exports['darwin browser detection detects browsers as expected 1'] = [ "edge", "microsoft-edge" ], + "minSupportedVersion": 79, "path": "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge", "version": "someVersion", "findAppParams": { @@ -150,6 +158,7 @@ exports['darwin browser detection detects browsers as expected 1'] = [ "displayName": "Edge Canary", "versionRegex": {}, "binary": "edge-canary", + "minSupportedVersion": 79, "path": "/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary", "version": "someVersion", "findAppParams": { @@ -166,6 +175,7 @@ exports['darwin browser detection detects browsers as expected 1'] = [ "displayName": "Edge Beta", "versionRegex": {}, "binary": "edge-beta", + "minSupportedVersion": 79, "path": "/Applications/Microsoft Edge Beta.app/Contents/MacOS/Microsoft Edge Beta", "version": "someVersion", "findAppParams": { @@ -185,6 +195,7 @@ exports['darwin browser detection detects browsers as expected 1'] = [ "edge-dev", "microsoft-edge-dev" ], + "minSupportedVersion": 79, "path": "/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev", "version": "someVersion", "findAppParams": { diff --git a/packages/launcher/__snapshots__/windows_spec.ts.js b/packages/launcher/__snapshots__/windows_spec.ts.js index 92070e8f8320..59264eb06d35 100644 --- a/packages/launcher/__snapshots__/windows_spec.ts.js +++ b/packages/launcher/__snapshots__/windows_spec.ts.js @@ -10,6 +10,7 @@ exports['windows browser detection detects browsers as expected 1'] = [ "chrome", "google-chrome-stable" ], + "minSupportedVersion": 64, "path": "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe", "version": "1.2.3", "findAppParams": { @@ -29,6 +30,7 @@ exports['windows browser detection detects browsers as expected 1'] = [ "chromium-browser", "chromium" ], + "minSupportedVersion": 64, "path": "C:/Program Files (x86)/Google/chrome-win32/chrome.exe", "version": "2.3.4", "findAppParams": { @@ -45,6 +47,7 @@ exports['windows browser detection detects browsers as expected 1'] = [ "displayName": "Chrome Beta", "versionRegex": {}, "binary": "google-chrome-beta", + "minSupportedVersion": 64, "path": "C:/Program Files (x86)/Google/Chrome Beta/Application/chrome.exe", "version": "6.7.8", "findAppParams": { @@ -61,6 +64,7 @@ exports['windows browser detection detects browsers as expected 1'] = [ "displayName": "Canary", "versionRegex": {}, "binary": "google-chrome-canary", + "minSupportedVersion": 64, "path": "C:/Users/flotwig/AppData/Local/Google/Chrome SxS/Application/chrome.exe", "version": "3.4.5", "findAppParams": { @@ -77,6 +81,7 @@ exports['windows browser detection detects browsers as expected 1'] = [ "displayName": "Firefox", "versionRegex": {}, "binary": "firefox", + "minSupportedVersion": 86, "path": "C:/Program Files/Mozilla Firefox/firefox.exe", "version": "72", "findAppParams": { @@ -96,6 +101,7 @@ exports['windows browser detection detects browsers as expected 1'] = [ "firefox-developer-edition", "firefox" ], + "minSupportedVersion": 86, "path": "C:/Program Files (x86)/Firefox Developer Edition/firefox.exe", "version": "73", "findAppParams": { @@ -115,6 +121,7 @@ exports['windows browser detection detects browsers as expected 1'] = [ "firefox-nightly", "firefox-trunk" ], + "minSupportedVersion": 86, "path": "C:/Program Files/Firefox Nightly/firefox.exe", "version": "74", "findAppParams": { @@ -134,6 +141,7 @@ exports['windows browser detection detects browsers as expected 1'] = [ "edge", "microsoft-edge" ], + "minSupportedVersion": 79, "path": "C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe", "version": "11", "findAppParams": { @@ -150,6 +158,7 @@ exports['windows browser detection detects browsers as expected 1'] = [ "displayName": "Edge Canary", "versionRegex": {}, "binary": "edge-canary", + "minSupportedVersion": 79, "path": "C:/Users/flotwig/AppData/Local/Microsoft/Edge SxS/Application/msedge.exe", "version": "14", "findAppParams": { @@ -166,6 +175,7 @@ exports['windows browser detection detects browsers as expected 1'] = [ "displayName": "Edge Beta", "versionRegex": {}, "binary": "edge-beta", + "minSupportedVersion": 79, "path": "C:/Program Files (x86)/Microsoft/Edge Beta/Application/msedge.exe", "version": "12", "findAppParams": { @@ -185,6 +195,7 @@ exports['windows browser detection detects browsers as expected 1'] = [ "edge-dev", "microsoft-edge-dev" ], + "minSupportedVersion": 79, "path": "C:/Program Files (x86)/Microsoft/Edge Dev/Application/msedge.exe", "version": "13", "findAppParams": { @@ -216,6 +227,7 @@ exports['windows browser detection detects local Firefox installs 1'] = [ "displayName": "Firefox", "versionRegex": {}, "binary": "firefox", + "minSupportedVersion": 86, "path": "C:/Users/flotwig/AppData/Local/Mozilla Firefox/firefox.exe", "version": "100", "findAppParams": { @@ -235,6 +247,7 @@ exports['windows browser detection detects local Firefox installs 1'] = [ "firefox-developer-edition", "firefox" ], + "minSupportedVersion": 86, "path": "C:/Users/flotwig/AppData/Local/Firefox Developer Edition/firefox.exe", "version": "300", "findAppParams": { @@ -254,6 +267,7 @@ exports['windows browser detection detects local Firefox installs 1'] = [ "firefox-nightly", "firefox-trunk" ], + "minSupportedVersion": 86, "path": "C:/Users/flotwig/AppData/Local/Firefox Nightly/firefox.exe", "version": "200", "findAppParams": { diff --git a/packages/launcher/lib/browsers.ts b/packages/launcher/lib/browsers.ts index 7ac8cb005729..6aa0dc750940 100644 --- a/packages/launcher/lib/browsers.ts +++ b/packages/launcher/lib/browsers.ts @@ -2,6 +2,13 @@ import { log } from './log' import * as cp from 'child_process' import { Browser, FoundBrowser } from './types' +// Chrome started exposing CDP 1.3 in 64 +const MIN_CHROME_VERSION = 64 +// Firefox started exposing CDP in 86 +const MIN_FIREFOX_VERSION = 86 +// Edge switched to Blink in 79 +const MIN_EDGE_VERSION = 79 + /** list of the browsers we can detect and use by default */ export const browsers: Browser[] = [ { @@ -11,6 +18,7 @@ export const browsers: Browser[] = [ displayName: 'Chrome', versionRegex: /Google Chrome (\S+)/m, binary: ['google-chrome', 'chrome', 'google-chrome-stable'], + minSupportedVersion: MIN_CHROME_VERSION, }, { name: 'chromium', @@ -20,6 +28,7 @@ export const browsers: Browser[] = [ displayName: 'Chromium', versionRegex: /Chromium (\S+)/m, binary: ['chromium-browser', 'chromium'], + minSupportedVersion: MIN_CHROME_VERSION, }, { name: 'chrome', @@ -28,6 +37,7 @@ export const browsers: Browser[] = [ displayName: 'Chrome Beta', versionRegex: /Google Chrome (\S+) beta/m, binary: 'google-chrome-beta', + minSupportedVersion: MIN_CHROME_VERSION, }, { name: 'chrome', @@ -36,6 +46,7 @@ export const browsers: Browser[] = [ displayName: 'Canary', versionRegex: /Google Chrome Canary (\S+)/m, binary: 'google-chrome-canary', + minSupportedVersion: MIN_CHROME_VERSION, }, { name: 'firefox', @@ -45,6 +56,7 @@ export const browsers: Browser[] = [ // Mozilla Firefox 70.0.1 versionRegex: /^Mozilla Firefox ([^\sab]+)$/m, binary: 'firefox', + minSupportedVersion: MIN_FIREFOX_VERSION, }, { name: 'firefox', @@ -55,6 +67,7 @@ export const browsers: Browser[] = [ versionRegex: /^Mozilla Firefox (\S+b\S*)$/m, // ubuntu PPAs install it as firefox binary: ['firefox-developer-edition', 'firefox'], + minSupportedVersion: MIN_FIREFOX_VERSION, }, { name: 'firefox', @@ -65,6 +78,7 @@ export const browsers: Browser[] = [ versionRegex: /^Mozilla Firefox (\S+a\S*)$/m, // ubuntu PPAs install it as firefox-trunk binary: ['firefox-nightly', 'firefox-trunk'], + minSupportedVersion: MIN_FIREFOX_VERSION, }, { name: 'edge', @@ -73,6 +87,7 @@ export const browsers: Browser[] = [ displayName: 'Edge', versionRegex: /Microsoft Edge (\S+)/m, binary: ['edge', 'microsoft-edge'], + minSupportedVersion: MIN_EDGE_VERSION, }, { name: 'edge', @@ -81,6 +96,7 @@ export const browsers: Browser[] = [ displayName: 'Edge Canary', versionRegex: /Microsoft Edge Canary (\S+)/m, binary: 'edge-canary', + minSupportedVersion: MIN_EDGE_VERSION, }, { name: 'edge', @@ -89,6 +105,7 @@ export const browsers: Browser[] = [ displayName: 'Edge Beta', versionRegex: /Microsoft Edge Beta (\S+)/m, binary: 'edge-beta', + minSupportedVersion: MIN_EDGE_VERSION, }, { name: 'edge', @@ -97,6 +114,7 @@ export const browsers: Browser[] = [ displayName: 'Edge Dev', versionRegex: /Microsoft Edge Dev (\S+)/m, binary: ['edge-dev', 'microsoft-edge-dev'], + minSupportedVersion: MIN_EDGE_VERSION, }, ] diff --git a/packages/launcher/lib/detect.ts b/packages/launcher/lib/detect.ts index 9d8531428f4c..83114d22d0f4 100644 --- a/packages/launcher/lib/detect.ts +++ b/packages/launcher/lib/detect.ts @@ -16,26 +16,32 @@ import { } from './types' import * as windowsHelper from './windows' -type HasVersion = { - version?: string - majorVersion?: string | number +type HasVersion = Partial & { + version: string name: string } export const setMajorVersion = (browser: T): T => { - let majorVersion = browser.majorVersion - - if (browser.version) { - majorVersion = parseInt(browser.version.split('.')[0]) || browser.version - log( - 'browser %s version %s major version %s', - browser.name, - browser.version, - majorVersion, - ) + const majorVersion = parseInt(browser.version.split('.')[0]) || browser.version + + const unsupportedVersion = browser.minSupportedVersion && majorVersion < browser.minSupportedVersion + + log( + 'browser %s version %s major version %s', + browser.name, + browser.version, + majorVersion, + unsupportedVersion, + ) + + const foundBrowser = extend({}, browser, { majorVersion }) + + if (unsupportedVersion) { + foundBrowser.unsupportedVersion = true + foundBrowser.warning = `Cypress does not support running ${browser.displayName} version ${majorVersion}. To use ${browser.displayName} with Cypress, install a version of ${browser.displayName} newer than or equal to ${browser.minSupportedVersion}.` } - return extend({}, browser, { majorVersion }) + return foundBrowser } type PlatformHelper = { @@ -102,6 +108,8 @@ function checkOneBrowser (browser: Browser): Promise { 'custom', 'warning', 'info', + 'minSupportedVersion', + 'unsupportedVersion', ]) const logBrowser = (props: any) => { @@ -125,21 +133,9 @@ function checkOneBrowser (browser: Browser): Promise { .then(pickBrowserProps) .then(tap(logBrowser)) .then((browser) => setMajorVersion(browser)) - .then(maybeSetFirefoxWarning) .catch(failed) } -export const firefoxGcWarning = 'This version of Firefox has a bug that causes excessive memory consumption and will cause your tests to run slowly. It is recommended to upgrade to Firefox 80 or newer. [Learn more.](https://docs.cypress.io/guides/references/configuration.html#firefoxGcInterval)' - -// @see https://github.com/cypress-io/cypress/issues/8241 -const maybeSetFirefoxWarning = (browser: FoundBrowser) => { - if (browser.family === 'firefox' && Number(browser.majorVersion) < 80) { - browser.warning = firefoxGcWarning - } - - return browser -} - /** returns list of detected browsers */ export const detect = (goalBrowsers?: Browser[]): Bluebird => { // we can detect same browser under different aliases @@ -192,18 +188,16 @@ export const detectByPath = ( const setCustomBrowserData = (browser: Browser, path: string, versionStr: string): FoundBrowser => { const version = helper.getVersionNumber(versionStr, browser) - let parsedBrowser = { + let parsedBrowser = extend({}, browser, { name: browser.name, displayName: `Custom ${browser.displayName}`, info: `Loaded from ${path}`, custom: true, path, version, - } - - parsedBrowser = setMajorVersion(parsedBrowser) + }) - return extend({}, browser, parsedBrowser) + return setMajorVersion(parsedBrowser) } const pathData = helper.getPathData(path) @@ -226,7 +220,6 @@ export const detectByPath = ( return setCustomBrowserData(browser, pathData.path, version) }) - .then(maybeSetFirefoxWarning) .catch((err: NotDetectedAtPathError) => { if (err.notDetectedAtPath) { throw err diff --git a/packages/launcher/lib/types.ts b/packages/launcher/lib/types.ts index f5d0a0799e3e..9b1dc888b079 100644 --- a/packages/launcher/lib/types.ts +++ b/packages/launcher/lib/types.ts @@ -43,6 +43,8 @@ export type Browser = { warning?: string /** optional info that will be shown in the GUI */ info?: string + /** if set, the majorVersion must be >= this to be run in Cypress */ + minSupportedVersion?: number } /** @@ -54,6 +56,7 @@ export type FoundBrowser = Omit & { majorVersion?: string /** is this a user-supplied browser? */ custom?: boolean + unsupportedVersion?: boolean } /** diff --git a/packages/launcher/test/unit/detect_spec.ts b/packages/launcher/test/unit/detect_spec.ts index 7fdfbcba5ff3..139d8c7728b4 100644 --- a/packages/launcher/test/unit/detect_spec.ts +++ b/packages/launcher/test/unit/detect_spec.ts @@ -1,5 +1,5 @@ require('../spec_helper') -import { firefoxGcWarning, detect, detectByPath, setMajorVersion } from '../../lib/detect' +import { detect, detectByPath, setMajorVersion } from '../../lib/detect' import { goalBrowsers } from '../fixtures' import { expect } from 'chai' import { utils } from '../../lib/utils' @@ -62,6 +62,21 @@ describe('browser detection', () => { // @ts-ignore expect(res.majorVersion).to.equal(foundBrowser.version) }) + + it('creates warning when version is unsupported', () => { + const foundBrowser = { + displayName: 'TestBro', + name: 'test browser', + version: '9000.1', + minSupportedVersion: 9001, + } + + const res = setMajorVersion(foundBrowser) + + // @ts-ignore + expect(res.warning).to.contain('does not support running TestBro version 9000') + .and.contain('TestBro newer than or equal to 9001') + }) }) context('#detectByPath', () => { @@ -143,18 +158,14 @@ describe('browser detection', () => { }) }) - // @see https://github.com/cypress-io/cypress/issues/8241 - it('adds warnings to Firefox versions less than 80', async () => { + it('creates warning when version is unsupported', async () => { execa.withArgs('/good-firefox', ['--version']) - .resolves({ stdout: 'Mozilla Firefox 80.0' }) + .resolves({ stdout: 'Mozilla Firefox 85.0' }) - execa.withArgs('/bad-firefox', ['--version']) - .resolves({ stdout: 'Mozilla Firefox 79.1' }) + const foundBrowser = await detectByPath('/good-firefox') - expect(await detectByPath('/good-firefox')).to.not.have.property('warning') - expect(await detectByPath('/bad-firefox')).to.include({ - warning: firefoxGcWarning, - }) + expect(foundBrowser.warning).to.contain('does not support running Custom Firefox version 85') + .and.contain('Firefox newer than or equal to 86') }) }) }) diff --git a/packages/launcher/test/unit/linux_spec.ts b/packages/launcher/test/unit/linux_spec.ts index 33eeeb43680d..a56a968d5acc 100644 --- a/packages/launcher/test/unit/linux_spec.ts +++ b/packages/launcher/test/unit/linux_spec.ts @@ -4,8 +4,7 @@ import _ from 'lodash' import * as linuxHelper from '../../lib/linux' import 'chai-as-promised' import { log } from '../log' -import { detect, firefoxGcWarning } from '../../lib/detect' -import { browsers } from '../../lib/browsers' +import { detect } from '../../lib/detect' import { goalBrowsers } from '../fixtures' import { expect } from 'chai' import { utils } from '../../lib/utils' @@ -49,7 +48,7 @@ describe('linux browser detection', () => { // https://github.com/cypress-io/cypress/pull/7039 it('sets profilePath on snapcraft chromium', () => { execa.withArgs('chromium', ['--version']) - .resolves({ stdout: 'Chromium 1.2.3 snap' }) + .resolves({ stdout: 'Chromium 64.2.3 snap' }) sinon.stub(os, 'platform').returns('linux') sinon.stub(os, 'homedir').returns('/home/foo') @@ -60,10 +59,11 @@ describe('linux browser detection', () => { name: 'chromium', family: 'chromium', displayName: 'Chromium', - majorVersion: 1, + majorVersion: 64, + minSupportedVersion: 64, path: 'chromium', profilePath: '/home/foo/snap/chromium/current', - version: '1.2.3', + version: '64.2.3', }) } @@ -93,18 +93,6 @@ describe('linux browser detection', () => { return linuxHelper.detect(goal).then(checkBrowser) }) - // @see https://github.com/cypress-io/cypress/issues/8241 - it('adds warnings to Firefox versions less than 80', async () => { - const goalFirefox = _.find(browsers, { binary: 'firefox' }) - - sinon.stub(os, 'platform').withArgs().returns('linux') - execa.withArgs('firefox', ['--version']).resolves({ stdout: 'Mozilla Firefox 79.1' }) - - expect((await detect([goalFirefox]))[0]).to.include({ - warning: firefoxGcWarning, - }) - }) - // despite using detect(), this test is in linux/spec instead of detect_spec because it is // testing side effects that occur within the Linux-specific detect function // https://github.com/cypress-io/cypress/issues/1400 diff --git a/packages/reporter/cypress/integration/forced_gc_spec.ts b/packages/reporter/cypress/integration/forced_gc_spec.ts deleted file mode 100644 index 86d9a95fbab6..000000000000 --- a/packages/reporter/cypress/integration/forced_gc_spec.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { EventEmitter } from 'events' -import { RootRunnable } from '../../src/runnables/runnables-store' - -describe('forced gc', () => { - let runner: EventEmitter - let runnables: RootRunnable - - beforeEach(() => { - cy.fixture('runnables').then((_runnables) => { - runnables = _runnables - }) - - runner = new EventEmitter() - - cy.visit('/').then((win) => { - win.render({ - runner, - spec: { - name: 'foo', - absolute: '/foo/bar', - relative: 'foo/bar', - }, - }) - }) - - cy.get('.reporter').then(() => { - runner.emit('runnables:ready', runnables) - runner.emit('reporter:start', {}) - }) - }) - - it('does not display the warning when interval is undefined', () => { - cy.get('.forced-gc-warning').should('not.exist') - }) - - describe('when interval is null or a number', () => { - beforeEach(() => { - runner.emit('before:firefox:force:gc', { gcInterval: null }) - }) - - it('expands on click', () => { - cy.contains('GC Interval').click() - cy.contains('Garbage Collection Interval').should('be.visible') - }) - - it('collapses on a second click', () => { - cy.contains('GC Interval').click().click() - cy.contains('Garbage Collection Interval').should('not.be.visible') - }) - - it('collapses on a clicking X', () => { - cy.contains('GC Interval').click() - cy.get('.forced-gc-warning .fa-times').click() - cy.contains('Garbage Collection Interval').should('not.be.visible') - }) - - it('opens links externally', () => { - cy.spy(runner, 'emit') - cy.contains('GC Interval').click() - cy.get('.forced-gc-warning a').each(($link) => { - cy.wrap($link).click() - cy.wrap(runner.emit).should('be.calledWith', 'external:open', $link.attr('href')) - }) - }) - }) - - describe('when interval is null', () => { - beforeEach(() => { - runner.emit('before:firefox:force:gc', { gcInterval: null }) - }) - - it('displays the warning with (disabled)', () => { - cy.get('.forced-gc-warning').should('be.visible') - cy.contains('GC Interval: disabled') - }) - - it('displays the full message with (disabled)', () => { - cy.contains('GC Interval').click() - cy.contains('Garbage Collection Interval: (disabled)').should('be.visible') - }) - }) - - describe('when interval is 0', () => { - beforeEach(() => { - runner.emit('before:firefox:force:gc', { gcInterval: 0 }) - }) - - it('displays the warning with (disabled)', () => { - cy.get('.forced-gc-warning').should('be.visible') - cy.contains('GC Interval: disabled') - }) - - it('displays the full message with (disabled)', () => { - cy.contains('GC Interval').click() - cy.contains('Garbage Collection Interval: (disabled)').should('be.visible') - }) - }) - - describe('when interval is greater than 0', () => { - beforeEach(() => { - runner.emit('before:firefox:force:gc', { gcInterval: 15 }) - }) - - it('displays the warning with duration and running message', () => { - cy.get('.forced-gc-warning').should('be.visible') - cy.contains('GC Duration: 0.00') - cy.contains('Running GC...') - - // ensure the page is loaded before taking snapshot - cy.contains('test 4').should('be.visible') - cy.percySnapshot() - }) - - it('displays the full message with (enabled)', () => { - cy.contains('GC Duration').click() - cy.contains('Garbage Collection Interval: (enabled)').should('be.visible') - }) - }) -}) diff --git a/packages/reporter/cypress/integration/unit/app_state_spec.ts b/packages/reporter/cypress/integration/unit/app_state_spec.ts index aa88263e8c83..4c43bdd0495c 100644 --- a/packages/reporter/cypress/integration/unit/app_state_spec.ts +++ b/packages/reporter/cypress/integration/unit/app_state_spec.ts @@ -141,28 +141,6 @@ describe('app state', () => { }) }) - context('#setForcingGc', () => { - it('sets forcingGc', () => { - const instance = new AppState() - - instance.setForcingGc(false) - expect(instance.forcingGc).to.be.false - instance.setForcingGc(true) - expect(instance.forcingGc).to.be.true - }) - }) - - context('#setFirefoxGcInterval', () => { - it('sets forcingGc', () => { - const instance = new AppState() - - instance.setFirefoxGcInterval(111) - expect(instance.firefoxGcInterval).to.eq(111) - instance.setFirefoxGcInterval(222) - expect(instance.firefoxGcInterval).to.eq(222) - }) - }) - context('#setStudioActive', () => { it('sets studioActive', () => { const instance = new AppState() diff --git a/packages/reporter/cypress/integration/unit/events_spec.ts b/packages/reporter/cypress/integration/unit/events_spec.ts index 919103691bc3..3c3c8320c021 100644 --- a/packages/reporter/cypress/integration/unit/events_spec.ts +++ b/packages/reporter/cypress/integration/unit/events_spec.ts @@ -25,8 +25,6 @@ type AppStateStub = AppState & { resume: SinonSpy end: SinonSpy temporarilySetAutoScrolling: SinonSpy - setFirefoxGcInterval: SinonSpy - setForcingGc: SinonSpy setStudioActive: SinonSpy stop: SinonSpy } @@ -39,8 +37,6 @@ const appStateStub = () => { resume: sinon.spy(), end: sinon.spy(), temporarilySetAutoScrolling: sinon.spy(), - setFirefoxGcInterval: sinon.spy(), - setForcingGc: sinon.spy(), setStudioActive: sinon.spy(), stop: sinon.spy(), } as AppStateStub @@ -194,28 +190,11 @@ describe('events', () => { expect(appState.temporarilySetAutoScrolling).to.have.been.calledWith(false) }) - it('sets firefoxGcInterval on the app state on reporter:start', () => { - runner.on.withArgs('reporter:start').callArgWith(1, { firefoxGcInterval: 111 }) - expect(appState.setFirefoxGcInterval).to.have.been.calledWith(111) - }) - it('sets studioActive on the app state on reporter:start', () => { runner.on.withArgs('reporter:start').callArgWith(1, { studioActive: true }) expect(appState.setStudioActive).to.have.been.calledWith(true) }) - it('sets forcingGc & firefoxGcInterval on the app state on before:firefox:force:gc', () => { - runner.on.withArgs('before:firefox:force:gc').callArgWith(1, { gcInterval: 222 }) - expect(appState.setFirefoxGcInterval).to.have.been.calledWith(222) - expect(appState.setForcingGc).to.have.been.calledWith(true) - }) - - it('sets forcingGc & firefoxGcInterval on the app state on after:firefox:force:gc', () => { - runner.on.withArgs('after:firefox:force:gc').callArgWith(1, { gcInterval: 333 }) - expect(appState.setFirefoxGcInterval).to.have.been.calledWith(333) - expect(appState.setForcingGc).to.have.been.calledWith(false) - }) - it('sets initial scrollTop on the scroller on reporter:start', () => { runner.on.withArgs('reporter:start').callArgWith(1, { scrollTop: 123 }) expect(runnablesStore.setInitialScrollTop).to.have.been.calledWith(123) diff --git a/packages/reporter/src/lib/app-state.ts b/packages/reporter/src/lib/app-state.ts index c6295cafa5bd..abb86f913e48 100644 --- a/packages/reporter/src/lib/app-state.ts +++ b/packages/reporter/src/lib/app-state.ts @@ -2,8 +2,6 @@ import _ from 'lodash' import { observable } from 'mobx' interface DefaultAppState { - forcingGc: boolean - firefoxGcInterval: number | null | undefined isPaused: boolean isRunning: boolean nextCommandName: string | null | undefined @@ -12,8 +10,6 @@ interface DefaultAppState { } const defaults: DefaultAppState = { - forcingGc: false, - firefoxGcInterval: undefined, isPaused: false, isRunning: false, nextCommandName: null, @@ -23,12 +19,10 @@ const defaults: DefaultAppState = { class AppState { @observable autoScrollingEnabled = true - @observable forcingGc = defaults.forcingGc @observable isPaused = defaults.isPaused @observable isRunning = defaults.isRunning @observable nextCommandName = defaults.nextCommandName @observable pinnedSnapshotId = defaults.pinnedSnapshotId - @observable firefoxGcInterval = defaults.firefoxGcInterval @observable studioActive = defaults.studioActive isStopped = false; @@ -59,14 +53,6 @@ class AppState { this._resetAutoScrolling() } - setForcingGc (forcingGc: boolean) { - this.forcingGc = forcingGc - } - - setFirefoxGcInterval (firefoxGcInterval: DefaultAppState['firefoxGcInterval']) { - this.firefoxGcInterval = firefoxGcInterval - } - temporarilySetAutoScrolling (isEnabled?: boolean | null) { if (isEnabled != null) { this.autoScrollingEnabled = isEnabled diff --git a/packages/reporter/src/lib/base.scss b/packages/reporter/src/lib/base.scss index 16a6457edd35..6f60ed3065e4 100644 --- a/packages/reporter/src/lib/base.scss +++ b/packages/reporter/src/lib/base.scss @@ -1,7 +1,5 @@ body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img,a img{border:none;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;} -@import './forced-gc-warning.scss'; - .reporter { background-color: #F6F6F6; bottom: 0; diff --git a/packages/reporter/src/lib/events.ts b/packages/reporter/src/lib/events.ts index b8bc05d32c25..31c5683386ff 100644 --- a/packages/reporter/src/lib/events.ts +++ b/packages/reporter/src/lib/events.ts @@ -34,7 +34,6 @@ export interface Events { interface StartInfo extends StatsStoreStartInfo { autoScrollingEnabled: boolean - firefoxGcInterval: number scrollTop: number studioActive: boolean } @@ -91,7 +90,6 @@ const events: Events = { runner.on('reporter:start', action('start', (startInfo: StartInfo) => { appState.temporarilySetAutoScrolling(startInfo.autoScrollingEnabled) - appState.setFirefoxGcInterval(startInfo.firefoxGcInterval) runnablesStore.setInitialScrollTop(startInfo.scrollTop) appState.setStudioActive(startInfo.studioActive) if (runnablesStore.hasTests) { @@ -135,16 +133,6 @@ const events: Events = { appState.pinnedSnapshotId = null })) - runner.on('before:firefox:force:gc', action('before:firefox:force:gc', ({ gcInterval }) => { - appState.setForcingGc(true) - appState.setFirefoxGcInterval(gcInterval) - })) - - runner.on('after:firefox:force:gc', action('after:firefox:force:gc', ({ gcInterval }) => { - appState.setForcingGc(false) - appState.setFirefoxGcInterval(gcInterval) - })) - localBus.on('resume', action('resume', () => { appState.resume() statsStore.resume() diff --git a/packages/reporter/src/lib/forced-gc-warning.scss b/packages/reporter/src/lib/forced-gc-warning.scss deleted file mode 100644 index 04f2dc75a5cf..000000000000 --- a/packages/reporter/src/lib/forced-gc-warning.scss +++ /dev/null @@ -1,79 +0,0 @@ -.forced-gc-warning { - background-color: $yellow-lightest; - padding: 1em; - bottom: 0; - left: 0; - width: 100%; - box-shadow: 0 -1px 3px #ccc; - border-right: 1px solid #ddd; - - p { - line-height: 1.5 !important; - } - - a.code-link { - &>code { - color: #1079c3; - background: #f7f7f7; - } - - &:hover, &:focus, &:active { - text-decoration: none; - - &>code { - background-color: #e5f1f6; - text-decoration: none; - } - } - - - } - - code { - background-color: #f9f2f4; - color: #c7254e; - border-radius: 2px; - letter-spacing: 0.5px; - padding: 2px 4px; - } - - >.gc-expando { - display: none; - - &.expanded { - display: block; - padding-bottom: 1em; - - .fa-times { - float: right; - font-size: 16px; - } - } - } - - .gc-status { - color: #888; - border-bottom: 1px dotted; - } - - >.gc-status-bar { - .status-text { - float: right; - } - - >.total-time { - >i { - margin-right: .3em; - } - } - } - - .clickable { - cursor: pointer; - opacity: .7; - - &:hover { - opacity: 1; - } - } -} diff --git a/packages/reporter/src/lib/forced-gc-warning.tsx b/packages/reporter/src/lib/forced-gc-warning.tsx deleted file mode 100644 index 4e8615aa99e4..000000000000 --- a/packages/reporter/src/lib/forced-gc-warning.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import { isUndefined, round } from 'lodash' -import { observer } from 'mobx-react' -import React from 'react' -import { AppState } from './app-state' -import { Events } from './events' - -export interface Props { - appState: Pick - events: Events -} - -interface State { - expanded: boolean -} - -@observer -class ForcedGcWarning extends React.Component { - gcStartMs: number | null = null - gcTotalMs: number = 0 - persisted = false - state: State - - constructor (props: Props) { - super(props) - this.state = { - expanded: false, - } - } - - _toggleExpando () { - this.setState({ expanded: !this.state.expanded }) - } - - _updateGcTimer () { - const { forcingGc } = this.props.appState - - if (!forcingGc) { - if (this.gcStartMs) { - const duration = Date.now() - this.gcStartMs - - this.gcStartMs = null - this.gcTotalMs += duration - } - } - - if (forcingGc && !this.gcStartMs) { - this.gcStartMs = Date.now() - } - } - - _renderDisabled () { - return ( -
-
-

- - Garbage Collection Interval: (disabled) - - this._toggleExpando()}> -

-
-

- Cypress can force Firefox to run Garbage Collection (GC) between tests by enabling: firefoxGcInterval -

-

- By default, firefoxGcInterval is only enabled in run mode. -

-

- Running GC prevents Firefox from running out of memory during longer test runs. -

-

- Learn more. -

-
-
-
this._toggleExpando()}> - - - GC Interval: disabled - -
-
- ) - } - - _handleLink = (e: React.MouseEvent) => { - if (!e.currentTarget || !e.currentTarget.href) { - return - } - - e.preventDefault() - this.props.events.emit('external:open', e.currentTarget.href) - } - - _renderForcedGcWarning () { - const { forcingGc } = this.props.appState - - return ( -
-
-
-

- - Garbage Collection Interval: (enabled) - - this._toggleExpando()}> -

-
-
-

- Cypress will force Firefox to run Garbage Collection (GC) between tests based on the value of: firefoxGcInterval -

-

- Running GC prevents Firefox from running out of memory during longer test runs. -

-

- Running GC is an expensive operation that can take up to a few seconds to complete. During this time Firefox may "freeze" and become unresponsive to user input. -

-

- To improve performance, you can try setting firefoxGcInterval to a higher value, which will result in running GC less frequently. -

-

- Learn more. -

-
-
-
this._toggleExpando()}> - - - GC Duration: {round(this.gcTotalMs / 1000, 2).toFixed(2)} - - - {forcingGc && - Running GC... - } -
-
- ) - } - - render () { - const { firefoxGcInterval } = this.props.appState - - if (isUndefined(firefoxGcInterval)) { - // we're either still loading or it is disabled - return null - } - - if (firefoxGcInterval === 0 || firefoxGcInterval == null) { - return this._renderDisabled() - } - - this._updateGcTimer() - - return this._renderForcedGcWarning() - } -} - -export default ForcedGcWarning diff --git a/packages/reporter/src/main.tsx b/packages/reporter/src/main.tsx index dd1765fc6633..8b2f127503ed 100644 --- a/packages/reporter/src/main.tsx +++ b/packages/reporter/src/main.tsx @@ -11,7 +11,6 @@ import EQ from 'css-element-queries/src/ElementQueries' import { RunnablesErrorModel } from './runnables/runnable-error' import appState, { AppState } from './lib/app-state' import events, { Runner, Events } from './lib/events' -import ForcedGcWarning from './lib/forced-gc-warning' import runnablesStore, { RunnablesStore } from './runnables/runnables-store' import scroller, { Scroller } from './lib/scroller' import statsStore, { StatsStore } from './header/stats-store' @@ -84,7 +83,6 @@ class Reporter extends Component { runnablesStore, scroller, error, - events, statsStore, experimentalStudioEnabled, renderReporterHeader = (props: ReporterHeaderProps) =>
, @@ -115,11 +113,6 @@ class Reporter extends Component { spec={spec} /> ))} - -
) } diff --git a/packages/runner-ct/src/app/ReporterContainer.tsx b/packages/runner-ct/src/app/ReporterContainer.tsx index 4fbe03a8aa58..e885047e10da 100644 --- a/packages/runner-ct/src/app/ReporterContainer.tsx +++ b/packages/runner-ct/src/app/ReporterContainer.tsx @@ -39,7 +39,6 @@ export const ReporterContainer = namedObserver('ReporterContainer', specRunId={props.state.specRunId} allSpecs={props.state.multiSpecs} error={errorMessages.reporterError(props.state.scriptError, props.state.spec.relative)} - firefoxGcInterval={props.config.firefoxGcInterval} resetStatsOnSpecChange={props.state.runMode === 'single'} renderReporterHeader={renderReporterHeader} experimentalStudioEnabled={false} diff --git a/packages/runner-shared/src/event-manager.js b/packages/runner-shared/src/event-manager.js index 0e118ef4d88d..6cce44751a83 100644 --- a/packages/runner-shared/src/event-manager.js +++ b/packages/runner-shared/src/event-manager.js @@ -21,7 +21,7 @@ ws.on('connect', () => { ws.emit('runner:connected') }) -const driverToReporterEvents = 'paused before:firefox:force:gc after:firefox:force:gc'.split(' ') +const driverToReporterEvents = 'paused'.split(' ') const driverToLocalAndReporterEvents = 'run:start run:end'.split(' ') const driverToSocketEvents = 'backend:request automation:request mocha recorder:frame'.split(' ') const driverTestEvents = 'test:before:run:async test:after:run'.split(' ') @@ -497,7 +497,6 @@ export const eventManager = { }) reporterBus.emit('reporter:start', { - firefoxGcInterval: Cypress.getFirefoxGcInterval(), startTime: Cypress.runner.getStartTime(), numPassed: state.passed, numFailed: state.failed, diff --git a/packages/runner/cypress/integration/retries.mochaEvents.spec.js b/packages/runner/cypress/integration/retries.mochaEvents.spec.js index 89ebf87add60..18b3af745bd5 100644 --- a/packages/runner/cypress/integration/retries.mochaEvents.spec.js +++ b/packages/runner/cypress/integration/retries.mochaEvents.spec.js @@ -1,7 +1,7 @@ const helpers = require('../support/helpers') const { shouldHaveTestResults, getRunState, cleanseRunStateMap } = helpers -const { runIsolatedCypress, snapshotMochaEvents, getAutCypress } = helpers.createCypress({ config: { retries: 2, isTextTerminal: true, firefoxGcInterval: null } }) +const { runIsolatedCypress, snapshotMochaEvents, getAutCypress } = helpers.createCypress({ config: { retries: 2, isTextTerminal: true } }) const { sinon } = Cypress const match = Cypress.sinon.match diff --git a/packages/runner/src/app/app.jsx b/packages/runner/src/app/app.jsx index 0ddfad35da07..09725a7b3e35 100644 --- a/packages/runner/src/app/app.jsx +++ b/packages/runner/src/app/app.jsx @@ -87,7 +87,6 @@ class App extends Component { spec={spec} autoScrollingEnabled={this.props.config.state.autoScrollingEnabled} error={errorMessages.reporterError(this.props.state.scriptError, spec.relative)} - firefoxGcInterval={this.props.config.firefoxGcInterval} experimentalStudioEnabled={this.props.config.experimentalStudio} />} diff --git a/packages/runner/src/app/app.spec.jsx b/packages/runner/src/app/app.spec.jsx index 7a0ad675a728..69dc2030e836 100644 --- a/packages/runner/src/app/app.spec.jsx +++ b/packages/runner/src/app/app.spec.jsx @@ -87,15 +87,6 @@ describe('', () => { expect(component.find(Reporter)).to.have.prop('autoScrollingEnabled', true) }) - it('renders the with the firefoxGcInterval flag', () => { - const props = createProps() - - props.config.firefoxGcInterval = 111 - const component = shallowRender() - - expect(component.find(Reporter)).to.have.prop('firefoxGcInterval', 111) - }) - it('renders the runner container with `left` set as the width of the reporter', () => { const props = createProps() diff --git a/packages/server/__snapshots__/3_config_spec.js b/packages/server/__snapshots__/3_config_spec.js index 1f2601967821..9b4b71c67077 100644 --- a/packages/server/__snapshots__/3_config_spec.js +++ b/packages/server/__snapshots__/3_config_spec.js @@ -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 diff --git a/packages/server/lib/browsers/chrome.ts b/packages/server/lib/browsers/chrome.ts index 4ccebd83d798..0571acb957ae 100644 --- a/packages/server/lib/browsers/chrome.ts +++ b/packages/server/lib/browsers/chrome.ts @@ -426,9 +426,13 @@ 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 + args.push('--force-device-scale-factor=1') } // force ipv4 diff --git a/packages/server/lib/browsers/firefox-util.ts b/packages/server/lib/browsers/firefox-util.ts index 62d9d9750708..3a86846148f7 100644 --- a/packages/server/lib/browsers/firefox-util.ts +++ b/packages/server/lib/browsers/firefox-util.ts @@ -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)\ + `, + }, + }) + }, } diff --git a/packages/server/lib/browsers/firefox.ts b/packages/server/lib/browsers/firefox.ts index 7d97645d19e8..95a4358fefd2 100644 --- a/packages/server/lib/browsers/firefox.ts +++ b/packages/server/lib/browsers/firefox.ts @@ -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 { diff --git a/packages/server/lib/config_options.ts b/packages/server/lib/config_options.ts index 9205a4e51a91..9db7d0cb6d28 100644 --- a/packages/server/lib/config_options.ts +++ b/packages/server/lib/config_options.ts @@ -107,13 +107,6 @@ export const options = [ defaultValue: '', validation: v.isString, isFolder: true, - }, { - name: 'firefoxGcInterval', - defaultValue: { - runMode: 1, - openMode: null, - }, - validation: v.isValidFirefoxGcInterval, }, { name: 'fixturesFolder', defaultValue: 'cypress/fixtures', @@ -331,5 +324,9 @@ export const breakingOptions = [ name: 'experimentalShadowDomSupport', errorKey: 'EXPERIMENTAL_SHADOW_DOM_REMOVED', isWarning: true, + }, { + name: 'firefoxGcInterval', + errorKey: 'FIREFOX_GC_INTERVAL_REMOVED', + isWarning: true, }, ] diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 4285f607f57d..9de60099b5b4 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -940,6 +940,13 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { return stripIndent`\ The \`experimentalRunEvents\` configuration option was removed in Cypress version \`6.7.0\`. It is no longer necessary when listening to run events in the plugins file. + You can safely remove this option from your config.` + case 'FIREFOX_GC_INTERVAL_REMOVED': + return stripIndent`\ + The \`firefoxGcInterval\` configuration option was removed in Cypress version \`8.0.0\`. It was introduced to work around a bug in Firefox 79 and below. + + Since Cypress no longer supports Firefox 85 and below in Cypress 8, this option was removed. + You can safely remove this option from your config.` case 'INCOMPATIBLE_PLUGIN_RETRIES': return stripIndent`\ @@ -980,6 +987,8 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { https://on.cypress.io/component-testing ` + case 'UNSUPPORTED_BROWSER_VERSION': + return arg1 default: } } diff --git a/packages/server/lib/modes/run.js b/packages/server/lib/modes/run.js index 4902dee18d02..8f69f99cfa45 100644 --- a/packages/server/lib/modes/run.js +++ b/packages/server/lib/modes/run.js @@ -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 @@ -1552,7 +1547,7 @@ module.exports = { trashAssets(config), ]) .spread((sys = {}, browser = {}, specs = []) => { - // return only what is return to the specPattern + // return only what is return to the specPattern if (specPattern) { specPattern = specsUtil.getPatternRelativeToProjectRoot(specPattern, projectRoot) } @@ -1567,6 +1562,10 @@ module.exports = { } } + if (browser.unsupportedVersion && browser.warning) { + errors.throw('UNSUPPORTED_BROWSER_VERSION', browser.warning) + } + if (browser.family === 'chromium') { chromePolicyCheck.run(onWarning) } diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 350559778f2b..0ff3d0db4141 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -38,8 +38,8 @@ import { checkSupportFile } from './project_utils' // and are required when creating a project. // TODO: Figure out how to type this better. type ReceivedCypressOptions = - Partial> - & Partial> + Partial> + & Partial> export interface Cfg extends ReceivedCypressOptions { projectRoot: string @@ -678,7 +678,7 @@ export class ProjectBase extends EE { return { ...browser, - warning: errors.getMsgByType('CHROME_WEB_SECURITY_NOT_SUPPORTED', browser.name), + warning: browser.warning || errors.getMsgByType('CHROME_WEB_SECURITY_NOT_SUPPORTED', browser.name), } }) } diff --git a/packages/server/lib/socket-base.ts b/packages/server/lib/socket-base.ts index 20d41fa42ac0..79ab5f2a5dae 100644 --- a/packages/server/lib/socket-base.ts +++ b/packages/server/lib/socket-base.ts @@ -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': diff --git a/packages/server/lib/util/validation.js b/packages/server/lib/util/validation.js index 51e11148019d..14830fe80218 100644 --- a/packages/server/lib/util/validation.js +++ b/packages/server/lib/util/validation.js @@ -121,25 +121,6 @@ const isValidRetriesConfig = (key, value) => { return errMsg(key, value, 'a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls') } -const isValidFirefoxGcInterval = (key, value) => { - const isIntervalValue = (val) => { - if (isNumber(val)) { - return val >= 0 - } - - return val == null - } - - if (isIntervalValue(value) - || (_.isEqual(_.keys(value), ['runMode', 'openMode']) - && isIntervalValue(value.runMode) - && isIntervalValue(value.openMode))) { - return true - } - - return errMsg(key, value, 'a positive number or null or an object with "openMode" and "runMode" as keys and positive numbers or nulls as values') -} - const isPlainObject = (key, value) => { if (value == null || _.isPlainObject(value)) { return true @@ -283,8 +264,6 @@ module.exports = { isValidBrowserList, - isValidFirefoxGcInterval, - isValidRetriesConfig, isValidConfig, diff --git a/packages/server/package.json b/packages/server/package.json index 1f2047756794..76c851cd64b0 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -185,7 +185,7 @@ "supertest-session": "4.0.0", "through2": "2.0.5", "ts-loader": "7.0.4", - "tsconfig-paths": "3.9.0", + "tsconfig-paths": "3.10.1", "webpack": "4.43.0", "ws": "5.2.3", "xvfb": "cypress-io/node-xvfb#22e3783c31d81ebe64d8c0df491ea00cdc74726a", diff --git a/packages/server/patches/tsconfig-paths+3.9.0.patch b/packages/server/patches/tsconfig-paths+3.10.1.patch similarity index 90% rename from packages/server/patches/tsconfig-paths+3.9.0.patch rename to packages/server/patches/tsconfig-paths+3.10.1.patch index ec902da9cac7..840801790eed 100644 --- a/packages/server/patches/tsconfig-paths+3.9.0.patch +++ b/packages/server/patches/tsconfig-paths+3.10.1.patch @@ -1,9 +1,9 @@ diff --git a/node_modules/tsconfig-paths/lib/register.js b/node_modules/tsconfig-paths/lib/register.js -index c12b996..8beea8c 100644 +index 311c7fd..ac9feec 100644 --- a/node_modules/tsconfig-paths/lib/register.js +++ b/node_modules/tsconfig-paths/lib/register.js @@ -51,7 +51,7 @@ function register(explicitParams) { - explicitParams: explicitParams + explicitParams: explicitParams, }); if (configLoaderResult.resultType === "failed") { - console.warn(configLoaderResult.message + ". tsconfig-paths will be skipped"); diff --git a/packages/server/test/e2e/5_headless_spec.ts b/packages/server/test/e2e/5_headless_spec.ts index 8366e021ac4c..264adffb61fd 100644 --- a/packages/server/test/e2e/5_headless_spec.ts +++ b/packages/server/test/e2e/5_headless_spec.ts @@ -84,4 +84,10 @@ describe('e2e headless', function () { project: Fixtures.projectPath('screen-size'), spec: 'default_size.spec.js', }) + + e2e.it('launches at DPR 1x', { + headed: false, + project: Fixtures.projectPath('screen-size'), + spec: 'device_pixel_ratio.spec.js', + }) }) diff --git a/packages/server/test/integration/cypress_spec.js b/packages/server/test/integration/cypress_spec.js index 6cd955fdbe93..c8ed17c1c7bc 100644 --- a/packages/server/test/integration/cypress_spec.js +++ b/packages/server/test/integration/cypress_spec.js @@ -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) diff --git a/packages/server/test/support/fixtures/projects/firefox-memory/cypress/integration/spec.js b/packages/server/test/support/fixtures/projects/firefox-memory/cypress/integration/spec.js index b82dce85a701..488076533ef9 100644 --- a/packages/server/test/support/fixtures/projects/firefox-memory/cypress/integration/spec.js +++ b/packages/server/test/support/fixtures/projects/firefox-memory/cypress/integration/spec.js @@ -19,18 +19,13 @@ function parse (obj) { const str = JSON.stringify(obj, [ 'usedJSHeapSize', 'totalJSHeapSize', - 'jsHeapSizeLimit', ]) return JSON.parse(str) } const stats = () => { - const { firefoxGcInterval, firefoxGcInOpenMode } = Cypress.config() - cy.task('console', { - firefoxGcInterval, - firefoxGcInOpenMode, numTests: NUM_TESTS, }) } diff --git a/packages/server/test/support/fixtures/projects/screen-size/cypress/integration/default_size.spec.js b/packages/server/test/support/fixtures/projects/screen-size/cypress/integration/default_size.spec.js index 4a0f893fa8c1..968669d3cb13 100644 --- a/packages/server/test/support/fixtures/projects/screen-size/cypress/integration/default_size.spec.js +++ b/packages/server/test/support/fixtures/projects/screen-size/cypress/integration/default_size.spec.js @@ -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, }) }) }) diff --git a/packages/server/test/support/fixtures/projects/screen-size/cypress/integration/device_pixel_ratio.spec.js b/packages/server/test/support/fixtures/projects/screen-size/cypress/integration/device_pixel_ratio.spec.js new file mode 100644 index 000000000000..2892299130a8 --- /dev/null +++ b/packages/server/test/support/fixtures/projects/screen-size/cypress/integration/device_pixel_ratio.spec.js @@ -0,0 +1,6 @@ +describe('devicePixelRatio', () => { + it('has DPR of 1', () => { + // assert the browser was spawned with DPR of 1 + expect(window.devicePixelRatio).to.equal(1) + }) +}) diff --git a/packages/server/test/support/fixtures/projects/screen-size/cypress/plugins/index.js b/packages/server/test/support/fixtures/projects/screen-size/cypress/plugins/index.js index c766ef4b2314..3f555935d8e7 100644 --- a/packages/server/test/support/fixtures/projects/screen-size/cypress/plugins/index.js +++ b/packages/server/test/support/fixtures/projects/screen-size/cypress/plugins/index.js @@ -4,6 +4,7 @@ module.exports = (on) => { on('before:browser:launch', (browser, options) => { // options.args.push('-width', '1280', '-height', '1024') + // options.args.push('--force-device-scale-factor=2') // return options }) diff --git a/packages/server/test/unit/browsers/chrome_spec.js b/packages/server/test/unit/browsers/chrome_spec.js index 355431af1704..da7398848c7b 100644 --- a/packages/server/test/unit/browsers/chrome_spec.js +++ b/packages/server/test/unit/browsers/chrome_spec.js @@ -99,7 +99,7 @@ describe('lib/browsers/chrome', () => { }) }) - it('sets default window size in headless mode', function () { + it('sets default window size and DPR in headless mode', function () { chrome._writeExtension.restore() return chrome.open({ isHeadless: true, isHeaded: false }, 'http://', {}, this.automation) @@ -108,7 +108,8 @@ describe('lib/browsers/chrome', () => { expect(args).to.include.members([ '--headless', - '--window-size=1920,1080', + '--window-size=1280,720', + '--force-device-scale-factor=1', ]) }) }) diff --git a/packages/server/test/unit/config_spec.js b/packages/server/test/unit/config_spec.js index 51d7baa382f9..c2733c2ece5c 100644 --- a/packages/server/test/unit/config_spec.js +++ b/packages/server/test/unit/config_spec.js @@ -853,38 +853,6 @@ describe('lib/config', () => { }) }) - context('firefoxGcInterval', () => { - it('passes if a number', function () { - this.setup({ firefoxGcInterval: 1 }) - - return this.expectValidationPasses() - }) - - it('passes if null', function () { - this.setup({ firefoxGcInterval: null }) - - return this.expectValidationPasses() - }) - - it('passes if correctly shaped object', function () { - this.setup({ firefoxGcInterval: { runMode: 1, openMode: null } }) - - return this.expectValidationPasses() - }) - - it('fails if string', function () { - this.setup({ firefoxGcInterval: 'foo' }) - - return this.expectValidationFails('a positive number or null or an object') - }) - - it('fails if invalid object', function () { - this.setup({ firefoxGcInterval: { foo: 'bar' } }) - - return this.expectValidationFails('a positive number or null or an object') - }) - }) - function pemCertificate () { return { clientCertificates: [ @@ -1433,6 +1401,16 @@ describe('lib/config', () => { expect(warning).to.be.calledWith('EXPERIMENTAL_NETWORK_STUBBING_REMOVED') }) + it('warns if firefoxGcInterval is passed', async function () { + const warning = sinon.spy(errors, 'warning') + + await this.defaults('firefoxGcInterval', true, { + firefoxGcInterval: true, + }) + + expect(warning).to.be.calledWith('FIREFOX_GC_INTERVAL_REMOVED') + }) + describe('.resolved', () => { it('sets reporter and port to cli', () => { const obj = { @@ -1465,7 +1443,6 @@ describe('lib/config', () => { experimentalSourceRewriting: { value: false, from: 'default' }, experimentalStudio: { value: false, from: 'default' }, fileServerFolder: { value: '', from: 'default' }, - firefoxGcInterval: { value: { openMode: null, runMode: 1 }, from: 'default' }, fixturesFolder: { value: 'cypress/fixtures', from: 'default' }, hosts: { value: null, from: 'default' }, ignoreTestFiles: { value: '*.hot-update.js', from: 'default' }, @@ -1571,7 +1548,6 @@ describe('lib/config', () => { }, }, fileServerFolder: { value: '', from: 'default' }, - firefoxGcInterval: { value: { openMode: null, runMode: 1 }, from: 'default' }, fixturesFolder: { value: 'cypress/fixtures', from: 'default' }, hosts: { value: null, from: 'default' }, ignoreTestFiles: { value: '*.hot-update.js', from: 'default' }, diff --git a/packages/server/test/unit/modes/run_spec.js b/packages/server/test/unit/modes/run_spec.js index 8083cde61f03..ce3a505d6e17 100644 --- a/packages/server/test/unit/modes/run_spec.js +++ b/packages/server/test/unit/modes/run_spec.js @@ -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', () => { @@ -701,6 +701,15 @@ describe('lib/modes/run', () => { .to.be.rejectedWith(/invalid browser family in/) }) + it('throws an error if unsupportedVersion', () => { + const browser = { displayName: 'SomeBrowser', warning: 'blah blah', unsupportedVersion: true } + + sinon.stub(browsers, 'ensureAndGetByNameOrPath').resolves(browser) + + return expect(runMode.run()) + .to.be.rejectedWith('blah blah') + }) + it('shows no warnings for chrome browser', () => { return runMode.run({ browser: 'chrome' }) .then(() => { diff --git a/packages/ui-components/src/dropdown.tsx b/packages/ui-components/src/dropdown.tsx index 34490ed65e49..f866ccf1d222 100644 --- a/packages/ui-components/src/dropdown.tsx +++ b/packages/ui-components/src/dropdown.tsx @@ -108,8 +108,10 @@ class Dropdown extends Component { } _onSelect (item: object) { - this.setState({ open: false }) - this.props.onSelect(item) + const retval = this.props.onSelect(item) + const open = _.isBoolean(retval) ? retval : false + + this.setState({ open }) } } diff --git a/scripts/ensure-dependencies.sh b/scripts/ensure-dependencies.sh index c5f217dd7101..aa0440fcba84 100755 --- a/scripts/ensure-dependencies.sh +++ b/scripts/ensure-dependencies.sh @@ -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 diff --git a/yarn.lock b/yarn.lock index eb10f68a916f..f7a62da0eb9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7917,11 +7917,6 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= - "@types/keyv@*": version "3.1.1" resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" @@ -24264,7 +24259,7 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.0, json5@^2.1.2, json5@^2.1.3: +json5@^2.1.0, json5@^2.1.2, json5@^2.1.3, json5@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== @@ -31047,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" @@ -37421,13 +37416,12 @@ tsconfig-paths-webpack-plugin@^3.3.0, tsconfig-paths-webpack-plugin@^3.5.1: enhanced-resolve "^5.7.0" tsconfig-paths "^3.9.0" -tsconfig-paths@3.9.0, tsconfig-paths@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" - integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== +tsconfig-paths@3.10.1, tsconfig-paths@^3.9.0: + version "3.10.1" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz#79ae67a68c15289fdf5c51cb74f397522d795ed7" + integrity sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q== dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.1" + json5 "^2.2.0" minimist "^1.2.0" strip-bom "^3.0.0" @@ -38961,7 +38955,7 @@ vue-style-loader@^4.1.0, vue-style-loader@^4.1.2: vue-template-compiler@2.6.12, vue-template-compiler@^2.6.11: version "2.6.12" - resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz#947ed7196744c8a5285ebe1233fe960437fcc57e" + resolved "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz#947ed7196744c8a5285ebe1233fe960437fcc57e" integrity sha512-OzzZ52zS41YUbkCBfdXShQTe69j1gQDZ9HIX8miuC9C3rBCk9wIRjLiZZLrmX9V+Ftq/YEyv1JaVr5Y/hNtByg== dependencies: de-indent "^1.0.2"