From b18c1d07e7f01dcdabb33257ccad083b390f39f2 Mon Sep 17 00:00:00 2001 From: vodlogic Date: Thu, 1 Apr 2021 16:38:55 +0100 Subject: [PATCH 1/5] feat: Support application/xhtml content type of cy.visit() (#15741) --- packages/server/lib/server-e2e.ts | 4 ++-- packages/server/test/integration/server_spec.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/server/lib/server-e2e.ts b/packages/server/lib/server-e2e.ts index 7165653431..913175cf01 100644 --- a/packages/server/lib/server-e2e.ts +++ b/packages/server/lib/server-e2e.ts @@ -23,7 +23,7 @@ import statusCode from './util/status_code' type WarningErr = Record const fullyQualifiedRe = /^https?:\/\// -const textHtmlContentTypeRe = /^text\/html/i +const htmlContentTypesRe = /^(text\/html|application\/xhtml)/i const debug = Debug('cypress:server:server-e2e') @@ -32,7 +32,7 @@ const isResponseHtml = function (contentType, responseBuffer) { // want to match anything starting with 'text/html' // including 'text/html;charset=utf-8' and 'Text/HTML' // https://github.com/cypress-io/cypress/issues/8506 - return textHtmlContentTypeRe.test(contentType) + return htmlContentTypesRe.test(contentType) } const body = _.invoke(responseBuffer, 'toString') diff --git a/packages/server/test/integration/server_spec.js b/packages/server/test/integration/server_spec.js index ae0c38fef4..674a18474a 100644 --- a/packages/server/test/integration/server_spec.js +++ b/packages/server/test/integration/server_spec.js @@ -474,12 +474,13 @@ describe('Server', () => { .get('/c').reply(200, 'notHtml', { 'content-type': 'text/html;charset=utf-8' }) // invalid, but let's be tolerant .get('/d').reply(200, 'notHtml', { 'content-type': 'text/html;' }) + .get('/e').reply(200, 'notHtml', { 'content-type': 'application/xhtml+xml' }) const bad = await this.server._onResolveUrl('http://example.com/a', {}, this.automationRequest) expect(bad.isHtml).to.be.false - for (const path of ['/b', '/c', '/d']) { + for (const path of ['/b', '/c', '/d', '/e']) { const details = await this.server._onResolveUrl(`http://example.com${path}`, {}, this.automationRequest) expect(details.isHtml).to.be.true From aea823eb16a8b90f0871524e0fdc2aefaad822f5 Mon Sep 17 00:00:00 2001 From: Kukhyeon Heo Date: Fri, 2 Apr 2021 00:51:44 +0900 Subject: [PATCH 2/5] fix: position: sticky visibility. (#15666) --- packages/driver/cypress/fixtures/sticky.html | 29 +++++++++++++++++++ .../integration/dom/visibility_spec.ts | 5 ++++ packages/driver/src/dom/visibility.js | 12 +++----- 3 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 packages/driver/cypress/fixtures/sticky.html diff --git a/packages/driver/cypress/fixtures/sticky.html b/packages/driver/cypress/fixtures/sticky.html new file mode 100644 index 0000000000..f8689eec67 --- /dev/null +++ b/packages/driver/cypress/fixtures/sticky.html @@ -0,0 +1,29 @@ + + +
+
+ Preselect +
+
+ Details + +
+
+ + + + \ No newline at end of file diff --git a/packages/driver/cypress/integration/dom/visibility_spec.ts b/packages/driver/cypress/integration/dom/visibility_spec.ts index da7785fa70..495efe3e06 100644 --- a/packages/driver/cypress/integration/dom/visibility_spec.ts +++ b/packages/driver/cypress/integration/dom/visibility_spec.ts @@ -780,6 +780,11 @@ describe('src/cypress/dom/visibility', () => { expect(this.$childPointerEventsNone.find('span')).to.be.visible expect(this.$childPointerEventsNone.find('span')).to.not.be.hidden }) + + it('is visible when position: sticky', () => { + cy.visit('fixtures/sticky.html') + cy.get('#button').should('be.visible') + }) }) describe('css display', function () { diff --git a/packages/driver/src/dom/visibility.js b/packages/driver/src/dom/visibility.js index b92b4c90d6..1c8c7fe80f 100644 --- a/packages/driver/src/dom/visibility.js +++ b/packages/driver/src/dom/visibility.js @@ -87,7 +87,7 @@ const isHidden = (el, methodName = 'isHidden()', options = { checkOpacity: true return true // is hidden } - if (elOrAncestorIsFixed($el)) { + if (elOrAncestorIsFixedOrSticky($el)) { return elIsNotElementFromPoint($el) } @@ -218,12 +218,8 @@ const canClipContent = function ($el, $ancestor) { return true } -const elOrAncestorIsFixed = function ($el) { - const $stickyOrFixedEl = $elements.getFirstFixedOrStickyPositionParent($el) - - if ($stickyOrFixedEl) { - return $stickyOrFixedEl.css('position') === 'fixed' - } +const elOrAncestorIsFixedOrSticky = function ($el) { + return !!$elements.getFirstFixedOrStickyPositionParent($el) } const elAtCenterPoint = function ($el) { @@ -509,7 +505,7 @@ const getReasonIsHidden = function ($el, options = { checkOpacity: true }) { } // nested else --___________-- - if (elOrAncestorIsFixed($el)) { + if (elOrAncestorIsFixedOrSticky($el)) { if (elIsNotElementFromPoint($el)) { // show the long element here const covered = $elements.stringify(elAtCenterPoint($el)) From bf6368e51bb879f62fc1fddcf6504c2219efb7cd Mon Sep 17 00:00:00 2001 From: Ben Kucera <14625260+Bkucera@users.noreply.github.com> Date: Thu, 1 Apr 2021 13:09:12 -0400 Subject: [PATCH 3/5] chore: add testingType to postRunRequest (#15703) * add testingType to postRunRequest --- packages/server/lib/api.js | 1 + packages/server/lib/modes/record.js | 6 ++++-- packages/server/lib/modes/run.js | 3 ++- packages/server/package.json | 2 +- packages/server/test/e2e/7_record_spec.js | 1 + packages/server/test/support/helpers/serverStub.ts | 2 +- packages/server/test/unit/modes/record_spec.js | 3 +++ yarn.lock | 8 ++++---- 8 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/server/lib/api.js b/packages/server/lib/api.js index c7ae670f0a..3d2c1e5f3f 100644 --- a/packages/server/lib/api.js +++ b/packages/server/lib/api.js @@ -261,6 +261,7 @@ module.exports = { 'recordKey', 'specPattern', 'tags', + 'testingType', ]), runnerCapabilities, } diff --git a/packages/server/lib/modes/record.js b/packages/server/lib/modes/record.js index fa429adc41..cc8e26893c 100644 --- a/packages/server/lib/modes/record.js +++ b/packages/server/lib/modes/record.js @@ -273,7 +273,7 @@ const createRun = Promise.method((options = {}) => { ciBuildId: null, }) - let { projectId, recordKey, platform, git, specPattern, specs, parallel, ciBuildId, group, tags } = options + let { projectId, recordKey, platform, git, specPattern, specs, parallel, ciBuildId, group, tags, testingType } = options if (recordKey == null) { recordKey = env.get('CYPRESS_RECORD_KEY') @@ -319,6 +319,7 @@ const createRun = Promise.method((options = {}) => { projectId, recordKey, specPattern, + testingType, ci: { params: ciProvider.ciParams(), provider: ciProvider.provider(), @@ -562,7 +563,7 @@ const _postInstanceTests = ({ } const createRunAndRecordSpecs = (options = {}) => { - const { specPattern, specs, sys, browser, projectId, config, projectRoot, runAllSpecs, parallel, ciBuildId, group, project, onError } = options + const { specPattern, specs, sys, browser, projectId, config, projectRoot, runAllSpecs, parallel, ciBuildId, group, project, onError, testingType } = options const recordKey = options.key // we want to normalize this to an array to send to API @@ -585,6 +586,7 @@ const createRunAndRecordSpecs = (options = {}) => { return createRun({ git, specs, + testingType, group, tags, parallel, diff --git a/packages/server/lib/modes/run.js b/packages/server/lib/modes/run.js index e2b43c5d1d..89b8016ebb 100644 --- a/packages/server/lib/modes/run.js +++ b/packages/server/lib/modes/run.js @@ -1513,7 +1513,7 @@ module.exports = { const socketId = random.id() - const { projectRoot, record, key, ciBuildId, parallel, group, browser: browserName, tag } = options + const { projectRoot, record, key, ciBuildId, parallel, group, browser: browserName, tag, testingType } = options // this needs to be a closure over `this.exitEarly` and not a reference // because `this.exitEarly` gets overwritten in `this.listenForProjectEnd` @@ -1630,6 +1630,7 @@ module.exports = { browser, parallel, ciBuildId, + testingType, project, projectId, projectRoot, diff --git a/packages/server/package.json b/packages/server/package.json index 3d5dd333b8..472e62006c 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -127,7 +127,7 @@ "@babel/core": "7.9.0", "@babel/preset-env": "7.9.0", "@cypress/debugging-proxy": "2.0.1", - "@cypress/json-schemas": "5.37.3", + "@cypress/json-schemas": "5.38.0", "@cypress/sinon-chai": "2.9.1", "@ffprobe-installer/ffprobe": "1.1.0", "@packages/desktop-gui": "0.0.0-development", diff --git a/packages/server/test/e2e/7_record_spec.js b/packages/server/test/e2e/7_record_spec.js index 992950db57..5247acd48f 100644 --- a/packages/server/test/e2e/7_record_spec.js +++ b/packages/server/test/e2e/7_record_spec.js @@ -100,6 +100,7 @@ describe('e2e record', () => { expect(postRun.body.projectId).to.eq('pid123') expect(postRun.body.recordKey).to.eq('f858a2bc-b469-4e48-be67-0876339ee7e1') expect(postRun.body.specPattern).to.eq('cypress/integration/record*') + expect(postRun.body.testingType).to.eq('e2e') const firstInstance = requests[1] diff --git a/packages/server/test/support/helpers/serverStub.ts b/packages/server/test/support/helpers/serverStub.ts index 028e638f85..d0e5474c01 100644 --- a/packages/server/test/support/helpers/serverStub.ts +++ b/packages/server/test/support/helpers/serverStub.ts @@ -54,7 +54,7 @@ const routeHandlers = { postRun: { method: 'post', url: '/runs', - req: 'postRunRequest@2.3.0', + req: 'postRunRequest@2.4.0', resSchema: 'postRunResponse@2.2.0', res: (req, res) => { if (!req.body.specs) { diff --git a/packages/server/test/unit/modes/record_spec.js b/packages/server/test/unit/modes/record_spec.js index 345801ffbb..90eee11fb2 100644 --- a/packages/server/test/unit/modes/record_spec.js +++ b/packages/server/test/unit/modes/record_spec.js @@ -273,6 +273,7 @@ describe('lib/modes/record', () => { version: '59', } const tag = 'nightly,develop' + const testingType = 'e2e' return recordMode.createRunAndRecordSpecs({ key, @@ -287,6 +288,7 @@ describe('lib/modes/record', () => { specPattern, runAllSpecs, tag, + testingType, }) .then(() => { expect(commitInfo.commitInfo).to.be.calledWith(projectRoot) @@ -297,6 +299,7 @@ describe('lib/modes/record', () => { projectId, ciBuildId, recordKey: key, + testingType, specPattern: 'spec/pattern1,spec/pattern2', specs: ['path/to/spec/a', 'path/to/spec/b'], platform: { diff --git a/yarn.lock b/yarn.lock index 46d8fa2bc7..0b764f997d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2164,10 +2164,10 @@ lodash.merge "^4.6.2" lodash.omit "^4.5.0" -"@cypress/json-schemas@5.37.3": - version "5.37.3" - resolved "https://registry.yarnpkg.com/@cypress/json-schemas/-/json-schemas-5.37.3.tgz#2aed2fadc9533cb8d812ab27df77ea40ccf17759" - integrity sha512-vMhydN3Ysx+d/PnbOixPzlWdvaOeHmano61D2Cwshexo3F8L68xsXqF6zy3T6teNWHXTMtNZ6fQUpcy3r082XQ== +"@cypress/json-schemas@5.38.0": + version "5.38.0" + resolved "https://registry.yarnpkg.com/@cypress/json-schemas/-/json-schemas-5.38.0.tgz#8aad0c140eea4b0bab43f3f9e3df53386040db8f" + integrity sha512-VEyiQ48KrFXJXg7giK6+uTQXa6GPDlnQl1Ss9OYxzhl5feoiV6ioHUKZKvrO+Acu4S9vFlpl3Xvvwlz0azw6kA== dependencies: "@cypress/schema-tools" "4.7.7" lodash "^4.17.21" From f0671102b26c58aac46cda4191bd4ecf862af14d Mon Sep 17 00:00:00 2001 From: Ben Kucera <14625260+Bkucera@users.noreply.github.com> Date: Thu, 1 Apr 2021 13:11:11 -0400 Subject: [PATCH 4/5] chore: add testingType, initialLaunch to update check (#15699) * add testingType, initialLaunch to update check --- .../cypress/integration/footer_spec.js | 5 +++ packages/desktop-gui/src/update/updates.js | 9 +++- packages/server-ct/index.ts | 32 +++++++++++--- packages/server-ct/test/unit/index.spec.ts | 42 +++++++++++++++++++ packages/server/lib/gui/events.js | 1 + packages/server/lib/updater.js | 15 +++++-- packages/server/test/unit/updater_spec.js | 5 ++- 7 files changed, 98 insertions(+), 11 deletions(-) create mode 100644 packages/server-ct/test/unit/index.spec.ts diff --git a/packages/desktop-gui/cypress/integration/footer_spec.js b/packages/desktop-gui/cypress/integration/footer_spec.js index fef26e1d8b..79a022c623 100644 --- a/packages/desktop-gui/cypress/integration/footer_spec.js +++ b/packages/desktop-gui/cypress/integration/footer_spec.js @@ -67,6 +67,11 @@ describe('Footer', () => { it('shows update indicator', () => { cy.get('.update-indicator').should('be.visible') + cy.wrap(ipc.updaterCheck).should('be.calledOnceWith', { + initialLaunch: true, + testingType: 'e2e', + }) + cy.percySnapshot() }) diff --git a/packages/desktop-gui/src/update/updates.js b/packages/desktop-gui/src/update/updates.js index 37e77b4c26..0948739d33 100644 --- a/packages/desktop-gui/src/update/updates.js +++ b/packages/desktop-gui/src/update/updates.js @@ -5,16 +5,23 @@ import updateStore from '../update/update-store' import ipc from '../lib/ipc' import { useLifecycle } from '../lib/use-lifecycle' +let initialLaunch = true + const checkForUpdate = () => { ipc.offUpdaterCheck() - ipc.updaterCheck() + ipc.updaterCheck({ + initialLaunch, + testingType: 'e2e', + }) .then((version) => { if (version) updateStore.setNewVersion(version) }) .catch((error) => { console.warn('Error checking for updates:', error) // eslint-disable-line no-console }) + + initialLaunch = false } export const useUpdateChecker = () => { diff --git a/packages/server-ct/index.ts b/packages/server-ct/index.ts index c59fe0edcd..a035cc0e9b 100644 --- a/packages/server-ct/index.ts +++ b/packages/server-ct/index.ts @@ -1,18 +1,40 @@ /* eslint-disable no-console */ -import chalk from 'chalk' import browsers from '@packages/server/lib/browsers' import openProject from '@packages/server/lib/open_project' - -export * from './src/socket-ct' - -export * from './src/server-ct' +import chalk from 'chalk' +import human from 'human-interval' +import _ from 'lodash' export * from './src/project-ct' +export * from './src/server-ct' + +export * from './src/socket-ct' + export * from './src/specs-store' +const Updater = require('@packages/server/lib/updater') + +const registerCheckForUpdates = () => { + const checkForUpdates = (initialLaunch) => { + Updater.check({ + initialLaunch, + testingType: 'ct', + onNewVersion: _.noop, + onNoNewVersion: _.noop, + }) + } + + setInterval(() => checkForUpdates(false), human('60 minutes')) + checkForUpdates(true) +} + // Partial because there are probably other options that are not included in this type. export const start = async (projectRoot: string, args: Record) => { + if (process.env['CYPRESS_INTERNAL_ENV'] === 'production') { + registerCheckForUpdates() + } + // add chrome as a default browser if none has been specified return browsers.ensureAndGetByNameOrPath(args.browser) .then((browser: Cypress.Browser) => { diff --git a/packages/server-ct/test/unit/index.spec.ts b/packages/server-ct/test/unit/index.spec.ts new file mode 100644 index 0000000000..2a3b85d43d --- /dev/null +++ b/packages/server-ct/test/unit/index.spec.ts @@ -0,0 +1,42 @@ +const Updater = require('@packages/server/lib/updater') +import openProject from '@packages/server/lib/open_project' +import browsers from '@packages/server/lib/browsers' +import sinon from 'sinon' +import { expect } from 'chai' +import * as Index from '../../index' + +describe('index.spec', () => { + let backupEnv + let stub_setInterval + + beforeEach(() => { + backupEnv = process.env + process.env.CYPRESS_INTERNAL_ENV = 'production' + stub_setInterval = sinon.spy(global, 'setInterval') + sinon.stub(Updater, 'check').resolves() + sinon.stub(openProject, 'create').resolves() + sinon.stub(openProject, 'launch').resolves() + sinon.stub(browsers, 'ensureAndGetByNameOrPath').resolves() + }) + + afterEach(() => { + if (backupEnv) { + process.env = backupEnv + backupEnv = null + } + }) + + it('registers update check', async () => { + await Index.start('/path/to/project', { + browser: 'chrome', + }) + + expect(stub_setInterval.callCount).eq(1) + expect(stub_setInterval.firstCall.args[1]).eq(1000 * 60 * 60) + expect(Updater.check.callCount).eq(1) + expect(Updater.check.firstCall.args[0]).includes({ + testingType: 'ct', + initialLaunch: true, + }) + }) +}) diff --git a/packages/server/lib/gui/events.js b/packages/server/lib/gui/events.js index 791fca7931..29e4c6ea46 100644 --- a/packages/server/lib/gui/events.js +++ b/packages/server/lib/gui/events.js @@ -202,6 +202,7 @@ const handleEvent = function (options, bus, event, id, type, arg) { case 'updater:check': return Updater.check({ + ...arg, onNewVersion ({ version }) { return send(version) }, diff --git a/packages/server/lib/updater.js b/packages/server/lib/updater.js index 37c5ae62eb..31cd8a70a5 100644 --- a/packages/server/lib/updater.js +++ b/packages/server/lib/updater.js @@ -1,3 +1,4 @@ +const os = require('os') const debug = require('debug')('cypress:server:updater') const semver = require('semver') const rp = require('@cypress/request-promise') @@ -5,15 +6,17 @@ const pkg = require('@packages/root') const { agent } = require('@packages/network') const konfig = require('./konfig') const { machineId } = require('./util/machine_id') - -const _getManifest = (id) => { +const _getManifest = ({ id, initialLaunch, testingType }) => { const url = konfig('desktop_manifest_url') return rp.get({ url, headers: { 'x-cypress-version': pkg.version, + 'x-os-name': os.platform(), 'x-machine-id': id, + 'x-initial-launch:': String(initialLaunch), + 'x-testing-type': testingType, }, agent, proxy: null, @@ -21,10 +24,14 @@ const _getManifest = (id) => { }) } -const check = async ({ onNewVersion, onNoNewVersion } = {}) => { +const check = async ({ testingType, initialLaunch, onNewVersion, onNoNewVersion } = {}) => { try { const id = await machineId() - const manifest = await _getManifest(id) + const manifest = await _getManifest({ + id, + testingType, + initialLaunch, + }) if (!manifest || !manifest.version) { throw new Error('manifest is empty or does not have a version') diff --git a/packages/server/test/unit/updater_spec.js b/packages/server/test/unit/updater_spec.js index c096d590f9..a8297b2923 100644 --- a/packages/server/test/unit/updater_spec.js +++ b/packages/server/test/unit/updater_spec.js @@ -10,12 +10,15 @@ describe('lib/updater', () => { it('sends the right headers', () => { sinon.stub(rp, 'get').resolves({}) - Updater._getManifest('machine-id') + Updater._getManifest({ testingType: 'type', initialLaunch: true, id: 'machine-id' }) expect(rp.get).to.be.calledWithMatch({ headers: { 'x-cypress-version': pkg.version, + 'x-os-name': 'linux', 'x-machine-id': 'machine-id', + 'x-initial-launch:': 'true', + 'x-testing-type': 'type', }, }) }) From ed0af3b4bb7d4bc678cc478445cf261f1767de6e Mon Sep 17 00:00:00 2001 From: Zach Bloomquist Date: Thu, 1 Apr 2021 16:18:05 -0400 Subject: [PATCH 5/5] fix: suppress EventEmitter warnings + other Node warnings in prod (#15723) --- packages/server/index.js | 2 +- packages/server/lib/browsers/electron.js | 17 +++++++--- packages/server/lib/plugins/child/index.js | 2 +- packages/server/lib/plugins/util.js | 4 +++ ...orized_warning.js => suppress_warnings.js} | 10 +++--- .../server/test/e2e/0_max_listeners_spec.ts | 33 +++++++++++++++++++ .../server/test/integration/cypress_spec.js | 1 + .../projects/max-listeners/cypress.json | 1 + packages/server/test/support/helpers/e2e.ts | 1 - ...ning_spec.ts => suppress_warnings_spec.ts} | 6 ++-- 10 files changed, 62 insertions(+), 15 deletions(-) rename packages/server/lib/util/{suppress_unauthorized_warning.js => suppress_warnings.js} (62%) create mode 100644 packages/server/test/e2e/0_max_listeners_spec.ts create mode 100644 packages/server/test/support/fixtures/projects/max-listeners/cypress.json rename packages/server/test/unit/{suppress_unauthorized_warning_spec.ts => suppress_warnings_spec.ts} (87%) diff --git a/packages/server/index.js b/packages/server/index.js index 96f51f467a..19a22cf49b 100644 --- a/packages/server/index.js +++ b/packages/server/index.js @@ -34,7 +34,7 @@ process.noDeprecation = process.env.CYPRESS_INTERNAL_ENV === 'production' // always show stack traces for Electron deprecation warnings process.traceDeprecation = true -require('./lib/util/suppress_unauthorized_warning').suppress() +require('./lib/util/suppress_warnings').suppress() function launchOrFork () { const nodeOptions = require('./lib/util/node_options') diff --git a/packages/server/lib/browsers/electron.js b/packages/server/lib/browsers/electron.js index 54cd0f88ae..febeb27542 100644 --- a/packages/server/lib/browsers/electron.js +++ b/packages/server/lib/browsers/electron.js @@ -235,7 +235,7 @@ module.exports = { return this._enableDebugger(win.webContents) }) .then(() => { - return this._handleDownloads(win.webContents, options.downloadsFolder, automation) + return this._handleDownloads(win, options.downloadsFolder, automation) }) .return(win) }, @@ -291,8 +291,8 @@ module.exports = { return webContents.debugger.sendCommand('Console.enable') }, - _handleDownloads (webContents, dir, automation) { - webContents.session.on('will-download', (event, downloadItem) => { + _handleDownloads (win, dir, automation) { + const onWillDownload = (event, downloadItem) => { const savePath = path.join(dir, downloadItem.getFilename()) automation.push('create:download', { @@ -307,9 +307,16 @@ module.exports = { id: downloadItem.getETag(), }) }) - }) + } - return webContents.debugger.sendCommand('Page.setDownloadBehavior', { + const { session } = win.webContents + + session.on('will-download', onWillDownload) + + // avoid adding redundant `will-download` handlers if session is reused for next spec + win.on('closed', () => session.removeListener('will-download', onWillDownload)) + + return win.webContents.debugger.sendCommand('Page.setDownloadBehavior', { behavior: 'allow', downloadPath: dir, }) diff --git a/packages/server/lib/plugins/child/index.js b/packages/server/lib/plugins/child/index.js index 224259ce09..9687e28819 100644 --- a/packages/server/lib/plugins/child/index.js +++ b/packages/server/lib/plugins/child/index.js @@ -1,6 +1,6 @@ require('graceful-fs').gracefulify(require('fs')) -require('../../util/suppress_unauthorized_warning').suppress() +require('../../util/suppress_warnings').suppress() const ipc = require('../util').wrapIpc(process) const { file: pluginsFile, projectRoot } = require('minimist')(process.argv.slice(2)) diff --git a/packages/server/lib/plugins/util.js b/packages/server/lib/plugins/util.js index ebb0e2a39e..29182c142a 100644 --- a/packages/server/lib/plugins/util.js +++ b/packages/server/lib/plugins/util.js @@ -19,6 +19,10 @@ module.exports = { return emitter.emit(message.event, ...message.args) }) + // prevent max listeners warning on ipc + // @see https://github.com/cypress-io/cypress/issues/1305#issuecomment-780895569 + emitter.setMaxListeners(Infinity) + return { send (event, ...args) { if (aProcess.killed) { diff --git a/packages/server/lib/util/suppress_unauthorized_warning.js b/packages/server/lib/util/suppress_warnings.js similarity index 62% rename from packages/server/lib/util/suppress_unauthorized_warning.js rename to packages/server/lib/util/suppress_warnings.js index 7cde591898..964ef170ed 100644 --- a/packages/server/lib/util/suppress_unauthorized_warning.js +++ b/packages/server/lib/util/suppress_warnings.js @@ -1,6 +1,7 @@ const _ = require('lodash') +const Debug = require('debug') -const originalEmitWarning = process.emitWarning +const debug = Debug('cypress:server:lib:util:suppress_warnings') let suppressed = false @@ -10,7 +11,8 @@ let suppressed = false * https://github.com/cypress-io/cypress/issues/5248 */ const suppress = () => { - if (suppressed) { + if (suppressed || process.env.CYPRESS_INTERNAL_ENV === 'production') { + // in development, still log warnings since they are helpful return } @@ -18,7 +20,7 @@ const suppress = () => { process.emitWarning = (warning, type, code, ...args) => { if (_.isString(warning) && _.includes(warning, 'NODE_TLS_REJECT_UNAUTHORIZED')) { - // https://github.com/nodejs/node/blob/82f89ec8c1554964f5029fab1cf0f4fad1fa55a8/lib/_tls_wrap.js#L1378-L1384 + // https://github.com/nodejs/node/blob/85e6089c4db4da23dd88358fe0a12edefcd411f2/lib/internal/options.js#L17 return } @@ -31,7 +33,7 @@ const suppress = () => { return } - return originalEmitWarning.call(process, warning, type, code, ...args) + debug('suppressed emitWarning from node: %o', { process, warning, type, code, args }) } } diff --git a/packages/server/test/e2e/0_max_listeners_spec.ts b/packages/server/test/e2e/0_max_listeners_spec.ts new file mode 100644 index 0000000000..acc964f332 --- /dev/null +++ b/packages/server/test/e2e/0_max_listeners_spec.ts @@ -0,0 +1,33 @@ +import _ from 'lodash' +import path from 'path' +import fs from 'fs-extra' + +import e2e from '../support/helpers/e2e' +import Fixtures from '../support/helpers/fixtures' + +const projectPath = Fixtures.projectPath('max-listeners') + +describe('max listeners warning spec', () => { + e2e.setup() + + // @see https://github.com/cypress-io/cypress/issues/1305 + e2e.it('does not log MaxEventListeners error', { + browser: 'electron', + project: projectPath, + spec: '*', + onRun: async (exec) => { + const integrationPath = path.join(projectPath, 'cypress/integration') + + // create a bunch of dummy test files to reproduce #1305 + await fs.mkdirp(integrationPath) + await Promise.all( + _.times(15, (i) => fs.writeFile(path.join(integrationPath, `${i}.spec.js`), `it('test', () => {})`)), + ) + + const { stderr } = await exec() + + expect(stderr).to.not.include('setMaxListeners') + expect(stderr).to.not.include('preprocessor:close') + }, + }) +}) diff --git a/packages/server/test/integration/cypress_spec.js b/packages/server/test/integration/cypress_spec.js index ef3ed30e1d..cc49cb3dd0 100644 --- a/packages/server/test/integration/cypress_spec.js +++ b/packages/server/test/integration/cypress_spec.js @@ -1143,6 +1143,7 @@ describe('lib/cypress', () => { setProxy: sinon.stub().resolves(), setUserAgent: sinon.stub(), on: sinon.stub(), + removeListener: sinon.stub(), }, } diff --git a/packages/server/test/support/fixtures/projects/max-listeners/cypress.json b/packages/server/test/support/fixtures/projects/max-listeners/cypress.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/packages/server/test/support/fixtures/projects/max-listeners/cypress.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/server/test/support/helpers/e2e.ts b/packages/server/test/support/helpers/e2e.ts index 762bb0235f..362375a2c3 100644 --- a/packages/server/test/support/helpers/e2e.ts +++ b/packages/server/test/support/helpers/e2e.ts @@ -416,7 +416,6 @@ const e2e = { if (process.env.NO_EXIT) { Fixtures.scaffoldWatch() - process.env.CYPRESS_INTERNAL_E2E_TESTS } sinon.stub(process, 'exit') diff --git a/packages/server/test/unit/suppress_unauthorized_warning_spec.ts b/packages/server/test/unit/suppress_warnings_spec.ts similarity index 87% rename from packages/server/test/unit/suppress_unauthorized_warning_spec.ts rename to packages/server/test/unit/suppress_warnings_spec.ts index b76b26fe02..7509cf48da 100644 --- a/packages/server/test/unit/suppress_unauthorized_warning_spec.ts +++ b/packages/server/test/unit/suppress_warnings_spec.ts @@ -6,9 +6,9 @@ import proxyquire from 'proxyquire' const ERROR_MESSAGE = 'Setting the NODE_TLS_REJECT_UNAUTHORIZED' const TLS_CONNECT = `require('tls').connect().on('error', ()=>{});` -const SUPPRESS_WARNING = `require('${__dirname}/../../lib/util/suppress_unauthorized_warning').suppress();` +const SUPPRESS_WARNING = `require('${__dirname}/../../lib/util/suppress_warnings').suppress();` -describe('lib/util/suppress_unauthorized_warning', function () { +describe('lib/util/suppress_warnings', function () { it('tls.connect emits warning if NODE_TLS_REJECT_UNAUTHORIZED=0 and not suppressed', function () { return execa.shell(`node -e "${TLS_CONNECT}"`, { env: { @@ -36,7 +36,7 @@ describe('lib/util/suppress_unauthorized_warning', function () { const emitWarning = sinon.spy(process, 'emitWarning') // force typescript to always be non-requireable - const { suppress } = proxyquire('../../lib/util/suppress_unauthorized_warning', {}) + const { suppress } = proxyquire('../../lib/util/suppress_warnings', {}) suppress()