From 703b8489b1a38c9e0f6775134f2078b6d2a778a1 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 17 Jun 2021 16:05:39 -0500 Subject: [PATCH 001/204] feat: make cypress.js available --- packages/runner-ct/cypress.js | 7 ++++ packages/runner-ct/cypress.json | 7 ---- packages/server/lib/modes/record.js | 5 ++- packages/server/lib/modes/run.js | 4 +- packages/server/lib/project-base.ts | 6 +-- packages/server/lib/util/settings.js | 55 +++++++++++++++++++++++++--- packages/server/test/specUtils.ts | 20 ++++++++++ 7 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 packages/runner-ct/cypress.js delete mode 100644 packages/runner-ct/cypress.json diff --git a/packages/runner-ct/cypress.js b/packages/runner-ct/cypress.js new file mode 100644 index 000000000000..cb5207cf84ae --- /dev/null +++ b/packages/runner-ct/cypress.js @@ -0,0 +1,7 @@ +module.exports = { + testFiles: '**/*spec.{ts,tsx}', + video: true, + env: { + reactDevtools: true, + }, +} diff --git a/packages/runner-ct/cypress.json b/packages/runner-ct/cypress.json deleted file mode 100644 index f1cc9487e5be..000000000000 --- a/packages/runner-ct/cypress.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "testFiles": "**/*spec.{ts,tsx}", - "video": true, - "env": { - "reactDevtools": true - } -} diff --git a/packages/server/lib/modes/record.js b/packages/server/lib/modes/record.js index cf38fe303ff0..c0420d312b0a 100644 --- a/packages/server/lib/modes/record.js +++ b/packages/server/lib/modes/record.js @@ -280,7 +280,7 @@ const createRun = Promise.method((options = {}) => { ciBuildId: null, }) - let { projectId, recordKey, platform, git, specPattern, specs, parallel, ciBuildId, group, tags, testingType } = options + let { projectId, recordKey, platform, git, specPattern, specs, parallel, ciBuildId, group, tags, projectRoot, testingType } = options if (recordKey == null) { recordKey = env.get('CYPRESS_RECORD_KEY') @@ -444,7 +444,7 @@ const createRun = Promise.method((options = {}) => { } } case 404: - return errors.throw('DASHBOARD_PROJECT_NOT_FOUND', projectId, settings.configFile(options)) + return errors.throw('DASHBOARD_PROJECT_NOT_FOUND', projectId, settings.configFile(projectRoot, options)) case 412: return errors.throw('DASHBOARD_INVALID_RUN_REQUEST', err.error) case 422: { @@ -612,6 +612,7 @@ const createRunAndRecordSpecs = (options = {}) => { git, specs, group, + projectRoot, tags, parallel, platform, diff --git a/packages/server/lib/modes/run.js b/packages/server/lib/modes/run.js index e4ca65816ff6..17aad4e7d2a6 100644 --- a/packages/server/lib/modes/run.js +++ b/packages/server/lib/modes/run.js @@ -1540,7 +1540,7 @@ module.exports = { recordMode.throwIfRecordParamsWithoutRecording(record, ciBuildId, parallel, group, tag) if (record) { - recordMode.throwIfNoProjectId(projectId, settings.configFile(options)) + recordMode.throwIfNoProjectId(projectId, settings.configFile(projectRoot, options)) recordMode.throwIfIncorrectCiBuildIdUsage(ciBuildId, parallel, group) recordMode.throwIfIndeterminateCiBuildId(ciBuildId, parallel, group) } @@ -1618,7 +1618,7 @@ module.exports = { } if (record) { - const { projectName } = config + const { projectName, projectRoot } = config return recordMode.createRunAndRecordSpecs({ tag, diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 3961df4f3ad8..57edf4d5fa2c 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -333,7 +333,7 @@ export class ProjectBase extends EE { return fs.pathExists(supportFile) .then((found) => { if (!found) { - errors.throw('SUPPORT_FILE_NOT_FOUND', supportFile, settings.configFile(cfg)) + errors.throw('SUPPORT_FILE_NOT_FOUND', supportFile, settings.configFile(this.projectRoot, cfg)) } }) } @@ -866,7 +866,7 @@ export class ProjectBase extends EE { return readSettings.projectId } - errors.throw('NO_PROJECT_ID', settings.configFile(this.options), this.projectRoot) + errors.throw('NO_PROJECT_ID', settings.configFile(this.projectRoot, this.options), this.projectRoot) }) } @@ -1043,7 +1043,7 @@ export class ProjectBase extends EE { static add (path, options) { // don't cache a project if a non-default configFile is set // https://git.io/JeGyF - if (settings.configFile(options) !== 'cypress.json') { + if (settings.configFile(path, options) !== 'cypress.json') { return Bluebird.resolve({ path }) } diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index c1d03d1f4d7b..ccbae9a1b911 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -97,8 +97,27 @@ module.exports = { return options.testingType === 'component' }, - configFile (options = {}) { - return options.configFile === false ? false : (options.configFile || 'cypress.json') + configFile (projectRoot, options = {}) { + const ls = fs.readdirSync(projectRoot) + + if (options.configFile === false) { + return false + } + + if (options.configFile) { + return options.configFile + } + + if (ls.includes('cypress.json')) { + return 'cypress.json' + } + + if (ls.includes('cypress.js')) { + return 'cypress.js' + } + + // Default is to create a new `cypress.json` file if one does not exist. + return 'cypress.json' }, id (projectRoot, options = {}) { @@ -124,8 +143,8 @@ module.exports = { // cypress.json does not exist, we missing project log('cannot find file %s', file) - return this._err('CONFIG_FILE_NOT_FOUND', this.configFile(options), projectRoot) - }).catch({ code: 'EACCES' }, { code: 'EPERM' }, () => { + return this._err('CONFIG_FILE_NOT_FOUND', this.configFile(projectRoot, options), projectRoot) + }).catch({ code: 'EACCES' }, () => { // we cannot write due to folder permissions return errors.warning('FOLDER_NOT_WRITABLE', projectRoot) }).catch((err) => { @@ -144,7 +163,31 @@ module.exports = { const file = this.pathToConfigFile(projectRoot, options) - return fs.readJsonAsync(file) + const requireAsync = (file) => { + return Promise.try(() => { + const config = require(file) + + // if it is not `cypress.js` just return it as-is. + if (!file.endsWith('cypress.js')) { + return config + } + + // otherwise, if it is cypress.js, we grab the runner-specific settings. + if (this.isComponentTesting(options)) { + return config.component || {} + } + + // additional, if config does *not* contain `e2e` but we are in `e2e` mode, + // it probably means the user is an e2e user who would like to use cypress.js + // instead of cypress.json. Just use the file as is. + return config.e2e || config + }) + } + + return requireAsync(file) + .catch({ code: 'MODULE_NOT_FOUND' }, () => { + return this._write(file, {}) + }) .catch({ code: 'ENOENT' }, () => { return this._write(file, {}) }).then((json = {}) => { @@ -211,7 +254,7 @@ module.exports = { }, pathToConfigFile (projectRoot, options = {}) { - const configFile = this.configFile(options) + const configFile = this.configFile(projectRoot, options) return configFile && this._pathToFile(projectRoot, configFile) }, diff --git a/packages/server/test/specUtils.ts b/packages/server/test/specUtils.ts index eae319679fd5..ddcfe42d72b0 100644 --- a/packages/server/test/specUtils.ts +++ b/packages/server/test/specUtils.ts @@ -46,3 +46,23 @@ export const getFs = () => { return recurse({ root: mockfs.getMockRoot() }, -1).root } + +export const supportedConfigFiles = [ + 'cypress.json', + 'cypress.js', + 'cypress.component.config.js', + 'cypress.e2e.config.js', +] + +/** + * Since we load the cypress.json, cypress.e2e.config.js or cypress.component.config.js + * via `require`, we need to clear the `require.cache` before/after some tests + * to ensure we are not using a cached configuration file. + */ +export const clearCypressJsonCache = () => { + Object.keys(require.cache).forEach((key) => { + if (supportedConfigFiles.some((file) => key.includes(file))) { + delete require.cache[key] + } + }) +} From e3ee515a25bc9c588bc05abc70ad9b6032c70bc5 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 17 Jun 2021 17:19:16 -0500 Subject: [PATCH 002/204] test: test cypress.js --- packages/server/test/unit/settings_spec.js | 61 ++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/packages/server/test/unit/settings_spec.js b/packages/server/test/unit/settings_spec.js index e1c9df1339bc..0977382d8700 100644 --- a/packages/server/test/unit/settings_spec.js +++ b/packages/server/test/unit/settings_spec.js @@ -3,19 +3,28 @@ require('../spec_helper') const path = require('path') const { fs } = require(`${root}lib/util/fs`) const settings = require(`${root}lib/util/settings`) +const { supportedConfigFiles } = require('../specUtils') const projectRoot = process.cwd() +const cleanupConfigFiles = () => { + return Promise.all(supportedConfigFiles.map((file) => fs.removeAsync(file))) +} + describe('lib/settings', () => { context('with no configFile option', () => { beforeEach(function () { - this.setup = (obj = {}) => { - return fs.writeJsonAsync('cypress.json', obj) + this.setup = (obj = {}, file = 'cypress.json') => { + if (file === 'cypress.json') { + return fs.writeJsonAsync(file, obj) + } + + return fs.outputFileAsync(file, obj) } }) afterEach(() => { - return fs.removeAsync('cypress.json') + return cleanupConfigFiles() }) context('nested cypress object', () => { @@ -97,6 +106,52 @@ describe('lib/settings', () => { }) context('.read', () => { + it('promises cypress.js -> `component` key for component testing runner', function () { + return this.setup(` + module.exports = { + component: { + bo: 'peep' + } + } + `, + 'cypress.js') + .then(() => { + return settings.read(projectRoot, {}) + }).then((obj) => { + expect(obj).to.deep.eq({ bo: 'peep' }) + }) + }) + + it('promises cypress.js -> `e2e` key for component testing runner', function () { + return this.setup(` + module.exports = { + e2e: { + e2e_setting: 'e2e_setting' + } + } + `, + 'cypress.js') + .then(() => { + return settings.read(projectRoot, {}) + }).then((obj) => { + expect(obj).to.deep.eq({ e2e_setting: 'e2e_setting' }) + }) + }) + + it('promises cypress.js assumes e2e if no runner specific keys are configured', function () { + return this.setup(` + module.exports = { + bab: 'beb' + } + `, + 'cypress.js') + .then(() => { + return settings.read(projectRoot, {}) + }).then((obj) => { + expect(obj).to.deep.eq({ bab: 'beb' }) + }) + }) + it('promises cypress.json', function () { return this.setup({ foo: 'bar' }) .then(() => { From 7344f15d6991d1c1e5b47195c2ce92996024abbb Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 22 Jun 2021 09:58:14 -0500 Subject: [PATCH 003/204] clear cache after each test --- packages/server/test/e2e/7_record_spec.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/server/test/e2e/7_record_spec.js b/packages/server/test/e2e/7_record_spec.js index f6764a1acf9b..1a1e95fffd76 100644 --- a/packages/server/test/e2e/7_record_spec.js +++ b/packages/server/test/e2e/7_record_spec.js @@ -16,6 +16,7 @@ const { postInstanceTestsResponse, } = require('../support/helpers/serverStub') const { expectRunsToHaveCorrectTimings } = require('../support/helpers/resultsUtils') +const { clearCypressJsonCache } = require('../specUtils') const e2ePath = Fixtures.projectPath('e2e') const outputPath = path.join(e2ePath, 'output.json') @@ -30,6 +31,10 @@ describe('e2e record', () => { requests = getRequests() }) + afterEach(() => { + return clearCypressJsonCache() + }) + context('passing', () => { setupStubbedServer(createRoutes()) From 3a57ee11f002957339d0095459507c6e9986ca7a Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 22 Jun 2021 11:07:21 -0500 Subject: [PATCH 004/204] test: mock configFile resolve --- packages/server/test/unit/project_spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/test/unit/project_spec.js b/packages/server/test/unit/project_spec.js index 9c74409a6cf3..ed703562c903 100644 --- a/packages/server/test/unit/project_spec.js +++ b/packages/server/test/unit/project_spec.js @@ -39,6 +39,7 @@ describe('lib/project-base', () => { }) sinon.stub(runEvents, 'execute').resolves() + sinon.stub(settings, 'configFile').returns('cypress.json') return settings.read(this.todosPath).then((obj = {}) => { ({ projectId: this.projectId } = obj) From b55ecc06ad29859939d100ea9faa13fb936f94c6 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 22 Jun 2021 11:15:00 -0500 Subject: [PATCH 005/204] test: mock fs instead of settings --- packages/server/test/unit/project_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/test/unit/project_spec.js b/packages/server/test/unit/project_spec.js index ed703562c903..08ae53386127 100644 --- a/packages/server/test/unit/project_spec.js +++ b/packages/server/test/unit/project_spec.js @@ -39,7 +39,7 @@ describe('lib/project-base', () => { }) sinon.stub(runEvents, 'execute').resolves() - sinon.stub(settings, 'configFile').returns('cypress.json') + sinon.stub(fs, 'readdirSync').returns(['cypress.json']) return settings.read(this.todosPath).then((obj = {}) => { ({ projectId: this.projectId } = obj) From 714311a1c3d0c1ca1c56eac7fae22837ea3f0380 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 22 Jun 2021 12:45:57 -0500 Subject: [PATCH 006/204] test: mock fs and fix setup --- packages/server/test/unit/settings_spec.js | 54 +------------------ .../server/test/unit/util/settings_spec.js | 5 ++ 2 files changed, 7 insertions(+), 52 deletions(-) diff --git a/packages/server/test/unit/settings_spec.js b/packages/server/test/unit/settings_spec.js index 0977382d8700..3798ceffd4ae 100644 --- a/packages/server/test/unit/settings_spec.js +++ b/packages/server/test/unit/settings_spec.js @@ -3,14 +3,10 @@ require('../spec_helper') const path = require('path') const { fs } = require(`${root}lib/util/fs`) const settings = require(`${root}lib/util/settings`) -const { supportedConfigFiles } = require('../specUtils') +const { clearCypressJsonCache } = require('../specUtils') const projectRoot = process.cwd() -const cleanupConfigFiles = () => { - return Promise.all(supportedConfigFiles.map((file) => fs.removeAsync(file))) -} - describe('lib/settings', () => { context('with no configFile option', () => { beforeEach(function () { @@ -24,7 +20,7 @@ describe('lib/settings', () => { }) afterEach(() => { - return cleanupConfigFiles() + return clearCypressJsonCache() }) context('nested cypress object', () => { @@ -106,52 +102,6 @@ describe('lib/settings', () => { }) context('.read', () => { - it('promises cypress.js -> `component` key for component testing runner', function () { - return this.setup(` - module.exports = { - component: { - bo: 'peep' - } - } - `, - 'cypress.js') - .then(() => { - return settings.read(projectRoot, {}) - }).then((obj) => { - expect(obj).to.deep.eq({ bo: 'peep' }) - }) - }) - - it('promises cypress.js -> `e2e` key for component testing runner', function () { - return this.setup(` - module.exports = { - e2e: { - e2e_setting: 'e2e_setting' - } - } - `, - 'cypress.js') - .then(() => { - return settings.read(projectRoot, {}) - }).then((obj) => { - expect(obj).to.deep.eq({ e2e_setting: 'e2e_setting' }) - }) - }) - - it('promises cypress.js assumes e2e if no runner specific keys are configured', function () { - return this.setup(` - module.exports = { - bab: 'beb' - } - `, - 'cypress.js') - .then(() => { - return settings.read(projectRoot, {}) - }).then((obj) => { - expect(obj).to.deep.eq({ bab: 'beb' }) - }) - }) - it('promises cypress.json', function () { return this.setup({ foo: 'bar' }) .then(() => { diff --git a/packages/server/test/unit/util/settings_spec.js b/packages/server/test/unit/util/settings_spec.js index e306accaf242..ad776eec7f2b 100644 --- a/packages/server/test/unit/util/settings_spec.js +++ b/packages/server/test/unit/util/settings_spec.js @@ -1,8 +1,13 @@ require('../../spec_helper') +const { fs } = require(`../../../lib/util/fs`) const setting = require(`../../../lib/util/settings`) describe('lib/util/settings', () => { describe('pathToConfigFile', () => { + beforeEach(() => { + sinon.stub(fs, 'readdirSync').returns(['cypress.json']) + }) + it('supports relative path', () => { const path = setting.pathToConfigFile('/users/tony/cypress', { configFile: 'e2e/config.json', From 00cb9bd955cfba3af3205fcd4dd9bf11d52ab432 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 22 Jun 2021 12:47:46 -0500 Subject: [PATCH 007/204] test: cleanup cypress.json --- packages/server/test/unit/settings_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/test/unit/settings_spec.js b/packages/server/test/unit/settings_spec.js index 3798ceffd4ae..871cc236287a 100644 --- a/packages/server/test/unit/settings_spec.js +++ b/packages/server/test/unit/settings_spec.js @@ -20,7 +20,7 @@ describe('lib/settings', () => { }) afterEach(() => { - return clearCypressJsonCache() + return fs.removeAsync('cypress.json').then(clearCypressJsonCache) }) context('nested cypress object', () => { From 584fe634fccce789a5cfe6f76954e08c2d506cf4 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 22 Jun 2021 13:24:18 -0500 Subject: [PATCH 008/204] add unit tests for cypress js loading --- packages/server/lib/util/settings.js | 17 +---- packages/server/test/unit/settings_spec.js | 73 +++++++++++++++++++++- 2 files changed, 73 insertions(+), 17 deletions(-) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index ccbae9a1b911..1b3fe19d2862 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -165,22 +165,7 @@ module.exports = { const requireAsync = (file) => { return Promise.try(() => { - const config = require(file) - - // if it is not `cypress.js` just return it as-is. - if (!file.endsWith('cypress.js')) { - return config - } - - // otherwise, if it is cypress.js, we grab the runner-specific settings. - if (this.isComponentTesting(options)) { - return config.component || {} - } - - // additional, if config does *not* contain `e2e` but we are in `e2e` mode, - // it probably means the user is an e2e user who would like to use cypress.js - // instead of cypress.json. Just use the file as is. - return config.e2e || config + return require(file) }) } diff --git a/packages/server/test/unit/settings_spec.js b/packages/server/test/unit/settings_spec.js index 871cc236287a..364645e05a54 100644 --- a/packages/server/test/unit/settings_spec.js +++ b/packages/server/test/unit/settings_spec.js @@ -20,7 +20,9 @@ describe('lib/settings', () => { }) afterEach(() => { - return fs.removeAsync('cypress.json').then(clearCypressJsonCache) + return fs.removeAsync('cypress.json') + .then(() => fs.removeAsync('cypress.js')) + .then(clearCypressJsonCache) }) context('nested cypress object', () => { @@ -102,6 +104,75 @@ describe('lib/settings', () => { }) context('.read', () => { + it('promises cypress.js -> `component` key for component testing runner', function () { + return this.setup(` + module.exports = { + commmon_setting: true, + component: { + component_setting: 'peep' + } + } + `, + 'cypress.js') + .then(() => { + return settings.read(projectRoot, { testingType: 'component' }) + }).then((obj) => { + expect(obj).to.deep.eq({ + commmon_setting: true, + component_setting: 'peep', + component: { + component_setting: 'peep', + }, + }) + }) + }) + + it('promises cypress.js -> `e2e` key for component testing runner', function () { + return this.setup(` + module.exports = { + commmon_setting: true, + e2e: { + e2e_setting: 'e2e_setting' + } + } + `, + 'cypress.js') + .then(() => { + return settings.read(projectRoot, { testingType: 'e2e' }) + }).then((obj) => { + expect(obj).to.deep.eq({ + commmon_setting: true, + e2e_setting: 'e2e_setting', + e2e: { + e2e_setting: 'e2e_setting', + }, + }) + }) + }) + + it('promises cypress.js assumes e2e if no runner specific keys are configured', function () { + return this.setup(` + module.exports = { + commmon_setting: true, + e2e: { + e2e_setting: 'e2e_setting', + }, + } + `, + 'cypress.js') + .then(() => { + return settings.read(projectRoot, {}) + }).then((obj) => { + expect(obj).to.deep.eq({ + commmon_setting: true, + e2e_setting: 'e2e_setting', + e2e: { + e2e_setting: 'e2e_setting', + }, + }) + }) + }) + it('promises cypress.json', function () { return this.setup({ foo: 'bar' }) .then(() => { From ac65d2f0eafc87ed2d7f2ed8a2bc46c335101cee Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 23 Jun 2021 16:49:00 -0500 Subject: [PATCH 009/204] refactro: use cypress.config instead of cypress.js --- npm/vue/cypress/plugins/index.js | 2 +- .../runner-ct/{cypress.js => cypress.config.js} | 0 packages/server/lib/util/settings.js | 4 ++-- packages/server/test/specUtils.ts | 4 +--- packages/server/test/unit/settings_spec.js | 14 +++++++------- 5 files changed, 11 insertions(+), 13 deletions(-) rename packages/runner-ct/{cypress.js => cypress.config.js} (100%) diff --git a/npm/vue/cypress/plugins/index.js b/npm/vue/cypress/plugins/index.js index f505daf3d0a4..ddfe2084be83 100644 --- a/npm/vue/cypress/plugins/index.js +++ b/npm/vue/cypress/plugins/index.js @@ -20,7 +20,7 @@ module.exports = (on, config) => { } require('@cypress/code-coverage/task')(on, config) - on('dev-server:start', (options) => startDevServer({ options, webpackConfig })) + // on('dev-server:start', (options) => startDevServer({ options, webpackConfig })) return config } diff --git a/packages/runner-ct/cypress.js b/packages/runner-ct/cypress.config.js similarity index 100% rename from packages/runner-ct/cypress.js rename to packages/runner-ct/cypress.config.js diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 1b3fe19d2862..f140d6999149 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -112,8 +112,8 @@ module.exports = { return 'cypress.json' } - if (ls.includes('cypress.js')) { - return 'cypress.js' + if (ls.includes('cypress.config.js')) { + return 'cypress.config.js' } // Default is to create a new `cypress.json` file if one does not exist. diff --git a/packages/server/test/specUtils.ts b/packages/server/test/specUtils.ts index ddcfe42d72b0..aca0f48fc86f 100644 --- a/packages/server/test/specUtils.ts +++ b/packages/server/test/specUtils.ts @@ -49,9 +49,7 @@ export const getFs = () => { export const supportedConfigFiles = [ 'cypress.json', - 'cypress.js', - 'cypress.component.config.js', - 'cypress.e2e.config.js', + 'cypress.config.js', ] /** diff --git a/packages/server/test/unit/settings_spec.js b/packages/server/test/unit/settings_spec.js index 364645e05a54..cab7a8f1967e 100644 --- a/packages/server/test/unit/settings_spec.js +++ b/packages/server/test/unit/settings_spec.js @@ -21,7 +21,7 @@ describe('lib/settings', () => { afterEach(() => { return fs.removeAsync('cypress.json') - .then(() => fs.removeAsync('cypress.js')) + .then(() => fs.removeAsync('cypress.config.js')) .then(clearCypressJsonCache) }) @@ -104,7 +104,7 @@ describe('lib/settings', () => { }) context('.read', () => { - it('promises cypress.js -> `component` key for component testing runner', function () { + it('promises cypress.config.js -> `component` key for component testing runner', function () { return this.setup(` module.exports = { commmon_setting: true, @@ -113,7 +113,7 @@ describe('lib/settings', () => { } } `, - 'cypress.js') + 'cypress.config.js') .then(() => { return settings.read(projectRoot, { testingType: 'component' }) }).then((obj) => { @@ -127,7 +127,7 @@ describe('lib/settings', () => { }) }) - it('promises cypress.js -> `e2e` key for component testing runner', function () { + it('promises cypress.config.js -> `e2e` key for component testing runner', function () { return this.setup(` module.exports = { commmon_setting: true, @@ -136,7 +136,7 @@ describe('lib/settings', () => { } } `, - 'cypress.js') + 'cypress.config.js') .then(() => { return settings.read(projectRoot, { testingType: 'e2e' }) }).then((obj) => { @@ -150,7 +150,7 @@ describe('lib/settings', () => { }) }) - it('promises cypress.js assumes e2e if no runner specific keys are configured', function () { + it('promises cypress.config.js assumes e2e if no runner specific keys are configured', function () { return this.setup(` module.exports = { commmon_setting: true, @@ -159,7 +159,7 @@ describe('lib/settings', () => { }, } `, - 'cypress.js') + 'cypress.config.js') .then(() => { return settings.read(projectRoot, {}) }).then((obj) => { From 0c89df54cfe7b699176c57a47d0290691ae9339f Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 24 Jun 2021 13:28:14 -0500 Subject: [PATCH 010/204] tests: integration tests --- .../server/test/integration/cypress_spec.js | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/server/test/integration/cypress_spec.js b/packages/server/test/integration/cypress_spec.js index 1b25bf2f2ac8..a8674bbae685 100644 --- a/packages/server/test/integration/cypress_spec.js +++ b/packages/server/test/integration/cypress_spec.js @@ -45,9 +45,27 @@ const env = require(`${root}lib/util/env`) const v = require(`${root}lib/util/validation`) const system = require(`${root}lib/util/system`) const appData = require(`${root}lib/util/app_data`) -const electronApp = require('../../lib/util/electron-app') +const electronApp = require(`${root}lib/util/electron-app`) const savedState = require(`${root}lib/saved_state`) +const supportedConfigFiles = [ + 'cypress.json', + 'cypress.config.js', +] + +/** + * Since we load the cypress.json, cypress.e2e.config.js or cypress.component.config.js + * via `require`, we need to clear the `require.cache` before/after some tests + * to ensure we are not using a cached configuration file. + */ +const clearCypressJsonCache = () => { + Object.keys(require.cache).forEach((key) => { + if (supportedConfigFiles.some((file) => key.includes(file))) { + delete require.cache[key] + } + }) +} + const TYPICAL_BROWSERS = [ { name: 'chrome', @@ -176,6 +194,7 @@ describe('lib/cypress', () => { } Fixtures.remove() + clearCypressJsonCache() }) context('test browsers', () => { From 2efedc186080bde6b382d9e9630431e5a5f1acf9 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 24 Jun 2021 13:33:38 -0500 Subject: [PATCH 011/204] refactor: mutualize tests cahche management --- packages/server/test/cache_helper.js | 19 +++++++++++++++++++ packages/server/test/e2e/7_record_spec.js | 2 +- .../server/test/integration/cypress_spec.js | 19 +------------------ packages/server/test/specUtils.ts | 18 ------------------ packages/server/test/unit/settings_spec.js | 2 +- 5 files changed, 22 insertions(+), 38 deletions(-) create mode 100644 packages/server/test/cache_helper.js diff --git a/packages/server/test/cache_helper.js b/packages/server/test/cache_helper.js new file mode 100644 index 000000000000..0fdc81d17af6 --- /dev/null +++ b/packages/server/test/cache_helper.js @@ -0,0 +1,19 @@ +const supportedConfigFiles = [ + 'cypress.json', + 'cypress.config.js', +] + +/** + * Since we load the cypress.json, cypress.e2e.config.js or cypress.component.config.js + * via `require`, we need to clear the `require.cache` before/after some tests + * to ensure we are not using a cached configuration file. + */ +const clearCypressJsonCache = () => { + Object.keys(require.cache).forEach((key) => { + if (supportedConfigFiles.some((file) => key.includes(file))) { + delete require.cache[key] + } + }) +} + +module.exports = { clearCypressJsonCache } diff --git a/packages/server/test/e2e/7_record_spec.js b/packages/server/test/e2e/7_record_spec.js index 1a1e95fffd76..6b3c722621c0 100644 --- a/packages/server/test/e2e/7_record_spec.js +++ b/packages/server/test/e2e/7_record_spec.js @@ -16,7 +16,7 @@ const { postInstanceTestsResponse, } = require('../support/helpers/serverStub') const { expectRunsToHaveCorrectTimings } = require('../support/helpers/resultsUtils') -const { clearCypressJsonCache } = require('../specUtils') +const { clearCypressJsonCache } = require('../cache_helper') const e2ePath = Fixtures.projectPath('e2e') const outputPath = path.join(e2ePath, 'output.json') diff --git a/packages/server/test/integration/cypress_spec.js b/packages/server/test/integration/cypress_spec.js index a8674bbae685..a6794c058a02 100644 --- a/packages/server/test/integration/cypress_spec.js +++ b/packages/server/test/integration/cypress_spec.js @@ -1,4 +1,5 @@ require('../spec_helper') +const { clearCypressJsonCache } = require('../cache_helper') const R = require('ramda') const _ = require('lodash') @@ -48,24 +49,6 @@ const appData = require(`${root}lib/util/app_data`) const electronApp = require(`${root}lib/util/electron-app`) const savedState = require(`${root}lib/saved_state`) -const supportedConfigFiles = [ - 'cypress.json', - 'cypress.config.js', -] - -/** - * Since we load the cypress.json, cypress.e2e.config.js or cypress.component.config.js - * via `require`, we need to clear the `require.cache` before/after some tests - * to ensure we are not using a cached configuration file. - */ -const clearCypressJsonCache = () => { - Object.keys(require.cache).forEach((key) => { - if (supportedConfigFiles.some((file) => key.includes(file))) { - delete require.cache[key] - } - }) -} - const TYPICAL_BROWSERS = [ { name: 'chrome', diff --git a/packages/server/test/specUtils.ts b/packages/server/test/specUtils.ts index aca0f48fc86f..eae319679fd5 100644 --- a/packages/server/test/specUtils.ts +++ b/packages/server/test/specUtils.ts @@ -46,21 +46,3 @@ export const getFs = () => { return recurse({ root: mockfs.getMockRoot() }, -1).root } - -export const supportedConfigFiles = [ - 'cypress.json', - 'cypress.config.js', -] - -/** - * Since we load the cypress.json, cypress.e2e.config.js or cypress.component.config.js - * via `require`, we need to clear the `require.cache` before/after some tests - * to ensure we are not using a cached configuration file. - */ -export const clearCypressJsonCache = () => { - Object.keys(require.cache).forEach((key) => { - if (supportedConfigFiles.some((file) => key.includes(file))) { - delete require.cache[key] - } - }) -} diff --git a/packages/server/test/unit/settings_spec.js b/packages/server/test/unit/settings_spec.js index cab7a8f1967e..9c7f4e2df7cc 100644 --- a/packages/server/test/unit/settings_spec.js +++ b/packages/server/test/unit/settings_spec.js @@ -3,7 +3,7 @@ require('../spec_helper') const path = require('path') const { fs } = require(`${root}lib/util/fs`) const settings = require(`${root}lib/util/settings`) -const { clearCypressJsonCache } = require('../specUtils') +const { clearCypressJsonCache } = require('../cache_helper') const projectRoot = process.cwd() From db2a0bda0a399a15738cfcf94d440249c8642cb1 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 24 Jun 2021 13:51:30 -0500 Subject: [PATCH 012/204] test: make last test pass (stop mocking internals) --- packages/server/test/integration/cypress_spec.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/server/test/integration/cypress_spec.js b/packages/server/test/integration/cypress_spec.js index a6794c058a02..144d45f8616c 100644 --- a/packages/server/test/integration/cypress_spec.js +++ b/packages/server/test/integration/cypress_spec.js @@ -1795,14 +1795,11 @@ describe('lib/cypress', () => { }) it('reads config from a custom config file', function () { - sinon.stub(fs, 'readJsonAsync') - fs.readJsonAsync.withArgs(path.join(this.pristinePath, this.filename)).resolves({ + fs.outputJSON(path.join(this.pristinePath, this.filename), { env: { foo: 'bar' }, port: 2020, }) - fs.readJsonAsync.callThrough() - return cypress.start([ `--config-file=${this.filename}`, ]) From 27f7344097053687d5465143ed7a14ba636d403a Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 24 Jun 2021 13:52:22 -0500 Subject: [PATCH 013/204] chore: remove test --- npm/vue/cypress/plugins/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/vue/cypress/plugins/index.js b/npm/vue/cypress/plugins/index.js index ddfe2084be83..f505daf3d0a4 100644 --- a/npm/vue/cypress/plugins/index.js +++ b/npm/vue/cypress/plugins/index.js @@ -20,7 +20,7 @@ module.exports = (on, config) => { } require('@cypress/code-coverage/task')(on, config) - // on('dev-server:start', (options) => startDevServer({ options, webpackConfig })) + on('dev-server:start', (options) => startDevServer({ options, webpackConfig })) return config } From 3dc6220344b7073065e6aa185806b9b69f263031 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 24 Jun 2021 14:23:52 -0500 Subject: [PATCH 014/204] test: fix another cache oops --- packages/server/test/integration/cypress_spec.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/server/test/integration/cypress_spec.js b/packages/server/test/integration/cypress_spec.js index 144d45f8616c..373a59ed049d 100644 --- a/packages/server/test/integration/cypress_spec.js +++ b/packages/server/test/integration/cypress_spec.js @@ -1794,6 +1794,10 @@ describe('lib/cypress', () => { this.open = sinon.stub(ServerE2E.prototype, 'open').resolves([]) }) + afterEach(function () { + delete require.cache[path.join(this.pristinePath, this.filename)] + }) + it('reads config from a custom config file', function () { fs.outputJSON(path.join(this.pristinePath, this.filename), { env: { foo: 'bar' }, From cc7cd59215a6d10db5dda515ff2f4d6291f1fb0c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 24 Jun 2021 17:20:41 -0500 Subject: [PATCH 015/204] fix: make cypress.config.js the default --- packages/driver/src/cypress/error_messages.js | 2 +- packages/server/lib/config.js | 4 +- packages/server/lib/config_options.ts | 2 +- packages/server/lib/errors.js | 2 +- packages/server/lib/project-base.ts | 2 +- packages/server/lib/saved_state.js | 19 +++++++-- packages/server/lib/util/settings.js | 40 +++++++++---------- 7 files changed, 43 insertions(+), 28 deletions(-) diff --git a/packages/driver/src/cypress/error_messages.js b/packages/driver/src/cypress/error_messages.js index cd3bba2d3adb..d9feea255755 100644 --- a/packages/driver/src/cypress/error_messages.js +++ b/packages/driver/src/cypress/error_messages.js @@ -21,7 +21,7 @@ const format = (data) => { const formatConfigFile = (configFile) => { if (configFile === false) { - return '`cypress.json` (currently disabled by --config-file=false)' + return '`cypress.config.js` (currently disabled by --config-file=false)' } return `\`${format(configFile)}\`` diff --git a/packages/server/lib/config.js b/packages/server/lib/config.js index 4320f4a4ba94..f74f2f8c110b 100644 --- a/packages/server/lib/config.js +++ b/packages/server/lib/config.js @@ -201,8 +201,10 @@ module.exports = { }, get (projectRoot, options = {}) { + const configFilename = settings.configFile(projectRoot, options) + return Promise.all([ - settings.read(projectRoot, options).then(validateFile('cypress.json')), + settings.read(projectRoot, options).then(validateFile(configFilename)), settings.readEnv(projectRoot).then(validateFile('cypress.env.json')), ]) .spread((settings, envFile) => { diff --git a/packages/server/lib/config_options.ts b/packages/server/lib/config_options.ts index dded69774f92..5dfba66fb036 100644 --- a/packages/server/lib/config_options.ts +++ b/packages/server/lib/config_options.ts @@ -48,7 +48,7 @@ export const options = [ isFolder: true, }, { name: 'configFile', - defaultValue: 'cypress.json', + defaultValue: 'cypress.config.js', validation: v.isStringOrFalse, // not truly internal, but can only be set via cli, // so we don't consider it a "public" option diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 4285f607f57d..0d118418778e 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -918,7 +918,7 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { You can safely remove this option from your config.` case 'EXPERIMENTAL_COMPONENT_TESTING_REMOVED': return stripIndent`\ - The ${chalk.yellow(`\`experimentalComponentTesting\``)} configuration option was removed in Cypress version \`7.0.0\`. Please remove this flag from \`cypress.json\`. + The ${chalk.yellow(`\`experimentalComponentTesting\``)} configuration option was removed in Cypress version \`7.0.0\`. Please remove this flag from \`${arg1}\`. Cypress Component Testing is now a standalone command. You can now run your component tests with: diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 57edf4d5fa2c..41b62012f98f 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -1043,7 +1043,7 @@ export class ProjectBase extends EE { static add (path, options) { // don't cache a project if a non-default configFile is set // https://git.io/JeGyF - if (settings.configFile(path, options) !== 'cypress.json') { + if (settings.configFile(path, options) !== 'cypress.config.js') { return Bluebird.resolve({ path }) } diff --git a/packages/server/lib/saved_state.js b/packages/server/lib/saved_state.js index abef16ff4590..f0773530705e 100644 --- a/packages/server/lib/saved_state.js +++ b/packages/server/lib/saved_state.js @@ -49,13 +49,26 @@ const formStatePath = (projectRoot) => { debug('missing project path, looking for project here') - const cypressJsonPath = cwd('cypress.json') + const cypressConfigJsPath = cwd('cypress.config.js') - return fs.pathExistsAsync(cypressJsonPath) + return fs.pathExistsAsync(cypressConfigJsPath) .then((found) => { if (found) { - debug('found cypress file %s', cypressJsonPath) + debug('found cypress file %s', cypressConfigJsPath) projectRoot = cwd() + } else { + const cypressJsonPath = cwd('cypress.json') + + // if not found with js, try with json + return fs.pathExistsAsync(cypressJsonPath) + .then((foundJson) => { + if (foundJson) { + debug('found cypress file %s', cypressJsonPath) + projectRoot = cwd() + } + + return projectRoot + }) } return projectRoot diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index f140d6999149..3907a01bd894 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -108,16 +108,16 @@ module.exports = { return options.configFile } - if (ls.includes('cypress.json')) { - return 'cypress.json' - } - if (ls.includes('cypress.config.js')) { return 'cypress.config.js' } + if (ls.includes('cypress.json')) { + return 'cypress.json' + } + // Default is to create a new `cypress.json` file if one does not exist. - return 'cypress.json' + return 'cypress.config.js' }, id (projectRoot, options = {}) { @@ -161,27 +161,27 @@ module.exports = { return Promise.resolve({}) } - const file = this.pathToConfigFile(projectRoot, options) - - const requireAsync = (file) => { + const requireAsync = (fileRequired) => { return Promise.try(() => { - return require(file) + return require(fileRequired) }) } + const file = this.pathToConfigFile(projectRoot, options) + return requireAsync(file) - .catch({ code: 'MODULE_NOT_FOUND' }, () => { - return this._write(file, {}) + .catch({ code: 'MODULE_NOT_FOUND' }, { code: 'ENOENT' }, () => { + return this._write(file, `module.exports = {}`) }) - .catch({ code: 'ENOENT' }, () => { - return this._write(file, {}) - }).then((json = {}) => { - if (this.isComponentTesting(options) && 'component' in json) { - json = { ...json, ...json.component } - } - - if (!this.isComponentTesting(options) && 'e2e' in json) { - json = { ...json, ...json.e2e } + .then((json = {}) => { + const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' + + if ((testingType in json)) { + if (typeof json[testingType] === 'object') { + json = { ...json, ...json[testingType] } + } else if (typeof json[testingType] === 'function') { + json = json[testingType](() => {}, json) || json + } } const changed = this._applyRewriteRules(json) From e6b1123ff7f8b5c397afc78fa6911083e4400113 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 25 Jun 2021 11:51:32 -0500 Subject: [PATCH 016/204] tests: fix a bunch --- packages/server/lib/util/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 3907a01bd894..bea87499d4df 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -171,7 +171,7 @@ module.exports = { return requireAsync(file) .catch({ code: 'MODULE_NOT_FOUND' }, { code: 'ENOENT' }, () => { - return this._write(file, `module.exports = {}`) + return this._write(file, {}) }) .then((json = {}) => { const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' From 6371846d2faf2fd22badca5bffb11747ad6577ff Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 25 Jun 2021 13:15:44 -0500 Subject: [PATCH 017/204] tests: fix more tests --- packages/server/test/unit/config_spec.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/server/test/unit/config_spec.js b/packages/server/test/unit/config_spec.js index aab1239f8dcb..1f7390dfe799 100644 --- a/packages/server/test/unit/config_spec.js +++ b/packages/server/test/unit/config_spec.js @@ -7,6 +7,7 @@ const debug = require('debug')('test') const config = require(`${root}lib/config`) const errors = require(`${root}lib/errors`) const configUtil = require(`${root}lib/util/config`) +const { fs } = require(`${root}lib/util/fs`) const findSystemNode = require(`${root}lib/util/find_system_node`) const scaffold = require(`${root}lib/scaffold`) let settings = require(`${root}lib/util/settings`) @@ -55,6 +56,7 @@ describe('lib/config', () => { this.projectRoot = '/_test-output/path/to/project' this.setup = (cypressJson = {}, cypressEnvJson = {}) => { + sinon.stub(fs, 'readdirSync').withArgs(this.projectRoot).returns([]) sinon.stub(settings, 'read').withArgs(this.projectRoot).resolves(cypressJson) sinon.stub(settings, 'readEnv').withArgs(this.projectRoot).resolves(cypressEnvJson) } @@ -142,10 +144,10 @@ describe('lib/config', () => { return this.expectValidationPasses() }) - it('validates cypress.json', function () { + it('validates cypress.config.js', function () { this.setup({ reporter: 5 }) - return this.expectValidationFails('cypress.json') + return this.expectValidationFails('cypress.config.js') }) it('validates cypress.env.json', function () { From 2aa9038b54a668bdb1604abd2115d0d6effec024 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 25 Jun 2021 15:04:43 -0500 Subject: [PATCH 018/204] only create/update config file if its json --- packages/server/lib/util/settings.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index bea87499d4df..3ddf772e8c7f 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -78,6 +78,10 @@ module.exports = { }, _write (file, obj = {}) { + if (!/\.json$/.test(file)) { + return Promise.resolve(obj) + } + return fs.outputJsonAsync(file, obj, { spaces: 2 }) .return(obj) .catch((err) => { @@ -171,7 +175,11 @@ module.exports = { return requireAsync(file) .catch({ code: 'MODULE_NOT_FOUND' }, { code: 'ENOENT' }, () => { - return this._write(file, {}) + if (/\.json$/.test(file)) { + return this._write(file, {}) + } + + return fs.writeFile(file, 'module.exports = {}').then(() => ({})) }) .then((json = {}) => { const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' @@ -184,6 +192,10 @@ module.exports = { } } + if (!/\.json$/.test(file)) { + return json + } + const changed = this._applyRewriteRules(json) // if our object is unchanged From 7bf6c8bf55eacc7073a2a91b39d46e796fb7ae9c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 25 Jun 2021 15:22:21 -0500 Subject: [PATCH 019/204] fix: add missing options param --- packages/server/lib/util/settings.js | 2 +- packages/server/test/unit/settings_spec.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 3ddf772e8c7f..a36294462cf4 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -236,7 +236,7 @@ module.exports = { return Promise.resolve({}) } - return this.read(projectRoot) + return this.read(projectRoot, options) .then((settings) => { _.extend(settings, obj) diff --git a/packages/server/test/unit/settings_spec.js b/packages/server/test/unit/settings_spec.js index 9c7f4e2df7cc..2373b3c74618 100644 --- a/packages/server/test/unit/settings_spec.js +++ b/packages/server/test/unit/settings_spec.js @@ -298,6 +298,8 @@ describe('lib/settings', () => { this.options = { configFile: 'my-test-config-file.json', } + + return fs.ensureDirAsync(this.projectRoot) }) afterEach(function () { From a51dbc81e27cefde4bef8f444606f4e65a83f9fb Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 25 Jun 2021 15:58:23 -0500 Subject: [PATCH 020/204] use the new default cypress.config.js --- packages/server/test/unit/project_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/test/unit/project_spec.js b/packages/server/test/unit/project_spec.js index 08ae53386127..453290a8f8c0 100644 --- a/packages/server/test/unit/project_spec.js +++ b/packages/server/test/unit/project_spec.js @@ -39,7 +39,7 @@ describe('lib/project-base', () => { }) sinon.stub(runEvents, 'execute').resolves() - sinon.stub(fs, 'readdirSync').returns(['cypress.json']) + sinon.stub(fs, 'readdirSync').returns(['cypress.config.js']) return settings.read(this.todosPath).then((obj = {}) => { ({ projectId: this.projectId } = obj) From 956184baf33cae531f2143a6f7b6e1218f8e1749 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 25 Jun 2021 17:54:30 -0500 Subject: [PATCH 021/204] finally all project tests are passing --- packages/server/lib/project-base.ts | 3 ++- packages/server/test/unit/project_spec.js | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 41b62012f98f..b0390db8dacf 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -1043,7 +1043,8 @@ export class ProjectBase extends EE { static add (path, options) { // don't cache a project if a non-default configFile is set // https://git.io/JeGyF - if (settings.configFile(path, options) !== 'cypress.config.js') { + + if (!['cypress.config.js', 'cypress.json'].includes(settings.configFile(path, options))) { return Bluebird.resolve({ path }) } diff --git a/packages/server/test/unit/project_spec.js b/packages/server/test/unit/project_spec.js index 453290a8f8c0..a9e514201db7 100644 --- a/packages/server/test/unit/project_spec.js +++ b/packages/server/test/unit/project_spec.js @@ -39,7 +39,7 @@ describe('lib/project-base', () => { }) sinon.stub(runEvents, 'execute').resolves() - sinon.stub(fs, 'readdirSync').returns(['cypress.config.js']) + sinon.stub(fs, 'readdirSync').returns(['cypress.json']) return settings.read(this.todosPath).then((obj = {}) => { ({ projectId: this.projectId } = obj) @@ -55,7 +55,7 @@ describe('lib/project-base', () => { }) afterEach(function () { - Fixtures.remove() + // Fixtures.remove() if (this.project) { this.project.close() @@ -893,7 +893,7 @@ This option will not have an effect in Some-other-name. Tests that rely on web s projectRoot: this.todosPath, } - return settings.write(this.idsPath, { port: 2020 }) + return settings.write(this.idsPath, { port: 2020 }, { configFile: 'cypress.json' }) }) it('returns fully qualified url when spec exists', function () { From 6bd9e08ea2993a602095a5be11e3bafa2b278b24 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 28 Jun 2021 09:18:54 -0500 Subject: [PATCH 022/204] test: fix FQDN driver tests --- packages/driver/cypress/integration/commands/request_spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/driver/cypress/integration/commands/request_spec.js b/packages/driver/cypress/integration/commands/request_spec.js index b466141e3613..b65ebd7735f2 100644 --- a/packages/driver/cypress/integration/commands/request_spec.js +++ b/packages/driver/cypress/integration/commands/request_spec.js @@ -830,7 +830,7 @@ describe('src/cy/commands/request', () => { expect(this.logs.length).to.eq(1) expect(lastLog.get('error')).to.eq(err) expect(lastLog.get('state')).to.eq('failed') - expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` in `cypress.json`. Neither of those values were present.') + expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` in `cypress.config.js`. Neither of those values were present.') expect(err.docsUrl).to.eq('https://on.cypress.io/request') done() @@ -851,7 +851,7 @@ describe('src/cy/commands/request', () => { expect(this.logs.length).to.eq(1) expect(lastLog.get('error')).to.eq(err) expect(lastLog.get('state')).to.eq('failed') - expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` in `cypress.json` (currently disabled by --config-file=false). Neither of those values were present.') + expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` in `cypress.config.js` (currently disabled by --config-file=false). Neither of those values were present.') done() }) From 5cf643352759ba2ee9c7dff5e619ec1bfb340c31 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 28 Jun 2021 14:38:12 -0500 Subject: [PATCH 023/204] feat: allow users to supply e2e and components as functions --- packages/server/lib/plugins/child/index.js | 4 ++-- packages/server/lib/plugins/child/run_plugins.js | 8 ++++++-- packages/server/lib/plugins/index.js | 4 ++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/server/lib/plugins/child/index.js b/packages/server/lib/plugins/child/index.js index 9687e288194a..4d0922e44584 100644 --- a/packages/server/lib/plugins/child/index.js +++ b/packages/server/lib/plugins/child/index.js @@ -3,6 +3,6 @@ require('graceful-fs').gracefulify(require('fs')) require('../../util/suppress_warnings').suppress() const ipc = require('../util').wrapIpc(process) -const { file: pluginsFile, projectRoot } = require('minimist')(process.argv.slice(2)) +const { file: pluginsFile, projectRoot, functionName } = require('minimist')(process.argv.slice(2)) -require('./run_plugins')(ipc, pluginsFile, projectRoot) +require('./run_plugins')(ipc, pluginsFile, projectRoot, functionName) diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js index 5b0d68abd28f..2d73395523ed 100644 --- a/packages/server/lib/plugins/child/run_plugins.js +++ b/packages/server/lib/plugins/child/run_plugins.js @@ -140,7 +140,11 @@ const execute = (ipc, event, ids, args = []) => { let tsRegistered = false -const runPlugins = (ipc, pluginsFile, projectRoot) => { +function getPluginsFunction (pluginsFile, functionName) { + return functionName ? require(pluginsFile)[functionName] : require(pluginsFile) +} + +const runPlugins = (ipc, pluginsFile, projectRoot, functionName) => { debug('pluginsFile:', pluginsFile) debug('project root:', projectRoot) if (!projectRoot) { @@ -172,7 +176,7 @@ const runPlugins = (ipc, pluginsFile, projectRoot) => { try { debug('require pluginsFile') - plugins = require(pluginsFile) + plugins = getPluginsFunction(pluginsFile, functionName) // Handle export default () => {} if (plugins && typeof plugins.default === 'function') { diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index 5573cdf35616..fd03fd79900e 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -86,6 +86,10 @@ const init = (config, options) => { stdio: 'inherit', } + if (typeof config[config.testingType] === 'function') { + childArguments.push('--functionName', config.testingType) + } + if (config.resolvedNodePath) { debug('launching using custom node version %o', _.pick(config, ['resolvedNodePath', 'resolvedNodeVersion'])) childOptions.execPath = config.resolvedNodePath From 0b8818dc54c151b09f1e5d1e2bd8ed89b3022849 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 28 Jun 2021 16:31:47 -0500 Subject: [PATCH 024/204] fix some issues --- .../server/__snapshots__/3_plugins_spec.js | 152 ++++++++++++++++++ packages/server/lib/config_options.ts | 8 +- packages/server/lib/util/validation.js | 10 ++ packages/server/test/e2e/3_plugins_spec.js | 11 ++ .../plugin-config-js/cypress.config.js | 18 +++ .../cypress/integration/app_spec.js | 16 ++ 6 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js create mode 100644 packages/server/test/support/fixtures/projects/plugin-config-js/cypress/integration/app_spec.js diff --git a/packages/server/__snapshots__/3_plugins_spec.js b/packages/server/__snapshots__/3_plugins_spec.js index 248834c535d2..0646319276a8 100644 --- a/packages/server/__snapshots__/3_plugins_spec.js +++ b/packages/server/__snapshots__/3_plugins_spec.js @@ -510,3 +510,155 @@ exports['e2e plugins does not report more screenshots than exist if user overwri ` + +exports['e2e-plugins fails when there is an async error inside an event handler 1'] = ` + +==================================================================================================== + + (Run Starting) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Cypress: 1.2.3 │ + │ Browser: FooBrowser 88 │ + │ Specs: 1 found (app_spec.js) │ + │ Searched: cypress/integration/app_spec.js │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +──────────────────────────────────────────────────────────────────────────────────────────────────── + + Running: app_spec.js (1 of 1) + +The following error was thrown by a plugin. We stopped running your tests because a plugin crashed. Please check your plugins file (\`/foo/bar/.projects/plugins-async-error/cypress/plugins/index.js\`) + + Error: Async error from plugins file + [stack trace lines] + + (Results) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Tests: 0 │ + │ Passing: 0 │ + │ Failing: 1 │ + │ Pending: 0 │ + │ Skipped: 0 │ + │ Screenshots: 0 │ + │ Video: false │ + │ Duration: X seconds │ + │ Spec Ran: app_spec.js │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +==================================================================================================== + + (Run Finished) + + + Spec Tests Passing Failing Pending Skipped + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ ✖ app_spec.js XX:XX - - 1 - - │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + ✖ 1 of 1 failed (100%) XX:XX - - 1 - - + + +` + +exports['e2e-plugins can modify config from plugins 1'] = ` + +==================================================================================================== + + (Run Starting) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Cypress: 1.2.3 │ + │ Browser: FooBrowser 88 │ + │ Specs: 1 found (app_spec.js) │ + │ Searched: cypress/integration/app_spec.js │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +──────────────────────────────────────────────────────────────────────────────────────────────────── + + Running: app_spec.js (1 of 1) + + + ✓ overrides config + ✓ overrides env + + 2 passing + + + (Results) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Tests: 2 │ + │ Passing: 2 │ + │ Failing: 0 │ + │ Pending: 0 │ + │ Skipped: 0 │ + │ Screenshots: 0 │ + │ Video: true │ + │ Duration: X seconds │ + │ Spec Ran: app_spec.js │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + (Video) + + - Started processing: Compressing to 20 CRF + - Finished processing: /XXX/XXX/XXX/cypress/videos/app_spec.js.mp4 (X second) + + +==================================================================================================== + + (Run Finished) + + + Spec Tests Passing Failing Pending Skipped + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ ✔ app_spec.js XX:XX 2 2 - - - │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + ✔ All specs passed! XX:XX 2 2 - - - + + +` + +exports['e2e-plugins catches invalid viewportWidth returned from plugins 1'] = ` +An invalid configuration value returned from the plugins file: \`cypress/plugins/index.js\` + +Expected \`viewportWidth\` to be a number. Instead the value was: \`"foo"\` + +` + +exports['e2e-plugins catches invalid browsers list returned from plugins 1'] = ` +An invalid configuration value returned from the plugins file: \`cypress/plugins/index.js\` + +Expected at least one browser + +` + +exports['e2e-plugins catches invalid browser returned from plugins 1'] = ` +An invalid configuration value returned from the plugins file: \`cypress/plugins/index.js\` + +Found an error while validating the \`browsers\` list. Expected \`displayName\` to be a non-empty string. Instead the value was: \`{"name":"browser name","family":"chromium"}\` + +` + +exports['e2e-plugins can filter browsers from config 1'] = ` +Can't run because you've entered an invalid browser name. + +Browser: 'chrome' was not found on your system or is not supported by Cypress. + +Cypress supports the following browsers: +- chrome +- chromium +- edge +- electron +- firefox + +You can also use a custom browser: https://on.cypress.io/customize-browsers + +Available browsers found on your system are: +- electron + +` diff --git a/packages/server/lib/config_options.ts b/packages/server/lib/config_options.ts index 5dfba66fb036..e7ace2f06ff1 100644 --- a/packages/server/lib/config_options.ts +++ b/packages/server/lib/config_options.ts @@ -39,8 +39,8 @@ export const options = [ }, { name: 'component', // runner-ct overrides - defaultValue: {}, - validation: v.isValidConfig, + defaultValue: null, + validation: v.functionOverrides, }, { name: 'componentFolder', defaultValue: 'cypress/component', @@ -69,8 +69,8 @@ export const options = [ }, { name: 'e2e', // e2e runner overrides - defaultValue: {}, - validation: v.isValidConfig, + defaultValue: null, + validation: v.functionOverrides, }, { name: 'env', validation: v.isPlainObject, diff --git a/packages/server/lib/util/validation.js b/packages/server/lib/util/validation.js index 334f92f5809b..d24c865c5e1d 100644 --- a/packages/server/lib/util/validation.js +++ b/packages/server/lib/util/validation.js @@ -185,6 +185,14 @@ const isOneOf = (...values) => { } } +const isOverrideFunction = (key, config) => { + if (typeof config === 'function') { + return true + } + + return isValidConfig(key, config) +} + module.exports = { isValidBrowser, @@ -196,6 +204,8 @@ module.exports = { isValidConfig, + isOverrideFunction, + isPlainObject, isNumber (key, value) { diff --git a/packages/server/test/e2e/3_plugins_spec.js b/packages/server/test/e2e/3_plugins_spec.js index cd81cfa510f8..85862247251c 100644 --- a/packages/server/test/e2e/3_plugins_spec.js +++ b/packages/server/test/e2e/3_plugins_spec.js @@ -49,6 +49,17 @@ describe('e2e plugins', function () { }) }) + it('can config plugins directly in the cypress.config.js', function () { + return e2e.exec(this, { + spec: 'app_spec.js', + env: 'foo=foo,bar=bar', + config: { pageLoadTimeout: 10000 }, + project: Fixtures.projectPath('plugin-config-js'), + sanitizeScreenshotDimensions: true, + snapshot: true, + }) + }) + it('passes version correctly', function () { return e2e.exec(this, { project: Fixtures.projectPath('plugin-config-version'), diff --git a/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js b/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js new file mode 100644 index 000000000000..8a1e232e6e32 --- /dev/null +++ b/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js @@ -0,0 +1,18 @@ +module.exports = { + e2e: (on, config) => { + // eslint-disable-next-line no-undef + console.log(config) + + return new Promise((resolve) => { + setTimeout(resolve, 100) + }) + .then(() => { + config.defaultCommandTimeout = 500 + config.videoCompression = 20 + config.env = config.env || {} + config.env.foo = 'bar' + + return config + }) + }, +} diff --git a/packages/server/test/support/fixtures/projects/plugin-config-js/cypress/integration/app_spec.js b/packages/server/test/support/fixtures/projects/plugin-config-js/cypress/integration/app_spec.js new file mode 100644 index 000000000000..83c0712595fd --- /dev/null +++ b/packages/server/test/support/fixtures/projects/plugin-config-js/cypress/integration/app_spec.js @@ -0,0 +1,16 @@ +it('overrides config', () => { + // overrides come from plugins + expect(Cypress.config('defaultCommandTimeout')).to.eq(500) + expect(Cypress.config('videoCompression')).to.eq(20) + + // overrides come from CLI + expect(Cypress.config('pageLoadTimeout')).to.eq(10000) +}) + +it('overrides env', () => { + // overrides come from plugins + expect(Cypress.env('foo')).to.eq('bar') + + // overrides come from CLI + expect(Cypress.env('bar')).to.eq('bar') +}) From 4164104b3c6186f8af00aae39df4aa8a85a6c34d Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 28 Jun 2021 17:14:15 -0500 Subject: [PATCH 025/204] make it work --- packages/server/lib/plugins/child/run_plugins.js | 1 + packages/server/lib/plugins/index.js | 13 ++++++++++--- packages/server/lib/util/settings.js | 2 -- .../projects/plugin-config-js/cypress.config.js | 3 --- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js index 2d73395523ed..ebbde5323c46 100644 --- a/packages/server/lib/plugins/child/run_plugins.js +++ b/packages/server/lib/plugins/child/run_plugins.js @@ -146,6 +146,7 @@ function getPluginsFunction (pluginsFile, functionName) { const runPlugins = (ipc, pluginsFile, projectRoot, functionName) => { debug('pluginsFile:', pluginsFile) + debug('functionName:', pluginsFile) debug('project root:', projectRoot) if (!projectRoot) { throw new Error('Unexpected: projectRoot should be a string') diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index fd03fd79900e..dc7b3b57eb97 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -81,13 +81,20 @@ const init = (config, options) => { const pluginsFile = config.pluginsFile || path.join(__dirname, 'child', 'default_plugins_file.js') const childIndexFilename = path.join(__dirname, 'child', 'index.js') - const childArguments = ['--file', pluginsFile, '--projectRoot', options.projectRoot] + const childArguments = ['--projectRoot', options.projectRoot] const childOptions = { stdio: 'inherit', } - if (typeof config[config.testingType] === 'function') { - childArguments.push('--functionName', config.testingType) + const testingType = config.testingType || 'e2e' + + if (typeof config[testingType] === 'function') { + childArguments.push( + '--functionName', testingType, + '--file', options.configFile, + ) + } else { + childArguments.push('--file', pluginsFile) } if (config.resolvedNodePath) { diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index a36294462cf4..5a7ffe3d24ba 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -187,8 +187,6 @@ module.exports = { if ((testingType in json)) { if (typeof json[testingType] === 'object') { json = { ...json, ...json[testingType] } - } else if (typeof json[testingType] === 'function') { - json = json[testingType](() => {}, json) || json } } diff --git a/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js b/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js index 8a1e232e6e32..ff739f4d7009 100644 --- a/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js +++ b/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js @@ -1,8 +1,5 @@ module.exports = { e2e: (on, config) => { - // eslint-disable-next-line no-undef - console.log(config) - return new Promise((resolve) => { setTimeout(resolve, 100) }) From 4b1f72317564849060a80c89fc5e350401b2d634 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 28 Jun 2021 22:35:55 -0500 Subject: [PATCH 026/204] use a testingType there and avoid recreating pluginsfile --- npm/vue/cypress.config.js | 32 ++++++++++++++++++++++++++++ npm/vue/cypress.json | 9 -------- npm/vue/cypress/plugins/index.js | 26 ---------------------- packages/server/lib/config.js | 4 ++-- packages/server/lib/plugins/index.js | 2 +- packages/server/lib/util/settings.js | 2 +- 6 files changed, 36 insertions(+), 39 deletions(-) create mode 100644 npm/vue/cypress.config.js delete mode 100644 npm/vue/cypress.json delete mode 100644 npm/vue/cypress/plugins/index.js diff --git a/npm/vue/cypress.config.js b/npm/vue/cypress.config.js new file mode 100644 index 000000000000..6d2dcac6f3bc --- /dev/null +++ b/npm/vue/cypress.config.js @@ -0,0 +1,32 @@ +const { startDevServer } = require('@cypress/webpack-dev-server') + +module.exports = { + viewportWidth: 500, + viewportHeight: 500, + video: false, + responseTimeout: 2500, + projectId: '134ej7', + testFiles: '**/*spec.js', + experimentalFetchPolyfill: true, + component: (on, config) => { + if (config.testingType !== 'component') { + throw Error(`This is a component testing project. testingType should be 'component'. Received ${config.testingType}`) + } + + const webpackConfig = require('./webpack.config') + + if (!webpackConfig.resolve) { + webpackConfig.resolve = {} + } + + webpackConfig.resolve.alias = { + ...webpackConfig.resolve.alias, + '@vue/compiler-core$': '@vue/compiler-core/dist/compiler-core.cjs.js', + } + + require('@cypress/code-coverage/task')(on, config) + on('dev-server:start', (options) => startDevServer({ options, webpackConfig })) + + return config + }, +} diff --git a/npm/vue/cypress.json b/npm/vue/cypress.json deleted file mode 100644 index 9aa5956f423b..000000000000 --- a/npm/vue/cypress.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "viewportWidth": 500, - "viewportHeight": 500, - "video": false, - "responseTimeout": 2500, - "projectId": "134ej7", - "testFiles": "**/*spec.js", - "experimentalFetchPolyfill": true -} \ No newline at end of file diff --git a/npm/vue/cypress/plugins/index.js b/npm/vue/cypress/plugins/index.js deleted file mode 100644 index f505daf3d0a4..000000000000 --- a/npm/vue/cypress/plugins/index.js +++ /dev/null @@ -1,26 +0,0 @@ -/// -const { startDevServer } = require('@cypress/webpack-dev-server') -const webpackConfig = require('../../webpack.config') - -/** - * @type Cypress.PluginConfig - */ -module.exports = (on, config) => { - if (config.testingType !== 'component') { - throw Error(`This is a component testing project. testingType should be 'component'. Received ${config.testingType}`) - } - - if (!webpackConfig.resolve) { - webpackConfig.resolve = {} - } - - webpackConfig.resolve.alias = { - ...webpackConfig.resolve.alias, - '@vue/compiler-core$': '@vue/compiler-core/dist/compiler-core.cjs.js', - } - - require('@cypress/code-coverage/task')(on, config) - on('dev-server:start', (options) => startDevServer({ options, webpackConfig })) - - return config -} diff --git a/packages/server/lib/config.js b/packages/server/lib/config.js index f74f2f8c110b..96c77003f4c2 100644 --- a/packages/server/lib/config.js +++ b/packages/server/lib/config.js @@ -245,7 +245,7 @@ module.exports = { config.rawJson = _.cloneDeep(config) - _.extend(config, _.pick(options, 'configFile', 'morgan', 'isTextTerminal', 'socketId', 'report', 'browsers')) + _.extend(config, _.pick(options, 'configFile', 'morgan', 'isTextTerminal', 'socketId', 'report', 'browsers', 'testingType')) debug('merged config with options, got %o', config) _ @@ -574,7 +574,7 @@ module.exports = { // - throw an error, because it should be there if the user // explicitly set it setPluginsFile: Promise.method((obj) => { - if (!obj.pluginsFile) { + if (!obj.pluginsFile || typeof obj[obj.testingType] === 'function') { return obj } diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index dc7b3b57eb97..c229c1604850 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -86,7 +86,7 @@ const init = (config, options) => { stdio: 'inherit', } - const testingType = config.testingType || 'e2e' + const testingType = options.testingType || 'e2e' if (typeof config[testingType] === 'function') { childArguments.push( diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 5a7ffe3d24ba..e266a3e47f92 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -174,7 +174,7 @@ module.exports = { const file = this.pathToConfigFile(projectRoot, options) return requireAsync(file) - .catch({ code: 'MODULE_NOT_FOUND' }, { code: 'ENOENT' }, () => { + .catch({ code: 'ENOENT' }, (e) => { if (/\.json$/.test(file)) { return this._write(file, {}) } From bd07a4976216878dfdbef124d12c336409271bec Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 29 Jun 2021 10:03:44 -0500 Subject: [PATCH 027/204] feat: allow use of typescript for cypres.config.ts --- .../{cypress.config.js => cypress.config.ts} | 7 +++--- npm/vue/package.json | 2 +- packages/server/lib/config_options.ts | 2 +- .../server/lib/plugins/child/run_plugins.js | 2 +- packages/server/lib/project-base.ts | 2 +- packages/server/lib/util/settings.js | 23 +++++++++++++++---- .../lib/{plugins/child => util}/ts_node.js | 2 +- packages/server/test/e2e/3_plugins_spec.js | 17 ++++++++++++++ yarn.lock | 12 +++++----- 9 files changed, 50 insertions(+), 19 deletions(-) rename npm/vue/{cypress.config.js => cypress.config.ts} (91%) rename packages/server/lib/{plugins/child => util}/ts_node.js (95%) diff --git a/npm/vue/cypress.config.js b/npm/vue/cypress.config.ts similarity index 91% rename from npm/vue/cypress.config.js rename to npm/vue/cypress.config.ts index 6d2dcac6f3bc..1b72c5a48dc7 100644 --- a/npm/vue/cypress.config.js +++ b/npm/vue/cypress.config.ts @@ -1,6 +1,7 @@ const { startDevServer } = require('@cypress/webpack-dev-server') +const webpackConfig = require('./webpack.config') -module.exports = { +export default { viewportWidth: 500, viewportHeight: 500, video: false, @@ -13,8 +14,6 @@ module.exports = { throw Error(`This is a component testing project. testingType should be 'component'. Received ${config.testingType}`) } - const webpackConfig = require('./webpack.config') - if (!webpackConfig.resolve) { webpackConfig.resolve = {} } @@ -29,4 +28,4 @@ module.exports = { return config }, -} +} as any diff --git a/npm/vue/package.json b/npm/vue/package.json index 806eaf940e55..2692bcce30e6 100644 --- a/npm/vue/package.json +++ b/npm/vue/package.json @@ -43,7 +43,7 @@ "rollup-plugin-istanbul": "2.0.1", "rollup-plugin-typescript2": "^0.29.0", "tailwindcss": "1.1.4", - "typescript": "3.9.6", + "typescript": "^4.2.4", "vue": "3.0.11", "vue-i18n": "9.0.0-rc.6", "vue-loader": "16.1.2", diff --git a/packages/server/lib/config_options.ts b/packages/server/lib/config_options.ts index e7ace2f06ff1..a7a3f3931cca 100644 --- a/packages/server/lib/config_options.ts +++ b/packages/server/lib/config_options.ts @@ -48,7 +48,7 @@ export const options = [ isFolder: true, }, { name: 'configFile', - defaultValue: 'cypress.config.js', + defaultValue: null, validation: v.isStringOrFalse, // not truly internal, but can only be set via cli, // so we don't consider it a "public" option diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js index ebbde5323c46..2a58d9a78c0d 100644 --- a/packages/server/lib/plugins/child/run_plugins.js +++ b/packages/server/lib/plugins/child/run_plugins.js @@ -7,11 +7,11 @@ const Promise = require('bluebird') const preprocessor = require('./preprocessor') const devServer = require('./dev-server') const resolve = require('../../util/resolve') +const tsNodeUtil = require('../../util/ts_node') const browserLaunch = require('./browser_launch') const task = require('./task') const util = require('../util') const validateEvent = require('./validate_event') -const tsNodeUtil = require('./ts_node') let registeredEventsById = {} let registeredEventsByName = {} diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index b0390db8dacf..aac2e51dcf69 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -1044,7 +1044,7 @@ export class ProjectBase extends EE { // don't cache a project if a non-default configFile is set // https://git.io/JeGyF - if (!['cypress.config.js', 'cypress.json'].includes(settings.configFile(path, options))) { + if (!['cypress.config.ts', 'cypress.config.js', 'cypress.json'].includes(settings.configFile(path, options))) { return Bluebird.resolve({ path }) } diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index e266a3e47f92..8ba8cfd014dd 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -3,14 +3,16 @@ const Promise = require('bluebird') const path = require('path') const errors = require('../errors') const log = require('../log') -const { fs } = require('../util/fs') +const { fs } = require('./fs') +const tsNodeUtil = require('./ts_node') + +let tsRegistered = false // TODO: // think about adding another PSemaphore // here since we can read + write the // settings at the same time something else // is potentially reading it - const flattenCypress = (obj) => { return obj.cypress ? obj.cypress : undefined } @@ -112,6 +114,10 @@ module.exports = { return options.configFile } + if (ls.includes('cypress.config.ts')) { + return 'cypress.config.ts' + } + if (ls.includes('cypress.config.js')) { return 'cypress.config.js' } @@ -120,7 +126,7 @@ module.exports = { return 'cypress.json' } - // Default is to create a new `cypress.json` file if one does not exist. + // Default is to create a new `cypress.config.js` file if one does not exist. return 'cypress.config.js' }, @@ -165,9 +171,18 @@ module.exports = { return Promise.resolve({}) } + if (!tsRegistered) { + tsNodeUtil.register(projectRoot, options.configFile) + + // ensure typescript is only registered once + tsRegistered = true + } + const requireAsync = (fileRequired) => { return Promise.try(() => { - return require(fileRequired) + const exp = require(fileRequired) + + return exp.default || exp }) } diff --git a/packages/server/lib/plugins/child/ts_node.js b/packages/server/lib/util/ts_node.js similarity index 95% rename from packages/server/lib/plugins/child/ts_node.js rename to packages/server/lib/util/ts_node.js index 49992c8a2856..f22e6af317ca 100644 --- a/packages/server/lib/plugins/child/ts_node.js +++ b/packages/server/lib/util/ts_node.js @@ -1,7 +1,7 @@ const debug = require('debug')('cypress:server:ts-node') const path = require('path') const tsnode = require('ts-node') -const resolve = require('../../util/resolve') +const resolve = require('./resolve') const getTsNodeOptions = (tsPath, pluginsFile) => { return { diff --git a/packages/server/test/e2e/3_plugins_spec.js b/packages/server/test/e2e/3_plugins_spec.js index 85862247251c..9e8cbab8e559 100644 --- a/packages/server/test/e2e/3_plugins_spec.js +++ b/packages/server/test/e2e/3_plugins_spec.js @@ -2,6 +2,7 @@ const path = require('path') const e2e = require('../support/helpers/e2e').default const Fixtures = require('../support/helpers/fixtures') +const { fs } = require(`${root}lib/util/fs`) const e2eProject = Fixtures.projectPath('e2e') @@ -60,6 +61,22 @@ describe('e2e plugins', function () { }) }) + it('does not create a new pluginsFile if cypress.config.js is properly configured', function () { + const jsProjectPath = Fixtures.projectPath('plugin-config-js') + + return e2e.exec(this, { + spec: 'app_spec.js', + env: 'foo=foo,bar=bar', + config: { pageLoadTimeout: 10000 }, + project: jsProjectPath, + sanitizeScreenshotDimensions: true, + }).then(() => { + return fs.exists(path.join(jsProjectPath, 'cypress/plugins/index.js')) + }).then((newPluginsFileExists) => { + expect(newPluginsFileExists).to.be.false + }) + }) + it('passes version correctly', function () { return e2e.exec(this, { project: Fixtures.projectPath('plugin-config-version'), diff --git a/yarn.lock b/yarn.lock index 166fccf620cd..c05c6d26e6eb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -37655,17 +37655,12 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@3.9.6: - version "3.9.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a" - integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw== - typescript@4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72" integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA== -typescript@4.2.4, typescript@^4.2.3, typescript@~4.2.4: +typescript@4.2.4, typescript@~4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== @@ -37675,6 +37670,11 @@ typescript@^3.9.7: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.9.tgz#e69905c54bc0681d0518bd4d587cc6f2d0b1a674" integrity sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w== +typescript@^4.2.3, typescript@^4.2.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.4.tgz#3f85b986945bcf31071decdd96cf8bfa65f9dcbc" + integrity sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew== + ua-parser-js@^0.7.18: version "0.7.24" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c" From 5a27ad15162e8471b1735306e91bbbd2fbf0db7f Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 29 Jun 2021 11:02:30 -0500 Subject: [PATCH 028/204] fix tsnode registration --- packages/server/lib/plugins/child/run_plugins.js | 12 ++++++------ packages/server/lib/util/resolve.js | 2 +- packages/server/lib/util/settings.js | 14 +++++++------- packages/server/lib/util/ts_node.js | 4 +++- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js index 2a58d9a78c0d..d14d39dd7cbb 100644 --- a/packages/server/lib/plugins/child/run_plugins.js +++ b/packages/server/lib/plugins/child/run_plugins.js @@ -141,7 +141,10 @@ const execute = (ipc, event, ids, args = []) => { let tsRegistered = false function getPluginsFunction (pluginsFile, functionName) { - return functionName ? require(pluginsFile)[functionName] : require(pluginsFile) + const exp = require(pluginsFile) + const resolvedExport = exp.default || exp + + return functionName ? resolvedExport[functionName] : resolvedExport } const runPlugins = (ipc, pluginsFile, projectRoot, functionName) => { @@ -176,13 +179,10 @@ const runPlugins = (ipc, pluginsFile, projectRoot, functionName) => { } try { - debug('require pluginsFile') + debug('require pluginsFile "%s", functionName "%s"', pluginsFile, functionName) plugins = getPluginsFunction(pluginsFile, functionName) - // Handle export default () => {} - if (plugins && typeof plugins.default === 'function') { - plugins = plugins.default - } + debug('plugins %o', plugins) } catch (err) { debug('failed to require pluginsFile:\n%s', err.stack) ipc.send('load:error', 'PLUGINS_FILE_ERROR', pluginsFile, err.stack) diff --git a/packages/server/lib/util/resolve.js b/packages/server/lib/util/resolve.js index 3e241644606a..1b71c64558e1 100644 --- a/packages/server/lib/util/resolve.js +++ b/packages/server/lib/util/resolve.js @@ -1,6 +1,6 @@ const resolve = require('resolve') const env = require('./env') -const debug = require('debug')('cypress:server:plugins') +const debug = require('debug')('cypress:server:util:resolve') module.exports = { /** diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 8ba8cfd014dd..8f04a17eafa9 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -171,13 +171,6 @@ module.exports = { return Promise.resolve({}) } - if (!tsRegistered) { - tsNodeUtil.register(projectRoot, options.configFile) - - // ensure typescript is only registered once - tsRegistered = true - } - const requireAsync = (fileRequired) => { return Promise.try(() => { const exp = require(fileRequired) @@ -188,6 +181,13 @@ module.exports = { const file = this.pathToConfigFile(projectRoot, options) + if (!tsRegistered) { + tsNodeUtil.register(projectRoot, file) + + // ensure typescript is only registered once + tsRegistered = true + } + return requireAsync(file) .catch({ code: 'ENOENT' }, (e) => { if (/\.json$/.test(file)) { diff --git a/packages/server/lib/util/ts_node.js b/packages/server/lib/util/ts_node.js index f22e6af317ca..ced991739751 100644 --- a/packages/server/lib/util/ts_node.js +++ b/packages/server/lib/util/ts_node.js @@ -18,13 +18,15 @@ const getTsNodeOptions = (tsPath, pluginsFile) => { const register = (projectRoot, pluginsFile) => { try { + debug('projectRoot path: %s', projectRoot) const tsPath = resolve.typescript(projectRoot) if (!tsPath) return + debug('typescript path: %s', tsPath) + const tsOptions = getTsNodeOptions(tsPath, pluginsFile) - debug('typescript path: %s', tsPath) debug('registering project TS with options %o', tsOptions) require('tsconfig-paths/register') From 33239102a05682514bad926ffa00a65546a8a40c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 29 Jun 2021 14:08:59 -0500 Subject: [PATCH 029/204] feat: add defineConfig function --- cli/lib/cypress.js | 4 ++++ cli/types/cypress-npm-api.d.ts | 2 ++ cli/types/cypress.d.ts | 6 ++++-- npm/vue/cypress.config.ts | 9 +++++---- npm/vue/{webpack.config.js => webpack.config.ts} | 10 ++++++---- 5 files changed, 21 insertions(+), 10 deletions(-) rename npm/vue/{webpack.config.js => webpack.config.ts} (88%) diff --git a/cli/lib/cypress.js b/cli/lib/cypress.js index d87e11277c03..d5278ac85fe3 100644 --- a/cli/lib/cypress.js +++ b/cli/lib/cypress.js @@ -68,6 +68,10 @@ const cypressModuleApi = { return cli.parseRunCommand(args) }, }, + + defineConfig (config) { + return config + }, } module.exports = cypressModuleApi diff --git a/cli/types/cypress-npm-api.d.ts b/cli/types/cypress-npm-api.d.ts index 9d0ed04c970e..3287635c8547 100644 --- a/cli/types/cypress-npm-api.d.ts +++ b/cli/types/cypress-npm-api.d.ts @@ -377,6 +377,8 @@ declare module 'cypress' { * Cypress does */ cli: CypressCommandLine.CypressCliParser + + defineConfig(config: Cypress.ConfigOptions): Cypress.ConfigOptions } // export Cypress NPM module interface diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 15ecae0d7655..88d2570ebd2e 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2682,15 +2682,17 @@ declare namespace Cypress { * Override default config options for Component Testing runner. * @default {} */ - component: ResolvedConfigOptions + component: ResolvedConfigOptions | TestingTypeFunctions /** * Override default config options for E2E Testing runner. * @default {} */ - e2e: ResolvedConfigOptions + e2e: ResolvedConfigOptions | TestingTypeFunctions } + type TestingTypeFunctions = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptions | undefined) + /** * Options appended to config object on runtime. */ diff --git a/npm/vue/cypress.config.ts b/npm/vue/cypress.config.ts index 1b72c5a48dc7..fea836238899 100644 --- a/npm/vue/cypress.config.ts +++ b/npm/vue/cypress.config.ts @@ -1,7 +1,8 @@ -const { startDevServer } = require('@cypress/webpack-dev-server') -const webpackConfig = require('./webpack.config') +import { defineConfig } from 'cypress' +import { startDevServer } from '@cypress/webpack-dev-server' +import webpackConfig from './webpack.config' -export default { +export default defineConfig({ viewportWidth: 500, viewportHeight: 500, video: false, @@ -28,4 +29,4 @@ export default { return config }, -} as any +} as Cypress.ConfigOptions) diff --git a/npm/vue/webpack.config.js b/npm/vue/webpack.config.ts similarity index 88% rename from npm/vue/webpack.config.js rename to npm/vue/webpack.config.ts index bd54d1a7e9bc..64e52772feaa 100644 --- a/npm/vue/webpack.config.js +++ b/npm/vue/webpack.config.ts @@ -1,11 +1,13 @@ // A basic webpack configuration // The default for running tests in this project // https://vue-loader.vuejs.org/guide/#manual-setup -const { VueLoaderPlugin } = require('vue-loader') -const path = require('path') +import { VueLoaderPlugin } from 'vue-loader' +import * as path from 'path' +import { Configuration } from 'webpack' + const pkg = require('package.json') -module.exports = { +export default { mode: 'development', output: { path: path.join(__dirname, 'dist'), @@ -49,4 +51,4 @@ module.exports = { // make sure to include the plugin for the magic new VueLoaderPlugin(), ], -} +} as Configuration From c10b56e3c9648bf001a198cd5bc1087b1857b3f6 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 29 Jun 2021 14:52:02 -0500 Subject: [PATCH 030/204] reeanble cleanup with remove fixtures --- packages/server/test/unit/project_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/test/unit/project_spec.js b/packages/server/test/unit/project_spec.js index a9e514201db7..00d81e394739 100644 --- a/packages/server/test/unit/project_spec.js +++ b/packages/server/test/unit/project_spec.js @@ -55,7 +55,7 @@ describe('lib/project-base', () => { }) afterEach(function () { - // Fixtures.remove() + Fixtures.remove() if (this.project) { this.project.close() From 97e313194fbbe924d791df6f81e31d6bbeccd4b2 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 29 Jun 2021 14:53:14 -0500 Subject: [PATCH 031/204] fix comment on cache helper --- packages/server/test/cache_helper.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/server/test/cache_helper.js b/packages/server/test/cache_helper.js index 0fdc81d17af6..1da4ed3e0d2b 100644 --- a/packages/server/test/cache_helper.js +++ b/packages/server/test/cache_helper.js @@ -4,10 +4,10 @@ const supportedConfigFiles = [ ] /** - * Since we load the cypress.json, cypress.e2e.config.js or cypress.component.config.js - * via `require`, we need to clear the `require.cache` before/after some tests - * to ensure we are not using a cached configuration file. - */ + * Since we load the cypress.json via `require`, + * we need to clear the `require.cache` before/after some tests + * to ensure we are not using a cached configuration file. + */ const clearCypressJsonCache = () => { Object.keys(require.cache).forEach((key) => { if (supportedConfigFiles.some((file) => key.includes(file))) { From 771ed0ec2d28a73e0d4199a835a55fb4893943af Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 6 Jul 2021 11:05:00 -0500 Subject: [PATCH 032/204] make ipc util typescript --- packages/server/lib/plugins/util.js | 88 ----------------------------- packages/server/lib/plugins/util.ts | 87 ++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 88 deletions(-) delete mode 100644 packages/server/lib/plugins/util.js create mode 100644 packages/server/lib/plugins/util.ts diff --git a/packages/server/lib/plugins/util.js b/packages/server/lib/plugins/util.js deleted file mode 100644 index eb78eaa57c7f..000000000000 --- a/packages/server/lib/plugins/util.js +++ /dev/null @@ -1,88 +0,0 @@ -const _ = require('lodash') -const EE = require('events') -const debug = require('debug')('cypress:server:plugins') -const Promise = require('bluebird') - -const UNDEFINED_SERIALIZED = '__cypress_undefined__' - -const serializeError = (err) => { - return _.pick(err, 'name', 'message', 'stack', 'code', 'annotated', 'type') -} - -module.exports = { - serializeError, - - wrapIpc (aProcess) { - const emitter = new EE() - - aProcess.on('message', (message) => { - 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) { - return - } - - return aProcess.send({ - event, - args, - }) - }, - - on: emitter.on.bind(emitter), - removeListener: emitter.removeListener.bind(emitter), - } - }, - - wrapChildPromise (ipc, invoke, ids, args = []) { - return Promise.try(() => { - return invoke(ids.eventId, args) - }) - .then((value) => { - // undefined is coerced into null when sent over ipc, but we need - // to differentiate between them for 'task' event - if (value === undefined) { - value = UNDEFINED_SERIALIZED - } - - return ipc.send(`promise:fulfilled:${ids.invocationId}`, null, value) - }).catch((err) => { - return ipc.send(`promise:fulfilled:${ids.invocationId}`, serializeError(err)) - }) - }, - - wrapParentPromise (ipc, eventId, callback) { - const invocationId = _.uniqueId('inv') - - return new Promise((resolve, reject) => { - const handler = function (err, value) { - ipc.removeListener(`promise:fulfilled:${invocationId}`, handler) - - if (err) { - debug('promise rejected for id %s %o', invocationId, ':', err.stack) - reject(_.extend(new Error(err.message), err)) - - return - } - - if (value === UNDEFINED_SERIALIZED) { - value = undefined - } - - debug(`promise resolved for id '${invocationId}' with value`, value) - - return resolve(value) - } - - ipc.on(`promise:fulfilled:${invocationId}`, handler) - - return callback(invocationId) - }) - }, -} diff --git a/packages/server/lib/plugins/util.ts b/packages/server/lib/plugins/util.ts new file mode 100644 index 000000000000..76a18e6ad690 --- /dev/null +++ b/packages/server/lib/plugins/util.ts @@ -0,0 +1,87 @@ +import _ from 'lodash' +import EE from 'events' +import Debug from 'debug' +import Promise from 'bluebird' + +const debug = Debug('cypress:server:plugins') +const UNDEFINED_SERIALIZED = '__cypress_undefined__' + +const serializeError = (err) => { + return _.pick(err, 'name', 'message', 'stack', 'code', 'annotated', 'type') +} + +export { serializeError } + +export function wrapIpc (aProcess) { + const emitter = new EE() + + aProcess.on('message', (message) => { + 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) { + return + } + + return aProcess.send({ + event, + args, + }) + }, + + on: emitter.on.bind(emitter), + removeListener: emitter.removeListener.bind(emitter), + } +} + +export function wrapChildPromise (ipc, invoke, ids, args = []) { + return Promise.try(() => { + return invoke(ids.eventId, args) + }) + .then((value) => { + // undefined is coerced into null when sent over ipc, but we need + // to differentiate between them for 'task' event + if (value === undefined) { + value = UNDEFINED_SERIALIZED + } + + return ipc.send(`promise:fulfilled:${ids.invocationId}`, null, value) + }).catch((err) => { + return ipc.send(`promise:fulfilled:${ids.invocationId}`, serializeError(err)) + }) +} + +export function wrapParentPromise (ipc, eventId, callback) { + const invocationId = _.uniqueId('inv') + + return new Promise((resolve, reject) => { + const handler = function (err, value) { + ipc.removeListener(`promise:fulfilled:${invocationId}`, handler) + + if (err) { + debug('promise rejected for id %s %o', invocationId, ':', err.stack) + reject(_.extend(new Error(err.message), err)) + + return + } + + if (value === UNDEFINED_SERIALIZED) { + value = undefined + } + + debug(`promise resolved for id '${invocationId}' with value`, value) + + return resolve(value) + } + + ipc.on(`promise:fulfilled:${invocationId}`, handler) + + return callback(invocationId) + }) +} From da5031d13d7d096e8749ec53c530b5459a3d1191 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 6 Jul 2021 14:46:11 -0500 Subject: [PATCH 033/204] fix run_plugins debug --- packages/server/lib/plugins/child/run_plugins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js index d14d39dd7cbb..f5742dd2d14a 100644 --- a/packages/server/lib/plugins/child/run_plugins.js +++ b/packages/server/lib/plugins/child/run_plugins.js @@ -149,7 +149,7 @@ function getPluginsFunction (pluginsFile, functionName) { const runPlugins = (ipc, pluginsFile, projectRoot, functionName) => { debug('pluginsFile:', pluginsFile) - debug('functionName:', pluginsFile) + debug('functionName:', functionName) debug('project root:', projectRoot) if (!projectRoot) { throw new Error('Unexpected: projectRoot should be a string') From e9296d77325acb458f64ad2e550c7393e0536909 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 6 Jul 2021 15:28:56 -0500 Subject: [PATCH 034/204] make cypress.config.ts run in a separate thread --- packages/server/lib/errors.js | 11 +++ packages/server/lib/plugins/index.js | 2 - packages/server/lib/plugins/util.js | 88 +++++++++++++++++++ packages/server/lib/plugins/util.ts | 87 ------------------ packages/server/lib/util/require_async.ts | 57 ++++++++++++ .../server/lib/util/require_async_child.js | 66 ++++++++++++++ packages/server/lib/util/settings.js | 24 ++--- 7 files changed, 227 insertions(+), 108 deletions(-) create mode 100644 packages/server/lib/plugins/util.js delete mode 100644 packages/server/lib/plugins/util.ts create mode 100644 packages/server/lib/util/require_async.ts create mode 100644 packages/server/lib/util/require_async_child.js diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 0d118418778e..06922531e7f8 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -559,6 +559,17 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { Or you might have renamed the extension of your \`supportFile\` to \`.ts\`. If that's the case, restart the test runner. Learn more at https://on.cypress.io/support-file-missing-or-invalid` + case 'CONFIG_FILE_ERROR': + msg = stripIndent`\ + The config file loading errored. + + You might have renamed the extension of your config file. If that's the case, restart the test runner.`.trim() + + if (arg2) { + return { msg, details: arg2 } + } + + return msg case 'PLUGINS_FILE_ERROR': msg = stripIndent`\ The plugins file is missing or invalid. diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index c229c1604850..9673ac981761 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -134,8 +134,6 @@ const init = (config, options) => { ipc.send('load', config) ipc.on('loaded', (newCfg, registrations) => { - _.omit(config, 'projectRoot', 'configFile') - _.each(registrations, (registration) => { debug('register plugins process event', registration.event, 'with id', registration.eventId) diff --git a/packages/server/lib/plugins/util.js b/packages/server/lib/plugins/util.js new file mode 100644 index 000000000000..eb78eaa57c7f --- /dev/null +++ b/packages/server/lib/plugins/util.js @@ -0,0 +1,88 @@ +const _ = require('lodash') +const EE = require('events') +const debug = require('debug')('cypress:server:plugins') +const Promise = require('bluebird') + +const UNDEFINED_SERIALIZED = '__cypress_undefined__' + +const serializeError = (err) => { + return _.pick(err, 'name', 'message', 'stack', 'code', 'annotated', 'type') +} + +module.exports = { + serializeError, + + wrapIpc (aProcess) { + const emitter = new EE() + + aProcess.on('message', (message) => { + 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) { + return + } + + return aProcess.send({ + event, + args, + }) + }, + + on: emitter.on.bind(emitter), + removeListener: emitter.removeListener.bind(emitter), + } + }, + + wrapChildPromise (ipc, invoke, ids, args = []) { + return Promise.try(() => { + return invoke(ids.eventId, args) + }) + .then((value) => { + // undefined is coerced into null when sent over ipc, but we need + // to differentiate between them for 'task' event + if (value === undefined) { + value = UNDEFINED_SERIALIZED + } + + return ipc.send(`promise:fulfilled:${ids.invocationId}`, null, value) + }).catch((err) => { + return ipc.send(`promise:fulfilled:${ids.invocationId}`, serializeError(err)) + }) + }, + + wrapParentPromise (ipc, eventId, callback) { + const invocationId = _.uniqueId('inv') + + return new Promise((resolve, reject) => { + const handler = function (err, value) { + ipc.removeListener(`promise:fulfilled:${invocationId}`, handler) + + if (err) { + debug('promise rejected for id %s %o', invocationId, ':', err.stack) + reject(_.extend(new Error(err.message), err)) + + return + } + + if (value === UNDEFINED_SERIALIZED) { + value = undefined + } + + debug(`promise resolved for id '${invocationId}' with value`, value) + + return resolve(value) + } + + ipc.on(`promise:fulfilled:${invocationId}`, handler) + + return callback(invocationId) + }) + }, +} diff --git a/packages/server/lib/plugins/util.ts b/packages/server/lib/plugins/util.ts deleted file mode 100644 index 76a18e6ad690..000000000000 --- a/packages/server/lib/plugins/util.ts +++ /dev/null @@ -1,87 +0,0 @@ -import _ from 'lodash' -import EE from 'events' -import Debug from 'debug' -import Promise from 'bluebird' - -const debug = Debug('cypress:server:plugins') -const UNDEFINED_SERIALIZED = '__cypress_undefined__' - -const serializeError = (err) => { - return _.pick(err, 'name', 'message', 'stack', 'code', 'annotated', 'type') -} - -export { serializeError } - -export function wrapIpc (aProcess) { - const emitter = new EE() - - aProcess.on('message', (message) => { - 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) { - return - } - - return aProcess.send({ - event, - args, - }) - }, - - on: emitter.on.bind(emitter), - removeListener: emitter.removeListener.bind(emitter), - } -} - -export function wrapChildPromise (ipc, invoke, ids, args = []) { - return Promise.try(() => { - return invoke(ids.eventId, args) - }) - .then((value) => { - // undefined is coerced into null when sent over ipc, but we need - // to differentiate between them for 'task' event - if (value === undefined) { - value = UNDEFINED_SERIALIZED - } - - return ipc.send(`promise:fulfilled:${ids.invocationId}`, null, value) - }).catch((err) => { - return ipc.send(`promise:fulfilled:${ids.invocationId}`, serializeError(err)) - }) -} - -export function wrapParentPromise (ipc, eventId, callback) { - const invocationId = _.uniqueId('inv') - - return new Promise((resolve, reject) => { - const handler = function (err, value) { - ipc.removeListener(`promise:fulfilled:${invocationId}`, handler) - - if (err) { - debug('promise rejected for id %s %o', invocationId, ':', err.stack) - reject(_.extend(new Error(err.message), err)) - - return - } - - if (value === UNDEFINED_SERIALIZED) { - value = undefined - } - - debug(`promise resolved for id '${invocationId}' with value`, value) - - return resolve(value) - } - - ipc.on(`promise:fulfilled:${invocationId}`, handler) - - return callback(invocationId) - }) -} diff --git a/packages/server/lib/util/require_async.ts b/packages/server/lib/util/require_async.ts new file mode 100644 index 000000000000..86a84a2d84bf --- /dev/null +++ b/packages/server/lib/util/require_async.ts @@ -0,0 +1,57 @@ +import _ from 'lodash' +import * as path from 'path' +import * as cp from 'child_process' +import * as inspector from 'inspector' +import * as util from '../plugins/util' +import * as errors from '../errors' +import Debug from 'debug' + +const debug = Debug('cypress:server:require_async') + +let configProcess: cp.ChildProcess + +interface RequireAsyncOptions{ + projectRoot: string + loadErrorCode: string +} + +interface ChildOptions{ + stdio: 'inherit' + execArgv?: string[] +} + +export default async function requireAsync (filePath: string, options: RequireAsyncOptions): Promise { + if (configProcess) { + debug('kill existing config process') + configProcess.kill() + } + + const childOptions: ChildOptions = { + stdio: 'inherit', + } + + if (inspector.url()) { + childOptions.execArgv = _.chain(process.execArgv.slice(0)) + .remove('--inspect-brk') + .push(`--inspect=${process.debugPort + 1}`) + .value() + } + + const childArguments = ['--projectRoot', options.projectRoot, '--file', filePath, '--loadErrorCode', options.loadErrorCode] + + configProcess = cp.fork(path.join(__dirname, 'require_async_child.js'), childArguments, childOptions) + const ipc = util.wrapIpc(configProcess) + + return new Promise((resolve, reject) => { + ipc.on('loaded', (newCfg) => { + debug('resolving with config %o', newCfg) + resolve(newCfg) + }) + + ipc.on('load:error', (type, ...args) => { + debug('load:error %s, rejecting', type) + + reject(errors.get(type, ...args)) + }) + }) +} diff --git a/packages/server/lib/util/require_async_child.js b/packages/server/lib/util/require_async_child.js new file mode 100644 index 000000000000..7923f3c65a69 --- /dev/null +++ b/packages/server/lib/util/require_async_child.js @@ -0,0 +1,66 @@ +require('graceful-fs').gracefulify(require('fs')) +const debug = require('debug')('cypress:server:require_async:child') +const tsNodeUtil = require('./ts_node') +const util = require('../plugins/util') +const ipc = util.wrapIpc(process) + +require('./suppress_warnings').suppress() + +const { file, projectRoot, loadErrorCode } = require('minimist')(process.argv.slice(2)) + +let tsRegistered = false + +run(ipc, file, projectRoot) + +/** + * runs and returns the passed `requiredFile` file in the ipc `load` event + * @param {*} ipc Inter Process Comunication protocol + * @param {*} requiredFile the file we are trying to load + * @param {*} projectRoot the root of the typescript project (useful mainly for tsnode) + * @returns + */ +function run (ipc, requiredFile, projectRoot) { + debug('requiredFile:', requiredFile) + debug('projectRoot:', projectRoot) + if (!projectRoot) { + throw new Error('Unexpected: projectRoot should be a string') + } + + if (!tsRegistered) { + tsNodeUtil.register(projectRoot, requiredFile) + + // ensure typescript is only registered once + tsRegistered = true + } + + process.on('uncaughtException', (err) => { + debug('uncaught exception:', util.serializeError(err)) + ipc.send('error', util.serializeError(err)) + + return false + }) + + process.on('unhandledRejection', (event) => { + const err = (event && event.reason) || event + + debug('unhandled rejection:', util.serializeError(err)) + ipc.send('error', util.serializeError(err)) + + return false + }) + + try { + const exp = require(requiredFile) + + const result = exp.default || exp + + ipc.send('loaded', result) + + debug('config %o', result) + } catch (err) { + debug('failed to load requiredFile:\n%s', err.stack) + ipc.send('load:error', loadErrorCode, requiredFile, err.stack) + + return + } +} diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 8f04a17eafa9..13e7fe32f55d 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -4,9 +4,7 @@ const path = require('path') const errors = require('../errors') const log = require('../log') const { fs } = require('./fs') -const tsNodeUtil = require('./ts_node') - -let tsRegistered = false +const requireAsync = require('./require_async').default // TODO: // think about adding another PSemaphore @@ -171,24 +169,12 @@ module.exports = { return Promise.resolve({}) } - const requireAsync = (fileRequired) => { - return Promise.try(() => { - const exp = require(fileRequired) - - return exp.default || exp - }) - } - const file = this.pathToConfigFile(projectRoot, options) - if (!tsRegistered) { - tsNodeUtil.register(projectRoot, file) - - // ensure typescript is only registered once - tsRegistered = true - } - - return requireAsync(file) + return requireAsync(file, + { projectRoot: options.projectRoot, + loadErrorCode: 'CONFIG_FILE_ERROR', + }) .catch({ code: 'ENOENT' }, (e) => { if (/\.json$/.test(file)) { return this._write(file, {}) From 57cdc4d31335f8c9c93508a5ccad2f8c465cd993 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 6 Jul 2021 17:12:51 -0500 Subject: [PATCH 035/204] add debugs and fix error messages --- packages/server/lib/errors.js | 2 +- packages/server/lib/util/settings.js | 33 ++++++++++++++++++++-------- packages/server/lib/util/ts_node.js | 1 + 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 06922531e7f8..8e56bd74fa54 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -561,7 +561,7 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { Learn more at https://on.cypress.io/support-file-missing-or-invalid` case 'CONFIG_FILE_ERROR': msg = stripIndent`\ - The config file loading errored. + Error when loading the config file. You might have renamed the extension of your config file. If that's the case, restart the test runner.`.trim() diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 13e7fe32f55d..5bbc9000fdab 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -5,6 +5,7 @@ const errors = require('../errors') const log = require('../log') const { fs } = require('./fs') const requireAsync = require('./require_async').default +const debug = require('debug')('cypress:server:settings') // TODO: // think about adding another PSemaphore @@ -172,8 +173,10 @@ module.exports = { const file = this.pathToConfigFile(projectRoot, options) return requireAsync(file, - { projectRoot: options.projectRoot, + { + projectRoot, loadErrorCode: 'CONFIG_FILE_ERROR', + functionNames: ['e2e', 'component'], }) .catch({ code: 'ENOENT' }, (e) => { if (/\.json$/.test(file)) { @@ -182,25 +185,37 @@ module.exports = { return fs.writeFile(file, 'module.exports = {}').then(() => ({})) }) - .then((json = {}) => { + .then(({ result: configObject, functionNames }) => { const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' - if ((testingType in json)) { - if (typeof json[testingType] === 'object') { - json = { ...json, ...json[testingType] } + debug('resolved configObject', configObject) + + if ((testingType in configObject)) { + if (typeof configObject[testingType] === 'object') { + configObject = { ...configObject, ...configObject[testingType] } } } if (!/\.json$/.test(file)) { - return json + // Tell the system we detected plugin functions + // for e2e or component that we cannot get this way. + // They will never be executed but will match the + // expected type instead of being not there. + functionNames.forEach((name) => { + configObject[name] = function () { + throw Error('This function whould always be executed within the plugins context') + } + }) + + return configObject } - const changed = this._applyRewriteRules(json) + const changed = this._applyRewriteRules(configObject) // if our object is unchanged // then just return it - if (_.isEqual(json, changed)) { - return json + if (_.isEqual(configObject, changed)) { + return configObject } // else write the new reduced obj diff --git a/packages/server/lib/util/ts_node.js b/packages/server/lib/util/ts_node.js index ced991739751..ca673032458b 100644 --- a/packages/server/lib/util/ts_node.js +++ b/packages/server/lib/util/ts_node.js @@ -19,6 +19,7 @@ const getTsNodeOptions = (tsPath, pluginsFile) => { const register = (projectRoot, pluginsFile) => { try { debug('projectRoot path: %s', projectRoot) + debug('pluginsFile: %s', pluginsFile) const tsPath = resolve.typescript(projectRoot) if (!tsPath) return From 2cc98677fbe119a7ac63e4ab6f0198b23825eb69 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 6 Jul 2021 17:13:17 -0500 Subject: [PATCH 036/204] transmit references to functions --- packages/server/lib/util/require_async.ts | 12 ++++-- .../server/lib/util/require_async_child.js | 43 +++++++++++++------ 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/packages/server/lib/util/require_async.ts b/packages/server/lib/util/require_async.ts index 86a84a2d84bf..0bd576588247 100644 --- a/packages/server/lib/util/require_async.ts +++ b/packages/server/lib/util/require_async.ts @@ -13,6 +13,10 @@ let configProcess: cp.ChildProcess interface RequireAsyncOptions{ projectRoot: string loadErrorCode: string + /** + * members of the object returned that are functions and will need to be wrapped + */ + functionNames: string[] } interface ChildOptions{ @@ -43,9 +47,11 @@ export default async function requireAsync (filePath: string, options: RequireAs const ipc = util.wrapIpc(configProcess) return new Promise((resolve, reject) => { - ipc.on('loaded', (newCfg) => { - debug('resolving with config %o', newCfg) - resolve(newCfg) + ipc.send('load', options.functionNames) + ipc.on('loaded', ({ result, functionNames }) => { + debug('resolving with result %o', result) + debug('resolving with functions %o', functionNames) + resolve({ result, functionNames }) }) ipc.on('load:error', (type, ...args) => { diff --git a/packages/server/lib/util/require_async_child.js b/packages/server/lib/util/require_async_child.js index 7923f3c65a69..b1e9c9310c51 100644 --- a/packages/server/lib/util/require_async_child.js +++ b/packages/server/lib/util/require_async_child.js @@ -49,18 +49,33 @@ function run (ipc, requiredFile, projectRoot) { return false }) - try { - const exp = require(requiredFile) - - const result = exp.default || exp - - ipc.send('loaded', result) - - debug('config %o', result) - } catch (err) { - debug('failed to load requiredFile:\n%s', err.stack) - ipc.send('load:error', loadErrorCode, requiredFile, err.stack) - - return - } + ipc.on('load', (functionNames) => { + try { + const exp = require(requiredFile) + + const result = exp.default || exp + + const functionsNamesOut = [] + + // Since functions cannot be transmitted through ipc + // register if they are indeed function to inform parent process + // that something is missing from the transmitted object + functionNames.forEach((name) => { + debug('check if %s is a function (%s)', name, typeof result[name]) + if (typeof result[name] === 'function') { + debug('%s is a function', name) + functionsNamesOut.push(name) + } + }) + + ipc.send('loaded', { result, functionNames: functionsNamesOut }) + + debug('config %o', result) + } catch (err) { + debug('failed to load requiredFile:\n%s', err.stack) + ipc.send('load:error', loadErrorCode, requiredFile, err.stack) + + return + } + }) } From e3526fa2a59d3092b652333874a6ba85c165ba9c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 6 Jul 2021 17:16:08 -0500 Subject: [PATCH 037/204] fix webpack config --- npm/vue/webpack.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/vue/webpack.config.ts b/npm/vue/webpack.config.ts index 64e52772feaa..d3dd724b2e6b 100644 --- a/npm/vue/webpack.config.ts +++ b/npm/vue/webpack.config.ts @@ -5,7 +5,7 @@ import { VueLoaderPlugin } from 'vue-loader' import * as path from 'path' import { Configuration } from 'webpack' -const pkg = require('package.json') +const pkg = require('./package.json') export default { mode: 'development', From 72c3592819bb9c11b62b8fe6e917701aaa7de414 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 6 Jul 2021 18:10:20 -0500 Subject: [PATCH 038/204] update the cypress API --- cli/types/cypress-npm-api.d.ts | 3 +++ npm/vue/cypress.config.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cli/types/cypress-npm-api.d.ts b/cli/types/cypress-npm-api.d.ts index 3287635c8547..8a3c6b4f2628 100644 --- a/cli/types/cypress-npm-api.d.ts +++ b/cli/types/cypress-npm-api.d.ts @@ -378,6 +378,9 @@ declare module 'cypress' { */ cli: CypressCommandLine.CypressCliParser + /** + * Type helper to make writing `cypress.config.ts` easier + */ defineConfig(config: Cypress.ConfigOptions): Cypress.ConfigOptions } diff --git a/npm/vue/cypress.config.ts b/npm/vue/cypress.config.ts index fea836238899..9aca3d12268e 100644 --- a/npm/vue/cypress.config.ts +++ b/npm/vue/cypress.config.ts @@ -29,4 +29,4 @@ export default defineConfig({ return config }, -} as Cypress.ConfigOptions) +}) From bc5f96300e46b319ca7a2d9f59861f1301e8b000 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 6 Jul 2021 18:10:33 -0500 Subject: [PATCH 039/204] Update cypress types --- cli/types/cypress.d.ts | 57 +++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 88d2570ebd2e..dbb41faff6c8 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2678,17 +2678,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 | TestingTypeFunctions + component: ConfigOptions | TestingTypeFunctions /** * Override default config options for E2E Testing runner. * @default {} */ - e2e: ResolvedConfigOptions | TestingTypeFunctions + e2e: ConfigOptions | TestingTypeFunctions } type TestingTypeFunctions = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptions | undefined) @@ -2703,10 +2732,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. */ @@ -2715,10 +2740,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. */ @@ -2738,22 +2759,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. */ From 43edb2b7957bdd4394fc1552b8536837a81e0c08 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 7 Jul 2021 13:37:05 -0500 Subject: [PATCH 040/204] avoid failling to close a project when it was never open --- packages/server/lib/project-base.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index aac2e51dcf69..08cf3d34755d 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -62,6 +62,7 @@ export class ProjectBase extends EE { protected _server?: TServer protected _automation?: Automation private _recordTests = null + private isOpen: boolean = false public browser: any public projectType?: 'e2e' | 'ct' @@ -267,6 +268,8 @@ export class ProjectBase extends EE { system: _.pick(sys, 'osName', 'osVersion'), } + this.isOpen = true + return runEvents.execute('before:run', cfg, beforeRunDetails) }) }) @@ -310,7 +313,7 @@ export class ProjectBase extends EE { const closePreprocessor = this.projectType === 'e2e' && preprocessor.close ?? undefined return Bluebird.join( - this.server?.close(), + this.isOpen ? this.server?.close() : Promise.resolve(), this.watchers?.close(), closePreprocessor?.(), ) From ca364155cc60a44ab26acb8e67665105896550fe Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 7 Jul 2021 13:37:58 -0500 Subject: [PATCH 041/204] kill configFile process once done or errored --- packages/server/lib/util/require_async.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/server/lib/util/require_async.ts b/packages/server/lib/util/require_async.ts index 0bd576588247..cb58b2545e28 100644 --- a/packages/server/lib/util/require_async.ts +++ b/packages/server/lib/util/require_async.ts @@ -51,11 +51,13 @@ export default async function requireAsync (filePath: string, options: RequireAs ipc.on('loaded', ({ result, functionNames }) => { debug('resolving with result %o', result) debug('resolving with functions %o', functionNames) + configProcess.kill() resolve({ result, functionNames }) }) ipc.on('load:error', (type, ...args) => { debug('load:error %s, rejecting', type) + configProcess.kill() reject(errors.get(type, ...args)) }) From 3c07ce996da1bc68d421e521406186ab16549191 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 7 Jul 2021 14:37:34 -0500 Subject: [PATCH 042/204] fix a few issues --- packages/server/test/e2e/3_config_spec.js | 20 +++++++++++++++++++ .../cypress.config.js | 1 + .../cypress/integration/test.js | 3 +++ .../cypress.config.js | 1 + .../cypress.json | 5 +++++ .../cypress/integration/test.js | 3 +++ 6 files changed, 33 insertions(+) create mode 100644 packages/server/test/support/fixtures/projects/config-with-empty-cypress-config/cypress.config.js create mode 100644 packages/server/test/support/fixtures/projects/config-with-empty-cypress-config/cypress/integration/test.js create mode 100644 packages/server/test/support/fixtures/projects/config-with-json-and-empty-cypress-config/cypress.config.js create mode 100644 packages/server/test/support/fixtures/projects/config-with-json-and-empty-cypress-config/cypress.json create mode 100644 packages/server/test/support/fixtures/projects/config-with-json-and-empty-cypress-config/cypress/integration/test.js diff --git a/packages/server/test/e2e/3_config_spec.js b/packages/server/test/e2e/3_config_spec.js index efc901ce4690..a3ac3565acca 100644 --- a/packages/server/test/e2e/3_config_spec.js +++ b/packages/server/test/e2e/3_config_spec.js @@ -1,3 +1,5 @@ +const fs = require('fs') +const path = require('path') const e2e = require('../support/helpers/e2e').default const Fixtures = require('../support/helpers/fixtures') @@ -47,4 +49,22 @@ describe('e2e config', () => { project: Fixtures.projectPath('shadow-dom-global-inclusion'), }) }) + + it('does not create a cypress.json when `module.exports = {}` is the only thing in cypress.config.js', function () { + const project = Fixtures.projectPath('config-with-empty-cypress-config') + + return e2e.exec(this, { + project, + }).then(() => { + expect(fs.existsSync(path.resolve(project, 'cypress.json'))).to.be.false + }) + }) + + it('does not crash when `module.exports = {}` is the only thing in cypress.config.js and there is a cypress.json', function () { + const project = Fixtures.projectPath('config-with-json-and-empty-cypress-config') + + return e2e.exec(this, { + project, + }) + }) }) diff --git a/packages/server/test/support/fixtures/projects/config-with-empty-cypress-config/cypress.config.js b/packages/server/test/support/fixtures/projects/config-with-empty-cypress-config/cypress.config.js new file mode 100644 index 000000000000..4ba52ba2c8df --- /dev/null +++ b/packages/server/test/support/fixtures/projects/config-with-empty-cypress-config/cypress.config.js @@ -0,0 +1 @@ +module.exports = {} diff --git a/packages/server/test/support/fixtures/projects/config-with-empty-cypress-config/cypress/integration/test.js b/packages/server/test/support/fixtures/projects/config-with-empty-cypress-config/cypress/integration/test.js new file mode 100644 index 000000000000..92e444116c98 --- /dev/null +++ b/packages/server/test/support/fixtures/projects/config-with-empty-cypress-config/cypress/integration/test.js @@ -0,0 +1,3 @@ +it('works', () => { + expect(true).to.be.true +}) diff --git a/packages/server/test/support/fixtures/projects/config-with-json-and-empty-cypress-config/cypress.config.js b/packages/server/test/support/fixtures/projects/config-with-json-and-empty-cypress-config/cypress.config.js new file mode 100644 index 000000000000..4ba52ba2c8df --- /dev/null +++ b/packages/server/test/support/fixtures/projects/config-with-json-and-empty-cypress-config/cypress.config.js @@ -0,0 +1 @@ +module.exports = {} diff --git a/packages/server/test/support/fixtures/projects/config-with-json-and-empty-cypress-config/cypress.json b/packages/server/test/support/fixtures/projects/config-with-json-and-empty-cypress-config/cypress.json new file mode 100644 index 000000000000..9dd38efd5217 --- /dev/null +++ b/packages/server/test/support/fixtures/projects/config-with-json-and-empty-cypress-config/cypress.json @@ -0,0 +1,5 @@ +{ + "defaultCommandTimeout": 1000, + "pluginsFile": false, + "supportFile": false +} diff --git a/packages/server/test/support/fixtures/projects/config-with-json-and-empty-cypress-config/cypress/integration/test.js b/packages/server/test/support/fixtures/projects/config-with-json-and-empty-cypress-config/cypress/integration/test.js new file mode 100644 index 000000000000..92e444116c98 --- /dev/null +++ b/packages/server/test/support/fixtures/projects/config-with-json-and-empty-cypress-config/cypress/integration/test.js @@ -0,0 +1,3 @@ +it('works', () => { + expect(true).to.be.true +}) From 5bcfd620d9d31b0034ff4350999659d59f41d260 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 7 Jul 2021 15:53:15 -0500 Subject: [PATCH 043/204] add typescript to saved state --- packages/server/lib/saved_state.js | 33 +++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/server/lib/saved_state.js b/packages/server/lib/saved_state.js index f0773530705e..cce88e639045 100644 --- a/packages/server/lib/saved_state.js +++ b/packages/server/lib/saved_state.js @@ -49,22 +49,35 @@ const formStatePath = (projectRoot) => { debug('missing project path, looking for project here') - const cypressConfigJsPath = cwd('cypress.config.js') + const cypressConfigTsPath = cwd('cypress.config.ts') - return fs.pathExistsAsync(cypressConfigJsPath) - .then((found) => { - if (found) { - debug('found cypress file %s', cypressConfigJsPath) + return fs.pathExistsAsync(cypressConfigTsPath) + .then((foundTs) => { + if (foundTs) { + debug('found cypress file %s', cypressConfigTsPath) projectRoot = cwd() } else { - const cypressJsonPath = cwd('cypress.json') + const cypressConfigJsPath = cwd('cypress.config.js') // if not found with js, try with json - return fs.pathExistsAsync(cypressJsonPath) - .then((foundJson) => { - if (foundJson) { - debug('found cypress file %s', cypressJsonPath) + return fs.pathExistsAsync(cypressConfigJsPath) + .then((foundJs) => { + if (foundJs) { + debug('found cypress file %s', cypressConfigJsPath) projectRoot = cwd() + } else { + const cypressJsonPath = cwd('cypress.json') + + // if not found with js, try with json + return fs.pathExistsAsync(cypressJsonPath) + .then((foundJson) => { + if (foundJson) { + debug('found cypress file %s', cypressJsonPath) + projectRoot = cwd() + } + + return projectRoot + }) } return projectRoot From dbaab7fda432218b52a1d528b1324e0f9bbe305c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 7 Jul 2021 21:28:50 -0500 Subject: [PATCH 044/204] show the right file in desktop-gui --- packages/desktop-gui/src/lib/config-file-formatted.jsx | 4 ++-- packages/desktop-gui/src/settings/node-version.jsx | 2 ++ packages/server/lib/util/settings.js | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/desktop-gui/src/lib/config-file-formatted.jsx b/packages/desktop-gui/src/lib/config-file-formatted.jsx index 4f6bccb6d98f..43d893dc1e0a 100644 --- a/packages/desktop-gui/src/lib/config-file-formatted.jsx +++ b/packages/desktop-gui/src/lib/config-file-formatted.jsx @@ -6,8 +6,8 @@ const configFileFormatted = (configFile) => { return <>cypress.json file (currently disabled by --config-file false) } - if (isUndefined(configFile) || configFile === 'cypress.json') { - return <>cypress.json file + if (isUndefined(configFile) || ['cypress.json', 'cypress.config.ts', 'cypress.config.js'].indexOf(configFile) !== -1) { + return <>{configFile} file } return <>custom config file {configFile} diff --git a/packages/desktop-gui/src/settings/node-version.jsx b/packages/desktop-gui/src/settings/node-version.jsx index 4c41cf3177e0..9be29d04c5ae 100644 --- a/packages/desktop-gui/src/settings/node-version.jsx +++ b/packages/desktop-gui/src/settings/node-version.jsx @@ -1,6 +1,7 @@ import _ from 'lodash' import { observer } from 'mobx-react' import React from 'react' +import configFileFormatted from '../lib/config-file-formatted' import ipc from '../lib/ipc' @@ -89,6 +90,7 @@ const NodeVersion = observer(({ project }) => {
This Node.js version is used to:
    +
  • Execute code in the {configFileFormatted(project.configFile)}.
  • Build files in the {formatIntegrationFolder()} folder.
  • Build files in the cypress/support folder.
  • Execute code in the {formatPluginsFile() ? formatPluginsFile() : 'plugins'} file.
  • diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 5bbc9000fdab..ae2f57763672 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -207,6 +207,8 @@ module.exports = { } }) + configObject.configFile = path.relative(projectRoot, file) + return configObject } From 0c083cfbb8b7e92918a0b88cb183605fef46f10a Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 8 Jul 2021 14:47:23 -0500 Subject: [PATCH 045/204] don't show e2e & component in the e2e desktop-gui --- packages/desktop-gui/src/projects/projects-api.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/desktop-gui/src/projects/projects-api.js b/packages/desktop-gui/src/projects/projects-api.js index 2c7b87831a7c..97d6a25513b7 100644 --- a/packages/desktop-gui/src/projects/projects-api.js +++ b/packages/desktop-gui/src/projects/projects-api.js @@ -192,6 +192,15 @@ const openProject = (project) => { return ipc.openProject(project.path) .then((config = {}) => { + // In this context we know we are in e2e. + // The configuration in e2e has already been merged with the main. + // It is not useful to display those 2 fields explicitely. + // + // NOTE: Even if we wanted to, we cannot display them. + // These two parameter could be functions and we + // cannot send/receive functions using ipc + delete config.e2e + delete config.component updateConfig(config) const projectIdAndPath = { id: config.projectId, path: project.path } From abfc9bb0909831a26933d97a12f6f3706db667de Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 8 Jul 2021 16:07:04 -0500 Subject: [PATCH 046/204] display e2e func instead of plugin when no plugin --- packages/desktop-gui/src/project/project-model.js | 6 ++++++ packages/desktop-gui/src/projects/projects-api.js | 10 +++++++--- packages/desktop-gui/src/settings/configuration.jsx | 4 ++-- packages/desktop-gui/src/settings/node-version.jsx | 4 ++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/desktop-gui/src/project/project-model.js b/packages/desktop-gui/src/project/project-model.js index c7732780bec2..9373171bafd9 100644 --- a/packages/desktop-gui/src/project/project-model.js +++ b/packages/desktop-gui/src/project/project-model.js @@ -42,6 +42,7 @@ export default class Project { // persisted with api @observable id @observable name + @observable configFile @observable public @observable lastBuildStatus @observable lastBuildCreatedAt @@ -59,6 +60,7 @@ export default class Project { @observable newUserBannerOpen = false @observable browserState = 'closed' @observable resolvedConfig + @observable hasE2EFunction @observable error /** @type {{[key: string] : {warning:Error & {dismissed: boolean}}}} */ @observable _warnings = {} @@ -214,6 +216,10 @@ export default class Project { this.resolvedConfig = resolved } + @action setE2EFunction (hasE2EFunction) { + this.hasE2EFunction = hasE2EFunction + } + @action setError (err = {}) { // for some reason, the original `stack` is unavailable on `err` once it is set on the model // `stack2` remains usable though, for some reason diff --git a/packages/desktop-gui/src/projects/projects-api.js b/packages/desktop-gui/src/projects/projects-api.js index 97d6a25513b7..6bed7ebab72c 100644 --- a/packages/desktop-gui/src/projects/projects-api.js +++ b/packages/desktop-gui/src/projects/projects-api.js @@ -156,7 +156,7 @@ const openProject = (project) => { }) } - const updateConfig = (config) => { + const updateConfig = (config, functions) => { project.update({ id: config.projectId, name: config.projectName, @@ -167,6 +167,7 @@ const openProject = (project) => { project.setOnBoardingConfig(config) project.setBrowsers(config.browsers) project.setResolvedConfig(config.resolved) + project.setE2EFunction(functions.indexOf('e2e') !== -1) project.prompts.setPromptStates(config) } @@ -191,7 +192,7 @@ const openProject = (project) => { }) return ipc.openProject(project.path) - .then((config = {}) => { + .then(({ config = {}, functions }) => { // In this context we know we are in e2e. // The configuration in e2e has already been merged with the main. // It is not useful to display those 2 fields explicitely. @@ -200,8 +201,11 @@ const openProject = (project) => { // These two parameter could be functions and we // cannot send/receive functions using ipc delete config.e2e + delete config.resolved.e2e delete config.component - updateConfig(config) + delete config.resolved.component + + updateConfig(config, functions) const projectIdAndPath = { id: config.projectId, path: project.path } specsStore.setFilter(projectIdAndPath, localData.get(specsStore.getSpecsFilterId(projectIdAndPath))) diff --git a/packages/desktop-gui/src/settings/configuration.jsx b/packages/desktop-gui/src/settings/configuration.jsx index 76453cbf87f4..629fd517b0bb 100644 --- a/packages/desktop-gui/src/settings/configuration.jsx +++ b/packages/desktop-gui/src/settings/configuration.jsx @@ -175,8 +175,8 @@ const Configuration = observer(({ project }) => ( set from CLI arguments - plugin - set from plugin file + {project.hasE2EFunction ? 'e2e function' : 'plugin'} + set from {project.hasE2EFunction ? 'e2e function' : 'plugin file'} diff --git a/packages/desktop-gui/src/settings/node-version.jsx b/packages/desktop-gui/src/settings/node-version.jsx index 9be29d04c5ae..09e1b5d03a4f 100644 --- a/packages/desktop-gui/src/settings/node-version.jsx +++ b/packages/desktop-gui/src/settings/node-version.jsx @@ -1,7 +1,7 @@ import _ from 'lodash' import { observer } from 'mobx-react' import React from 'react' -import configFileFormatted from '../lib/config-file-formatted' +import { configFileFormatted } from '../lib/config-file-formatted' import ipc from '../lib/ipc' @@ -90,7 +90,7 @@ const NodeVersion = observer(({ project }) => {
    This Node.js version is used to:
      -
    • Execute code in the {configFileFormatted(project.configFile)}.
    • +
    • Execute code in {configFileFormatted(project.configFile)}.
    • Build files in the {formatIntegrationFolder()} folder.
    • Build files in the cypress/support folder.
    • Execute code in the {formatPluginsFile() ? formatPluginsFile() : 'plugins'} file.
    • From cfbd2ea199fbca9b6371e8014f81c245f0ecca01 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 8 Jul 2021 18:28:59 -0500 Subject: [PATCH 047/204] make the settings menu display the proper name --- npm/vue/cypress.config.ts | 5 ++++ .../src/settings/configuration.jsx | 27 ++++++++++++++----- .../desktop-gui/src/settings/settings.scss | 4 +-- packages/server/lib/gui/events.js | 11 +++++++- packages/server/lib/project-base.ts | 9 +++++++ 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/npm/vue/cypress.config.ts b/npm/vue/cypress.config.ts index 9aca3d12268e..3588788d2e8e 100644 --- a/npm/vue/cypress.config.ts +++ b/npm/vue/cypress.config.ts @@ -27,6 +27,11 @@ export default defineConfig({ require('@cypress/code-coverage/task')(on, config) on('dev-server:start', (options) => startDevServer({ options, webpackConfig })) + return config + }, + e2e (_, config) { + config.includeShadowDom = true + return config }, }) diff --git a/packages/desktop-gui/src/settings/configuration.jsx b/packages/desktop-gui/src/settings/configuration.jsx index 629fd517b0bb..9011221d09d2 100644 --- a/packages/desktop-gui/src/settings/configuration.jsx +++ b/packages/desktop-gui/src/settings/configuration.jsx @@ -85,7 +85,7 @@ ObjectLabel.defaultProps = { data: 'undefined', } -const computeFromValue = (obj, name, path) => { +const computeFromValue = (obj, hasE2EFunction, name, path) => { const normalizedPath = path.replace('$.', '').replace(name, `['${name}']`) let value = _.get(obj, normalizedPath) @@ -99,11 +99,15 @@ const computeFromValue = (obj, name, path) => { return undefined } + if (value.from === 'plugin' && hasE2EFunction) { + return 'function' + } + return value.from ? value.from : undefined } -const ConfigDisplay = ({ data: obj }) => { - const getFromValue = _.partial(computeFromValue, obj) +const ConfigDisplay = ({ data: obj, hasE2EFunction }) => { + const getFromValue = _.partial(computeFromValue, obj, hasE2EFunction) const renderNode = ({ depth, name, data, isNonenumerable, expanded, path }) => { if (depth === 0) { return null @@ -124,6 +128,8 @@ const ConfigDisplay = ({ data: obj }) => { const data = normalizeWithoutMeta(obj) + if (!data) return
      + data.env = normalizeWithoutMeta(obj.env) return ( @@ -175,12 +181,21 @@ const Configuration = observer(({ project }) => ( set from CLI arguments - {project.hasE2EFunction ? 'e2e function' : 'plugin'} - set from {project.hasE2EFunction ? 'e2e function' : 'plugin file'} + {project.hasE2EFunction ? + <> + function + set in the e2e function if the {configFileFormatted(project.configFile)} file + + : + <> + plugin + set from plugin file + + } - +
      )) diff --git a/packages/desktop-gui/src/settings/settings.scss b/packages/desktop-gui/src/settings/settings.scss index 428789e2d035..ee73ccedef45 100644 --- a/packages/desktop-gui/src/settings/settings.scss +++ b/packages/desktop-gui/src/settings/settings.scss @@ -100,7 +100,7 @@ } } - .envFile, .env, .config, .cli, .plugin, .default { + .envFile, .env, .config, .cli, .plugin, .function, .default { font-family: $font-mono; padding: 2px; } @@ -125,7 +125,7 @@ color: #A21313; } - .plugin { + .plugin, .function { background-color: #f0e7fc; color: #134aa2; } diff --git a/packages/server/lib/gui/events.js b/packages/server/lib/gui/events.js index b4ed086abb06..289776248e51 100644 --- a/packages/server/lib/gui/events.js +++ b/packages/server/lib/gui/events.js @@ -321,7 +321,16 @@ const handleEvent = function (options, bus, event, id, type, arg) { onError, onWarning, }) - }).call('getConfig') + }).then((project) => { + return project.getConfig() + }) + .then((cfg) => { + const functions = ['e2e', 'component'].filter((func) => { + return typeof cfg[func] === 'function' + }) + + return { config: cfg, functions } + }) .then(send) .catch(sendErr) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 08cf3d34755d..3051486053eb 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -268,6 +268,11 @@ export class ProjectBase extends EE { system: _.pick(sys, 'osName', 'osVersion'), } + // flag the project as open + // this is useful if we want to close project + // that was never open in the first place + // since it allows us to skip a few lines + // @see close() function this.isOpen = true return runEvents.execute('before:run', cfg, beforeRunDetails) @@ -367,6 +372,10 @@ export class ProjectBase extends EE { .then((modifiedCfg) => { debug('plugin config yielded: %o', modifiedCfg) + if (modifiedCfg) { + modifiedCfg.configFile = path.relative(this.projectRoot, modifiedCfg.configFile) + } + const updatedConfig = config.updateWithPluginValues(cfg, modifiedCfg) if (this.projectType === 'ct') { From 88ec179f134501a8d643084741b1b2376e8f98c5 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 9 Jul 2021 10:01:27 -0500 Subject: [PATCH 048/204] make sure file only appears once --- packages/desktop-gui/src/settings/configuration.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop-gui/src/settings/configuration.jsx b/packages/desktop-gui/src/settings/configuration.jsx index 9011221d09d2..158fe4ce2b0a 100644 --- a/packages/desktop-gui/src/settings/configuration.jsx +++ b/packages/desktop-gui/src/settings/configuration.jsx @@ -184,7 +184,7 @@ const Configuration = observer(({ project }) => ( {project.hasE2EFunction ? <> function - set in the e2e function if the {configFileFormatted(project.configFile)} file + set in the e2e function if the {configFileFormatted(project.configFile)} : <> From 9687b55f3e354f31b636a708c5edea4992b60ee2 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 9 Jul 2021 10:36:09 -0500 Subject: [PATCH 049/204] tests: fix some tests on moved files --- packages/server/test/unit/plugins/child/run_plugins_spec.js | 2 +- packages/server/test/unit/plugins/child/ts_node_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/test/unit/plugins/child/run_plugins_spec.js b/packages/server/test/unit/plugins/child/run_plugins_spec.js index f4b088cbc1b8..6323777f78ba 100644 --- a/packages/server/test/unit/plugins/child/run_plugins_spec.js +++ b/packages/server/test/unit/plugins/child/run_plugins_spec.js @@ -10,7 +10,7 @@ const util = require(`${root}../../lib/plugins/util`) const resolve = require(`${root}../../lib/util/resolve`) const browserUtils = require(`${root}../../lib/browsers/utils`) const Fixtures = require(`${root}../../test/support/helpers/fixtures`) -const tsNodeUtil = require(`${root}../../lib/plugins/child/ts_node`) +const tsNodeUtil = require(`${root}../../lib/util/ts_node`) const runPlugins = require(`${root}../../lib/plugins/child/run_plugins`) diff --git a/packages/server/test/unit/plugins/child/ts_node_spec.js b/packages/server/test/unit/plugins/child/ts_node_spec.js index e5a5ef7c6c12..84b261f1ceff 100644 --- a/packages/server/test/unit/plugins/child/ts_node_spec.js +++ b/packages/server/test/unit/plugins/child/ts_node_spec.js @@ -4,7 +4,7 @@ const tsnode = require('ts-node') const resolve = require(`${root}../../lib/util/resolve`) -const tsNodeUtil = require(`${root}../../lib/plugins/child/ts_node`) +const tsNodeUtil = require(`${root}../../lib/util/ts_node`) describe('lib/plugins/child/ts_node', () => { beforeEach(() => { From 52620b4d2a28d5edea1867ed67443d6f139d8318 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 16 Jul 2021 10:33:53 -0500 Subject: [PATCH 050/204] tests: fix tests of desktop-gui this.config was used in a few tests but never initialized I initialized with only a testFiles member and a resolved member as expected --- .../cypress/integration/error_message_spec.js | 2 +- .../cypress/integration/global_mode_spec.js | 2 +- .../cypress/integration/login_spec.js | 2 +- .../cypress/integration/project_mode_spec.js | 2 +- .../cypress/integration/project_spec.js | 2 +- .../cypress/integration/projects_list_spec.js | 2 +- .../cypress/integration/setup_project_spec.js | 2 +- .../cypress/integration/warning_message_spec.js | 2 +- packages/desktop-gui/cypress/support/index.js | 7 +++++++ .../desktop-gui/src/projects/projects-api.js | 16 ++++++++-------- 10 files changed, 23 insertions(+), 16 deletions(-) diff --git a/packages/desktop-gui/cypress/integration/error_message_spec.js b/packages/desktop-gui/cypress/integration/error_message_spec.js index f0a21e48030d..e11ac7360153 100644 --- a/packages/desktop-gui/cypress/integration/error_message_spec.js +++ b/packages/desktop-gui/cypress/integration/error_message_spec.js @@ -35,7 +35,7 @@ describe('Error Message', function () { cy.stub(this.ipc, 'getOptions').resolves({ projectRoot: '/foo/bar' }) cy.stub(this.ipc, 'getCurrentUser').resolves(this.user) - cy.stub(this.ipc, 'openProject').resolves(this.config) + cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) cy.stub(this.ipc, 'closeProject').resolves() cy.stub(this.ipc, 'onConfigChanged') cy.stub(this.ipc, 'externalOpen') diff --git a/packages/desktop-gui/cypress/integration/global_mode_spec.js b/packages/desktop-gui/cypress/integration/global_mode_spec.js index 6cb37962f937..91f999958f23 100644 --- a/packages/desktop-gui/cypress/integration/global_mode_spec.js +++ b/packages/desktop-gui/cypress/integration/global_mode_spec.js @@ -28,7 +28,7 @@ describe('Global Mode', function () { cy.stub(this.ipc, 'updaterCheck').resolves(false) cy.stub(this.ipc, 'logOut').resolves({}) cy.stub(this.ipc, 'addProject').resolves(this.projects[0]) - cy.stub(this.ipc, 'openProject').resolves(this.config) + cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) cy.stub(this.ipc, 'getSpecs').yields(null, this.specs) cy.stub(this.ipc, 'offOpenProject') cy.stub(this.ipc, 'offGetSpecs') diff --git a/packages/desktop-gui/cypress/integration/login_spec.js b/packages/desktop-gui/cypress/integration/login_spec.js index 5a9f25433628..7e165ae36db2 100644 --- a/packages/desktop-gui/cypress/integration/login_spec.js +++ b/packages/desktop-gui/cypress/integration/login_spec.js @@ -15,7 +15,7 @@ describe('Login', function () { cy.stub(this.ipc, 'updaterCheck').resolves(false) cy.stub(this.ipc, 'getProjects').resolves([]) cy.stub(this.ipc, 'getProjectStatuses').resolves([]) - cy.stub(this.ipc, 'openProject').resolves(this.config) + cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) cy.stub(this.ipc, 'getSpecs').yields(null, this.specs) cy.stub(this.ipc, 'externalOpen') cy.stub(this.ipc, 'logOut').resolves() diff --git a/packages/desktop-gui/cypress/integration/project_mode_spec.js b/packages/desktop-gui/cypress/integration/project_mode_spec.js index cdf17ab1a833..4aa92aa35f39 100644 --- a/packages/desktop-gui/cypress/integration/project_mode_spec.js +++ b/packages/desktop-gui/cypress/integration/project_mode_spec.js @@ -14,7 +14,7 @@ describe('Project Mode', function () { cy.stub(this.ipc, 'onFocusTests') cy.stub(this.ipc, 'getOptions').resolves({ projectRoot: '/foo/bar' }) cy.stub(this.ipc, 'updaterCheck').resolves(false) - cy.stub(this.ipc, 'openProject').resolves(this.config) + cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) cy.stub(this.ipc, 'getSpecs') this.getCurrentUser = this.util.deferred() diff --git a/packages/desktop-gui/cypress/integration/project_spec.js b/packages/desktop-gui/cypress/integration/project_spec.js index 5a5fb428bd0a..ff84f7532f63 100644 --- a/packages/desktop-gui/cypress/integration/project_spec.js +++ b/packages/desktop-gui/cypress/integration/project_spec.js @@ -10,7 +10,7 @@ describe('Project', function () { cy.stub(this.ipc, 'getOptions').resolves({ projectRoot: '/foo/bar' }) cy.stub(this.ipc, 'getCurrentUser').resolves(this.user) - cy.stub(this.ipc, 'openProject').resolves(this.config) + cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) cy.stub(this.ipc, 'getSpecs').yields(null, this.specs) cy.stub(this.ipc, 'closeProject').resolves() cy.stub(this.ipc, 'onConfigChanged') diff --git a/packages/desktop-gui/cypress/integration/projects_list_spec.js b/packages/desktop-gui/cypress/integration/projects_list_spec.js index 43caadc00213..cdd3607b8198 100644 --- a/packages/desktop-gui/cypress/integration/projects_list_spec.js +++ b/packages/desktop-gui/cypress/integration/projects_list_spec.js @@ -28,7 +28,7 @@ describe('Projects List', function () { cy.stub(this.ipc, 'getCurrentUser').resolves(this.user) cy.stub(this.ipc, 'logOut').resolves({}) cy.stub(this.ipc, 'addProject').resolves(this.projects[0]) - cy.stub(this.ipc, 'openProject').resolves(this.config) + cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) cy.stub(this.ipc, 'getSpecs').yields(null, this.specs) cy.stub(this.ipc, 'removeProject') diff --git a/packages/desktop-gui/cypress/integration/setup_project_spec.js b/packages/desktop-gui/cypress/integration/setup_project_spec.js index 130513e72abd..d624eb78de3b 100644 --- a/packages/desktop-gui/cypress/integration/setup_project_spec.js +++ b/packages/desktop-gui/cypress/integration/setup_project_spec.js @@ -108,7 +108,7 @@ describe('Connect to Dashboard', function () { cy.stub(this.ipc, 'updaterCheck').resolves(false) cy.stub(this.ipc, 'closeBrowser').resolves(null) this.config.projectId = null - cy.stub(this.ipc, 'openProject').resolves(this.config) + cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) cy.stub(this.ipc, 'getSpecs').yields(null, this.specs) cy.stub(this.ipc, 'getRuns').resolves([]) cy.stub(this.ipc, 'getRecordKeys').resolves(this.keys) diff --git a/packages/desktop-gui/cypress/integration/warning_message_spec.js b/packages/desktop-gui/cypress/integration/warning_message_spec.js index e322983da51e..ab333b1f9a4c 100644 --- a/packages/desktop-gui/cypress/integration/warning_message_spec.js +++ b/packages/desktop-gui/cypress/integration/warning_message_spec.js @@ -12,7 +12,7 @@ describe('Warning Message', function () { cy.stub(this.ipc, 'getOptions').resolves({ projectRoot: '/foo/bar' }) cy.stub(this.ipc, 'getCurrentUser').resolves(this.user) - cy.stub(this.ipc, 'openProject').resolves(this.config) + cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) cy.stub(this.ipc, 'getSpecs').yields(null, this.specs) cy.stub(this.ipc, 'closeProject').resolves() cy.stub(this.ipc, 'onConfigChanged') diff --git a/packages/desktop-gui/cypress/support/index.js b/packages/desktop-gui/cypress/support/index.js index 9bc56cde2438..92feb213406e 100644 --- a/packages/desktop-gui/cypress/support/index.js +++ b/packages/desktop-gui/cypress/support/index.js @@ -9,6 +9,13 @@ require('cypress-real-events/support') const BluebirdPromise = require('bluebird') beforeEach(function () { + this.config = { + testFiles: '**/*.spec.js', + resolved: { + testFiles: '**/*.spec.js', + }, + } + this.util = { deferred (Promise = BluebirdPromise) { const deferred = {} diff --git a/packages/desktop-gui/src/projects/projects-api.js b/packages/desktop-gui/src/projects/projects-api.js index 6bed7ebab72c..1cec461b85e9 100644 --- a/packages/desktop-gui/src/projects/projects-api.js +++ b/packages/desktop-gui/src/projects/projects-api.js @@ -192,18 +192,18 @@ const openProject = (project) => { }) return ipc.openProject(project.path) - .then(({ config = {}, functions }) => { + .then(({ config, functions = [] }) => { // In this context we know we are in e2e. // The configuration in e2e has already been merged with the main. - // It is not useful to display those 2 fields explicitely. + // It is not useful to display component/e2e fields explicitely. // - // NOTE: Even if we wanted to, we cannot display them. + // NOTE: Even if we wanted to, we cannot display them accurately. // These two parameter could be functions and we - // cannot send/receive functions using ipc - delete config.e2e - delete config.resolved.e2e - delete config.component - delete config.resolved.component + // cannot send/receive functions using ipc. + + config.resolved = _.omit(config.resolved, 'e2e', 'component') + + config = _.omit(config, 'e2e', 'component') updateConfig(config, functions) const projectIdAndPath = { id: config.projectId, path: project.path } From 3f23589b08340d8e3847e4a4b36b4b4218a20305 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 16 Jul 2021 10:55:51 -0500 Subject: [PATCH 051/204] fix: error and specs when request is non FQDN --- .../integration/commands/request_spec.js | 24 ++----------------- packages/driver/src/cypress/error_messages.js | 2 +- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/packages/driver/cypress/integration/commands/request_spec.js b/packages/driver/cypress/integration/commands/request_spec.js index b65ebd7735f2..6fb55ba9b82e 100644 --- a/packages/driver/cypress/integration/commands/request_spec.js +++ b/packages/driver/cypress/integration/commands/request_spec.js @@ -830,7 +830,7 @@ describe('src/cy/commands/request', () => { expect(this.logs.length).to.eq(1) expect(lastLog.get('error')).to.eq(err) expect(lastLog.get('state')).to.eq('failed') - expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` in `cypress.config.js`. Neither of those values were present.') + expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` option. Neither of those values were present.') expect(err.docsUrl).to.eq('https://on.cypress.io/request') done() @@ -851,27 +851,7 @@ describe('src/cy/commands/request', () => { expect(this.logs.length).to.eq(1) expect(lastLog.get('error')).to.eq(err) expect(lastLog.get('state')).to.eq('failed') - expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` in `cypress.config.js` (currently disabled by --config-file=false). Neither of those values were present.') - - done() - }) - - cy.request('/foo/bar') - }) - - it('throws when url is not FQDN, notes that configFile is non-default', { - baseUrl: '', - configFile: 'foo.json', - }, function (done) { - cy.stub(cy, 'getRemoteLocation').withArgs('origin').returns('') - - cy.on('fail', (err) => { - const { lastLog } = this - - expect(this.logs.length).to.eq(1) - expect(lastLog.get('error')).to.eq(err) - expect(lastLog.get('state')).to.eq('failed') - expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` in `foo.json`. Neither of those values were present.') + expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` option. (currently disabled by --config-file=false). Neither of those values were present.') done() }) diff --git a/packages/driver/src/cypress/error_messages.js b/packages/driver/src/cypress/error_messages.js index d9feea255755..c4114813ffcb 100644 --- a/packages/driver/src/cypress/error_messages.js +++ b/packages/driver/src/cypress/error_messages.js @@ -1221,7 +1221,7 @@ module.exports = { }, url_invalid ({ configFile }) { return { - message: `${cmd('request')} must be provided a fully qualified \`url\` - one that begins with \`http\`. By default ${cmd('request')} will use either the current window's origin or the \`baseUrl\` in ${formatConfigFile(configFile)}. Neither of those values were present.`, + message: `${cmd('request')} must be provided a fully qualified \`url\` - one that begins with \`http\`. By default ${cmd('request')} will use either the current window's origin or the \`baseUrl\` option. Neither of those values were present.`, docsUrl: 'https://on.cypress.io/request', } }, From 6c12a0beb2b465dab610356b6a0c52b2a1436593 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 16 Jul 2021 11:42:50 -0500 Subject: [PATCH 052/204] fix: make sure that configFile always has a value --- packages/server/lib/project-base.ts | 6 ++---- packages/server/lib/util/settings.js | 9 +++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 6ad29272f6f3..c33be8366e00 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -385,11 +385,9 @@ export class ProjectBase extends EE { // internals and breaking cypress const allowedCfg = config.allowed(cfg) - const configFile = settings.pathToConfigFile(this.projectRoot, options) - const modifiedCfg = await plugins.init(allowedCfg, { projectRoot: this.projectRoot, - configFile, + configFile: settings.pathToConfigFile(this.projectRoot, options), testingType: options.testingType, onError: (err: Error) => this._onError(err, options), onWarning: options.onWarning, @@ -402,7 +400,7 @@ export class ProjectBase extends EE { // desktop-gui receives the proper config file, even if it is one of the defaults // we can't know in advance which one json, js or ts file will be present // but we need it to be forwarded to the gui for display and help - finalConfig.configFile = path.relative(this.projectRoot, finalConfig.configFile ?? configFile) + finalConfig.configFile = path.relative(this.projectRoot, finalConfig.configFile) return finalConfig } diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index ae2f57763672..2f8f41cb00e3 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -222,6 +222,15 @@ module.exports = { // else write the new reduced obj return this._write(file, changed) + .then(function (config) { + // when json configfile is written, update the value of the configfile + // with the value found. + // NOTE: it does not have to be cypress.json. + // it could be ./e2e/custom-config.json + config.configFile = path.relative(projectRoot, file) + + return config + }) }).catch((err) => { if (errors.isCypressErr(err)) { throw err From 70a86db5ce66b7905b7f766a776a7ffef6684b76 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 16 Jul 2021 12:00:23 -0500 Subject: [PATCH 053/204] tests: restore some of the test --- .../desktop-gui/src/projects/projects-api.js | 2 +- .../integration/commands/request_spec.js | 24 +++++++++++++++++-- packages/driver/src/cypress/error_messages.js | 4 ++-- packages/server/lib/project-base.ts | 6 +++-- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/packages/desktop-gui/src/projects/projects-api.js b/packages/desktop-gui/src/projects/projects-api.js index 1cec461b85e9..69e959d8dde5 100644 --- a/packages/desktop-gui/src/projects/projects-api.js +++ b/packages/desktop-gui/src/projects/projects-api.js @@ -192,7 +192,7 @@ const openProject = (project) => { }) return ipc.openProject(project.path) - .then(({ config, functions = [] }) => { + .then(({ config = {}, functions = [] } = {}) => { // In this context we know we are in e2e. // The configuration in e2e has already been merged with the main. // It is not useful to display component/e2e fields explicitely. diff --git a/packages/driver/cypress/integration/commands/request_spec.js b/packages/driver/cypress/integration/commands/request_spec.js index 6fb55ba9b82e..b466141e3613 100644 --- a/packages/driver/cypress/integration/commands/request_spec.js +++ b/packages/driver/cypress/integration/commands/request_spec.js @@ -830,7 +830,7 @@ describe('src/cy/commands/request', () => { expect(this.logs.length).to.eq(1) expect(lastLog.get('error')).to.eq(err) expect(lastLog.get('state')).to.eq('failed') - expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` option. Neither of those values were present.') + expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` in `cypress.json`. Neither of those values were present.') expect(err.docsUrl).to.eq('https://on.cypress.io/request') done() @@ -851,7 +851,27 @@ describe('src/cy/commands/request', () => { expect(this.logs.length).to.eq(1) expect(lastLog.get('error')).to.eq(err) expect(lastLog.get('state')).to.eq('failed') - expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` option. (currently disabled by --config-file=false). Neither of those values were present.') + expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` in `cypress.json` (currently disabled by --config-file=false). Neither of those values were present.') + + done() + }) + + cy.request('/foo/bar') + }) + + it('throws when url is not FQDN, notes that configFile is non-default', { + baseUrl: '', + configFile: 'foo.json', + }, function (done) { + cy.stub(cy, 'getRemoteLocation').withArgs('origin').returns('') + + cy.on('fail', (err) => { + const { lastLog } = this + + expect(this.logs.length).to.eq(1) + expect(lastLog.get('error')).to.eq(err) + expect(lastLog.get('state')).to.eq('failed') + expect(err.message).to.eq('`cy.request()` must be provided a fully qualified `url` - one that begins with `http`. By default `cy.request()` will use either the current window\'s origin or the `baseUrl` in `foo.json`. Neither of those values were present.') done() }) diff --git a/packages/driver/src/cypress/error_messages.js b/packages/driver/src/cypress/error_messages.js index c4114813ffcb..cd3bba2d3adb 100644 --- a/packages/driver/src/cypress/error_messages.js +++ b/packages/driver/src/cypress/error_messages.js @@ -21,7 +21,7 @@ const format = (data) => { const formatConfigFile = (configFile) => { if (configFile === false) { - return '`cypress.config.js` (currently disabled by --config-file=false)' + return '`cypress.json` (currently disabled by --config-file=false)' } return `\`${format(configFile)}\`` @@ -1221,7 +1221,7 @@ module.exports = { }, url_invalid ({ configFile }) { return { - message: `${cmd('request')} must be provided a fully qualified \`url\` - one that begins with \`http\`. By default ${cmd('request')} will use either the current window's origin or the \`baseUrl\` option. Neither of those values were present.`, + message: `${cmd('request')} must be provided a fully qualified \`url\` - one that begins with \`http\`. By default ${cmd('request')} will use either the current window's origin or the \`baseUrl\` in ${formatConfigFile(configFile)}. Neither of those values were present.`, docsUrl: 'https://on.cypress.io/request', } }, diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index c33be8366e00..6ad29272f6f3 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -385,9 +385,11 @@ export class ProjectBase extends EE { // internals and breaking cypress const allowedCfg = config.allowed(cfg) + const configFile = settings.pathToConfigFile(this.projectRoot, options) + const modifiedCfg = await plugins.init(allowedCfg, { projectRoot: this.projectRoot, - configFile: settings.pathToConfigFile(this.projectRoot, options), + configFile, testingType: options.testingType, onError: (err: Error) => this._onError(err, options), onWarning: options.onWarning, @@ -400,7 +402,7 @@ export class ProjectBase extends EE { // desktop-gui receives the proper config file, even if it is one of the defaults // we can't know in advance which one json, js or ts file will be present // but we need it to be forwarded to the gui for display and help - finalConfig.configFile = path.relative(this.projectRoot, finalConfig.configFile) + finalConfig.configFile = path.relative(this.projectRoot, finalConfig.configFile ?? configFile) return finalConfig } From 36fbafb036321c3e6723b71500d75763d53ea1b1 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 16 Jul 2021 15:49:58 -0500 Subject: [PATCH 054/204] test: fix 2 tests desktop gui --- packages/desktop-gui/cypress/integration/nav_spec.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/desktop-gui/cypress/integration/nav_spec.js b/packages/desktop-gui/cypress/integration/nav_spec.js index 6dcaf8d85e2a..48b206438bbb 100644 --- a/packages/desktop-gui/cypress/integration/nav_spec.js +++ b/packages/desktop-gui/cypress/integration/nav_spec.js @@ -91,7 +91,7 @@ describe('Navigation', function () { }) it('closes off hover', function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) cy.get('.docs-menu').trigger('mouseover') cy.get('.docs-dropdown').should('be.visible') @@ -104,7 +104,7 @@ describe('Navigation', function () { describe('ci1', function () { context('opens on click', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) cy.get('.docs-menu').trigger('mouseover') cy.get('.docs-dropdown').should('be.visible') @@ -160,27 +160,27 @@ describe('Navigation', function () { }) it('opens when after 4 days from first open, no projectId, and not already shown', function () { - this.openProject.resolve({ + this.openProject.resolve({ config: { ...this.config, projectId: null, state: { ...this.config.state, promptsShown: {}, }, - }) + } }) cy.get('.prompt-ci1').should('be.visible') }) it('sends correct utm_content when opened', function () { - this.openProject.resolve({ + this.openProject.resolve({ config: { ...this.config, projectId: null, state: { ...this.config.state, promptsShown: {}, }, - }) + } }) cy.get('.see-other-guides').click() cy.wrap(this.ipc.externalOpen).should('have.been.calledWithMatch', { From 30c2b3637048b99896d0640d3057cdac255ec0b3 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 16 Jul 2021 15:51:40 -0500 Subject: [PATCH 055/204] tests: make project nav tests pass --- .../cypress/integration/project_nav_spec.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/desktop-gui/cypress/integration/project_nav_spec.js b/packages/desktop-gui/cypress/integration/project_nav_spec.js index ee27737b99cf..558fb008c6d1 100644 --- a/packages/desktop-gui/cypress/integration/project_nav_spec.js +++ b/packages/desktop-gui/cypress/integration/project_nav_spec.js @@ -40,7 +40,7 @@ describe('Project Nav', function () { context('project nav', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('displays projects nav', function () { @@ -99,7 +99,7 @@ describe('Project Nav', function () { context('browsers dropdown', function () { describe('browsers available', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) context('normal browser list behavior', function () { @@ -324,7 +324,7 @@ describe('Project Nav', function () { channel: 'canary', })) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) cy.get('.browsers-list .dropdown-chosen') .should('contain', 'Canary').and('not.contain', 'Edge') @@ -335,7 +335,7 @@ describe('Project Nav', function () { it('displays chosen browser with old string-style id in localStorage', function () { localStorage.setItem('chosenBrowser', 'chrome') - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) cy.get('.browsers-list .dropdown-chosen') .should('contain', 'Chrome') @@ -344,7 +344,7 @@ describe('Project Nav', function () { it('displays default browser with null localStorage', function () { localStorage.removeItem('chosenBrowser') - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) cy.get('.browsers-list .dropdown-chosen') .should('contain', this.config.browsers[0].displayName) @@ -355,7 +355,7 @@ describe('Project Nav', function () { beforeEach(function () { localStorage.setItem('chosenBrowser', 'netscape-navigator') - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('defaults to first browser', () => { @@ -375,7 +375,7 @@ describe('Project Nav', function () { majorVersion: '50', }] - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('displays no dropdown btn', () => { @@ -400,7 +400,7 @@ describe('Project Nav', function () { }, ] - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('shows warning icon with linkified tooltip', function () { @@ -419,7 +419,7 @@ describe('Project Nav', function () { beforeEach(function () { this.config.browsers[this.config.browsers.length - 1].custom = true - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('displays generic icon', () => { @@ -454,7 +454,7 @@ describe('Project Nav', function () { info: this.info, }] - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('shows info icon with linkified tooltip', function () { @@ -470,7 +470,7 @@ describe('Project Nav', function () { context('issue #869 - nav responsiveness', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('main nav does not block project nav when long project name pushes it to multiple lines', () => { From 08303ae5b60badd942726a975569bd691d4adec3 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 16 Jul 2021 15:55:47 -0500 Subject: [PATCH 056/204] test: desktop-gui more fixes --- .../cypress/integration/runs_list_spec.js | 36 +++++++-------- .../cypress/integration/settings_spec.js | 26 +++++------ .../cypress/integration/specs_list_spec.js | 44 +++++++++---------- 3 files changed, 53 insertions(+), 53 deletions(-) diff --git a/packages/desktop-gui/cypress/integration/runs_list_spec.js b/packages/desktop-gui/cypress/integration/runs_list_spec.js index a90f7a1fcc84..19f9da5951c1 100644 --- a/packages/desktop-gui/cypress/integration/runs_list_spec.js +++ b/packages/desktop-gui/cypress/integration/runs_list_spec.js @@ -59,7 +59,7 @@ describe('Runs List', function () { context('page display', function () { beforeEach(function () { this.getCurrentUser.resolve(this.user) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.getRuns.resolve(this.runs) this.goToRuns() @@ -77,7 +77,7 @@ describe('Runs List', function () { cy.clock(timestamp) this.getCurrentUser.resolve(this.user) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.getRuns.resolve(this.runs) this.goToRuns() @@ -249,7 +249,7 @@ describe('Runs List', function () { beforeEach(function () { this.getCurrentUser.resolve(this.user) this.config.projectId = this.projects[0].id - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.pingApiServer.resolve() const timestamp = new Date(2016, 11, 19, 10, 0, 0).valueOf() @@ -299,7 +299,7 @@ describe('Runs List', function () { context('without a current user', function () { beforeEach(function () { this.getCurrentUser.resolve(null) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.pingApiServer.resolve() this.goToRuns() @@ -325,7 +325,7 @@ describe('Runs List', function () { beforeEach(function () { this.getCurrentUser.resolve(this.user) this.config.projectId = undefined - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.pingApiServer.resolve() this.goToRuns() @@ -341,7 +341,7 @@ describe('Runs List', function () { beforeEach(function () { this.getCurrentUser.resolve(null) this.config.projectId = undefined - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.pingApiServer.resolve() this.goToRuns() @@ -368,7 +368,7 @@ describe('Runs List', function () { describe('polling runs', function () { beforeEach(function () { this.getCurrentUser.resolve(this.user) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.pingApiServer.resolve() this.getRunsAgain = this.util.deferred() this.ipc.getRuns.onCall(1).returns(this.getRunsAgain.promise) @@ -459,7 +459,7 @@ describe('Runs List', function () { describe('manually refreshing runs', function () { beforeEach(function () { this.getCurrentUser.resolve(this.user) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.pingApiServer.resolve() this.getRunsAgain = this.util.deferred() this.ipc.getRuns.onCall(1).returns(this.getRunsAgain.promise) @@ -510,7 +510,7 @@ describe('Runs List', function () { describe('permissions error', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.goToRuns() }) @@ -674,7 +674,7 @@ describe('Runs List', function () { describe('timed out error', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.goToRuns().then(() => { this.getRuns.reject({ name: 'foo', message: 'There\'s an error', type: 'TIMED_OUT' }) @@ -689,7 +689,7 @@ describe('Runs List', function () { describe('not found error', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.goToRuns().then(() => { this.getRuns.reject({ name: 'foo', message: 'There\'s an error', type: 'NOT_FOUND' }) @@ -704,7 +704,7 @@ describe('Runs List', function () { describe('unauthenticated error', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.goToRuns().then(() => { this.getRuns.reject({ name: '', message: '', statusCode: 401 }) @@ -722,7 +722,7 @@ describe('Runs List', function () { describe('no project id error', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.ipc.setupDashboardProject.resolves(this.validCiProject) this.ipc.getRuns.onCall(1).resolves([]) @@ -753,7 +753,7 @@ describe('Runs List', function () { describe('unexpected error', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.goToRuns().then(() => { this.getRuns.reject({ name: 'foo', message: `{"no runs": "for you"}`, type: 'UNKNOWN' }) @@ -769,7 +769,7 @@ describe('Runs List', function () { describe('unauthorized project', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.getProjectStatus.resolve({ state: 'UNAUTHORIZED', }) @@ -784,7 +784,7 @@ describe('Runs List', function () { describe('invalid project', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.ipc.setupDashboardProject.resolves(this.validCiProject) this.getProjectStatus.resolve({ @@ -821,7 +821,7 @@ describe('Runs List', function () { context('having never setup CI', function () { beforeEach(function () { this.config.projectId = null - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.goToRuns().then(() => { this.getRuns.resolve([]) @@ -845,7 +845,7 @@ describe('Runs List', function () { context('having previously set up CI', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.goToRuns().then(() => { this.getRuns.resolve([]) diff --git a/packages/desktop-gui/cypress/integration/settings_spec.js b/packages/desktop-gui/cypress/integration/settings_spec.js index 6891dbea6812..398380d26875 100644 --- a/packages/desktop-gui/cypress/integration/settings_spec.js +++ b/packages/desktop-gui/cypress/integration/settings_spec.js @@ -52,7 +52,7 @@ describe('Settings', () => { describe('general functionality', () => { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -99,7 +99,7 @@ describe('Settings', () => { describe('configuration panel', () => { describe('displays config', () => { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -359,7 +359,7 @@ describe('Settings', () => { describe('project id panel', () => { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -393,7 +393,7 @@ describe('Settings', () => { describe('record key panel', () => { context('when project is set up and you have access', () => { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -523,7 +523,7 @@ describe('Settings', () => { }) it('does not show ci Keys section when project is invalid', function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.projectStatuses[0].state = 'INVALID' this.getProjectStatus.resolve(this.projectStatuses[0]) this.goToSettings() @@ -535,7 +535,7 @@ describe('Settings', () => { context('when you are not a user of this projects org', () => { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('does not show record key', function () { @@ -590,7 +590,7 @@ describe('Settings', () => { describe('proxy settings panel', () => { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.config.resolved.baseUrl.value = 'http://localhost:7777' this.projectStatuses[0].id = this.config.projectId @@ -654,7 +654,7 @@ describe('Settings', () => { describe('no experimental features turned on', () => { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -674,7 +674,7 @@ describe('Settings', () => { value: true, } - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -708,7 +708,7 @@ describe('Settings', () => { value: true, } - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -736,7 +736,7 @@ describe('Settings', () => { from: 'default', } - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -770,7 +770,7 @@ describe('Settings', () => { cy.stub(this.ipc, 'getUserEditor').returns(this.getUserEditor.promise) cy.stub(this.ipc, 'setUserEditor').resolves() - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -847,7 +847,7 @@ describe('Settings', () => { this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.goToSettings() openConfiguration() diff --git a/packages/desktop-gui/cypress/integration/specs_list_spec.js b/packages/desktop-gui/cypress/integration/specs_list_spec.js index 0cdd87393c15..2db7b18ccefa 100644 --- a/packages/desktop-gui/cypress/integration/specs_list_spec.js +++ b/packages/desktop-gui/cypress/integration/specs_list_spec.js @@ -41,7 +41,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, []) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('displays empty message', () => { @@ -80,7 +80,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.componentSpecs) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('shows both types of specs', () => { @@ -103,7 +103,7 @@ describe('Specs List', function () { beforeEach(function () { this.config.isNewProject = true - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) context('banner', function () { @@ -145,7 +145,7 @@ describe('Specs List', function () { describe('first time user in existing project', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) this.ipc.hasOpenedCypress.resolves(false) }) @@ -179,7 +179,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specsWindows) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) context('displays list of specs', function () { @@ -212,7 +212,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) context('run all specs', function () { @@ -360,7 +360,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('collapsing root spec will keep root itself expanded', function () { @@ -439,7 +439,7 @@ describe('Specs List', function () { unit: [], }) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('hides expand/collapse buttons when there are no folders', function () { @@ -451,7 +451,7 @@ describe('Specs List', function () { context('filtering specs', function () { it('scrolls the specs and not the filter', function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) cy.contains('last_list_spec').scrollIntoView() cy.get('.filter').should('be.visible') @@ -462,7 +462,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) cy.contains('.all-tests', runAllIntegrationSpecsLabel) cy.get('.filter').type('new') @@ -576,7 +576,7 @@ describe('Specs List', function () { }) it('applies it for the appropriate project', function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) cy.get('.filter').should('have.value', 'app') cy.contains('.all-tests', 'Run 1 integration spec') @@ -584,7 +584,7 @@ describe('Specs List', function () { it('does not apply it for a different project', function () { this.config.projectId = 'different' - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) cy.get('.filter').should('have.value', '') }) @@ -597,7 +597,7 @@ describe('Specs List', function () { }) it('saves the filter to local storage', function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) cy.get('.filter').type('my-filter') cy.window().then((win) => { @@ -610,7 +610,7 @@ describe('Specs List', function () { it('applies the saved filter when returning to the project', function () { cy.window().then(function (win) { win.localStorage[`specsFilter--/foo/bar`] = JSON.stringify('my-filter') - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) cy.get('.filter').should('have.value', 'my-filter') @@ -621,7 +621,7 @@ describe('Specs List', function () { context('click on spec', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) cy.contains('.file .file-name-wrapper', 'app_spec.coffee').as('firstSpec') }) @@ -668,7 +668,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) context('choose shallow spec', function () { @@ -704,7 +704,7 @@ describe('Specs List', function () { context('switching specs', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) cy.get('.file').contains('a', 'app_spec.coffee').as('firstSpec') .click() @@ -727,7 +727,7 @@ describe('Specs List', function () { context('with component tests', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('shows separate run specs buttons', function () { @@ -778,7 +778,7 @@ describe('Specs List', function () { context('returning to specs tab', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) // https://github.com/cypress-io/cypress/issues/9151 @@ -805,7 +805,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('updates spec list selected on specChanged', function () { @@ -832,7 +832,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) cy.get('.file').contains('a', 'app_spec.coffee').parent().as('spec') cy.get('@spec').contains('Open in IDE').as('button') @@ -986,7 +986,7 @@ describe('Specs List', function () { describe('new spec file', function () { beforeEach(function () { - this.openProject.resolve(this.config) + this.openProject.resolve({ config: this.config }) }) it('launches system save dialog', function () { From e122dfbde3d2b43817c03ab0cedff407d7c1939b Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 16 Jul 2021 16:50:32 -0500 Subject: [PATCH 057/204] test: fix settings --- .../cypress/integration/settings_spec.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/desktop-gui/cypress/integration/settings_spec.js b/packages/desktop-gui/cypress/integration/settings_spec.js index 398380d26875..ddac865a40bf 100644 --- a/packages/desktop-gui/cypress/integration/settings_spec.js +++ b/packages/desktop-gui/cypress/integration/settings_spec.js @@ -225,7 +225,7 @@ describe('Settings', () => { this.ipc.onConfigChanged.yield() cy.contains('.line', 'env:null').then(() => { - this.ipc.openProject.resolves(this.config) + this.ipc.openProject.resolves({ config: this.config }) this.ipc.onConfigChanged.yield() cy.contains('.line', 'env:fileServerFolder') @@ -233,7 +233,7 @@ describe('Settings', () => { this.ipc.openProject.resolves(setConfigEnv(this.config, null)) this.ipc.onConfigChanged.yield() cy.contains('.line', 'env:null').then(() => { - this.ipc.openProject.resolves(this.config) + this.ipc.openProject.resolves({ config: this.config }) this.ipc.onConfigChanged.yield() cy.contains('.line', 'env:fileServerFolder') @@ -306,7 +306,7 @@ describe('Settings', () => { newConfig.clientUrl = 'http://localhost:8888' newConfig.clientUrlDisplay = 'http://localhost:8888' newConfig.browsers = this.browsers - this.openProject.resolve(newConfig) + this.openProject.resolve({ config: newConfig }) this.goToSettings() @@ -317,7 +317,7 @@ describe('Settings', () => { const newConfig = this.util.deepClone(this.config) newConfig.resolved.baseUrl.value = 'http://localhost:7777' - this.ipc.openProject.onCall(1).resolves(newConfig) + this.ipc.openProject.onCall(1).resolves({ config: newConfig }) this.ipc.onConfigChanged.yield() cy.contains('http://localhost:7777') @@ -326,9 +326,9 @@ describe('Settings', () => { context('when configFile is false', () => { beforeEach(function () { - this.openProject.resolve(Cypress._.assign({ + this.openProject.resolve({ config: Cypress._.assign({ configFile: false, - }, this.config)) + }, this.config) }) this.goToSettings() @@ -342,9 +342,9 @@ describe('Settings', () => { context('when configFile is set', function () { beforeEach(function () { - this.openProject.resolve(Cypress._.assign({ + this.openProject.resolve({ config: Cypress._.assign({ configFile: 'special-cypress.json', - }, this.config)) + }, this.config) }) this.goToSettings() @@ -555,7 +555,7 @@ describe('Settings', () => { beforeEach(function () { this.navigateWithConfig = function (config) { - this.openProject.resolve(_.defaults(config, this.config)) + this.openProject.resolve({ config: _.defaults(config, this.config) }) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) this.goToSettings() @@ -877,8 +877,8 @@ describe('Settings', () => { // -- function setConfigEnv (config, v) { - return flow([ + return { config: flow([ merge(config), set('resolved.env', v), - ])({}) + ])({}) } } From cb0e18707b2ec55568ad69350e6b3f31588f6eaa Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 16 Jul 2021 17:24:35 -0500 Subject: [PATCH 058/204] tess: last tests for desktop-gui --- packages/desktop-gui/src/project/project-model.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop-gui/src/project/project-model.js b/packages/desktop-gui/src/project/project-model.js index 9373171bafd9..d6f829fd26cd 100644 --- a/packages/desktop-gui/src/project/project-model.js +++ b/packages/desktop-gui/src/project/project-model.js @@ -276,7 +276,7 @@ export default class Project { } getConfigValue (key) { - if (!this.resolvedConfig) return + if (!this.resolvedConfig || !this.resolvedConfig[key]) return return toJS(this.resolvedConfig[key]).value } From 73fc60def4617b5adff095729539648d7819515c Mon Sep 17 00:00:00 2001 From: Zach Bloomquist Date: Fri, 16 Jul 2021 14:52:23 -0400 Subject: [PATCH 059/204] =?UTF-8?q?chore:=20revert=20"fix(deps):=20update?= =?UTF-8?q?=20dependency=20electron=20to=20v13=20=F0=9F=8C=9F=20(#17037)"?= =?UTF-8?q?=20(#17372)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit bda59dd7cc407518ae673eeffc49576652dc4972. --- .node-version | 2 +- appveyor.yml | 2 +- circle.yml | 5 ++--- package.json | 2 +- packages/electron/package.json | 2 +- scripts/run-docker-local.sh | 2 +- yarn.lock | 10 +++++----- 7 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.node-version b/.node-version index 62df50f1eefe..2a0dc9a810cf 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -14.17.0 +14.16.0 diff --git a/appveyor.yml b/appveyor.yml index e68db73558be..48189a8e4246 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,7 +8,7 @@ branches: # https://www.appveyor.com/docs/lang/nodejs-iojs/ environment: # use matching version of Node.js - nodejs_version: "14.17.0" + nodejs_version: "14.16.0" # encode secure variables which will NOT be used # in pull requests # https://www.appveyor.com/docs/build-configuration/#secure-variables diff --git a/circle.yml b/circle.yml index 19c64156cb5b..ee71591d5d93 100644 --- a/circle.yml +++ b/circle.yml @@ -49,7 +49,7 @@ executors: # the Docker image with Cypress dependencies and Chrome browser cy-doc: docker: - - image: cypress/browsers:node14.17.0-chrome91-ff89 + - image: cypress/browsers:node14.16.0-chrome90-ff88 # by default, we use "small" to save on CI costs. bump on a per-job basis if needed. resource_class: small environment: @@ -58,7 +58,7 @@ executors: # Docker image with non-root "node" user non-root-docker-user: docker: - - image: cypress/browsers:node14.17.0-chrome91-ff89 + - image: cypress/browsers:node14.16.0-chrome90-ff88 user: node environment: PLATFORM: linux @@ -1126,7 +1126,6 @@ jobs: runner-integration-tests-electron: <<: *defaults - resource_class: medium parallelism: 2 steps: - run-runner-integration-tests: diff --git a/package.json b/package.json index ce5dcaa05c48..7d0664ab4906 100644 --- a/package.json +++ b/package.json @@ -196,7 +196,7 @@ "yarn-deduplicate": "3.1.0" }, "engines": { - "node": ">=14.17.0", + "node": ">=14.16.0", "yarn": ">=1.17.3" }, "productName": "Cypress", diff --git a/packages/electron/package.json b/packages/electron/package.json index f04b955d3a92..393751786549 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -24,7 +24,7 @@ "minimist": "1.2.5" }, "devDependencies": { - "electron": "13.1.6", + "electron": "12.0.0-beta.14", "execa": "4.1.0", "mocha": "3.5.3" }, diff --git a/scripts/run-docker-local.sh b/scripts/run-docker-local.sh index 2f359b980fbf..17dd15696c02 100755 --- a/scripts/run-docker-local.sh +++ b/scripts/run-docker-local.sh @@ -3,7 +3,7 @@ set e+x echo "This script should be run from cypress's root" -name=cypress/browsers:node14.17.0-chrome91-ff89 +name=cypress/browsers:node14.16.0-chrome90-ff88 echo "Pulling CI container $name" docker pull $name diff --git a/yarn.lock b/yarn.lock index d304da703721..aea84ea8bc4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16943,10 +16943,10 @@ electron-to-chromium@^1.3.247, electron-to-chromium@^1.3.378, electron-to-chromi resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz#857e310ca00f0b75da4e1db6ff0e073cc4a91ddf" integrity sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg== -electron@13.1.6: - version "13.1.6" - resolved "https://registry.npmjs.org/electron/-/electron-13.1.6.tgz#6ecaf969255d62ce82cc0b5c948bf26e7dfb489b" - integrity sha512-XiB55/JTaQpDFQrD9pulYnOGwaWeMyRIub5ispvoE2bWBvM5zVMLptwMLb0m3KTMrfSkzhedZvOu7fwYvR7L7Q== +electron@12.0.0-beta.14: + version "12.0.0-beta.14" + resolved "https://registry.npmjs.org/electron/-/electron-12.0.0-beta.14.tgz#f8c40c7e479879c305e519380e710c0a357aa734" + integrity sha512-PYM+EepIEj9kLePXEb9gIxzZk5H4zM7LGg5iw60OHt+SYEECPNFJmPj3N6oHKu3W+KrCG7285Vgz2ZCp1u0kKA== dependencies: "@electron/get" "^1.0.1" "@types/node" "^14.6.2" @@ -31047,7 +31047,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.yarnpkg.com/pretty-format/-/pretty-format-26.4.0.tgz#c08073f531429e9e5024049446f42ecc9f933a3b" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.0.tgz#c08073f531429e9e5024049446f42ecc9f933a3b" integrity sha512-mEEwwpCseqrUtuMbrJG4b824877pM5xald3AkilJ47Po2YLr97/siejYQHqj2oDQBeJNbu+Q0qUuekJ8F0NAPg== dependencies: "@jest/types" "^26.3.0" From 34b70946847196bca3cc25660b5c233e3e58ac22 Mon Sep 17 00:00:00 2001 From: Ben Kucera <14625260+Bkucera@users.noreply.github.com> Date: Fri, 16 Jul 2021 15:18:03 -0400 Subject: [PATCH 060/204] fix(ui): use mouseenter instead of mouseover for ui hover events (#17258) * use mouseenter instead of mouseover for ui hover events --- packages/reporter/cypress/integration/commands_spec.ts | 9 ++++++++- packages/reporter/src/commands/command.tsx | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/reporter/cypress/integration/commands_spec.ts b/packages/reporter/cypress/integration/commands_spec.ts index 4790cf41c96b..469e2b8df2a2 100644 --- a/packages/reporter/cypress/integration/commands_spec.ts +++ b/packages/reporter/cypress/integration/commands_spec.ts @@ -306,13 +306,20 @@ describe('commands', () => { beforeEach(() => { cy.spy(runner, 'emit') cy.clock() - cy.get('.command').first().trigger('mouseover') + cy.get('.command-wrapper').first().trigger('mouseover') + + // react uses mouseover for mouseenter events, + // and uses e.fromElement to decide to send it + cy.get('.command-method').first().trigger('mouseover', { + fromElement: cy.$$('.command-wrapper-text:first')[0], + }) }) it('shows snapshot after 50ms passes', () => { cy.wrap(runner.emit).should('not.be.calledWith', 'runner:show:snapshot') cy.tick(50) cy.wrap(runner.emit).should('be.calledWith', 'runner:show:snapshot', 1) + cy.wrap(runner.emit).should('be.calledOnce') }) describe('then mousing out', () => { diff --git a/packages/reporter/src/commands/command.tsx b/packages/reporter/src/commands/command.tsx index 09908cf338db..ef211f2937e4 100644 --- a/packages/reporter/src/commands/command.tsx +++ b/packages/reporter/src/commands/command.tsx @@ -187,8 +187,8 @@ class Command extends Component { 'command-is-open': this.isOpen, }, )} - onMouseOver={() => this._snapshot(true)} - onMouseOut={() => this._snapshot(false)} + onMouseEnter={() => this._snapshot(true)} + onMouseLeave={() => this._snapshot(false)} > Date: Fri, 16 Jul 2021 22:32:05 -0500 Subject: [PATCH 061/204] add some logs --- packages/server/lib/util/require_async.ts | 5 ++++- packages/server/lib/util/require_async_child.js | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/server/lib/util/require_async.ts b/packages/server/lib/util/require_async.ts index cb58b2545e28..a7adf21abdf3 100644 --- a/packages/server/lib/util/require_async.ts +++ b/packages/server/lib/util/require_async.ts @@ -43,11 +43,11 @@ export default async function requireAsync (filePath: string, options: RequireAs const childArguments = ['--projectRoot', options.projectRoot, '--file', filePath, '--loadErrorCode', options.loadErrorCode] + debug('fork child process', path.join(__dirname, 'require_async_child.js'), childArguments, childOptions) configProcess = cp.fork(path.join(__dirname, 'require_async_child.js'), childArguments, childOptions) const ipc = util.wrapIpc(configProcess) return new Promise((resolve, reject) => { - ipc.send('load', options.functionNames) ipc.on('loaded', ({ result, functionNames }) => { debug('resolving with result %o', result) debug('resolving with functions %o', functionNames) @@ -61,5 +61,8 @@ export default async function requireAsync (filePath: string, options: RequireAs reject(errors.get(type, ...args)) }) + + debug('trigger the load of the file') + ipc.send('load', options.functionNames) }) } diff --git a/packages/server/lib/util/require_async_child.js b/packages/server/lib/util/require_async_child.js index b1e9c9310c51..f5e20203fd3d 100644 --- a/packages/server/lib/util/require_async_child.js +++ b/packages/server/lib/util/require_async_child.js @@ -51,6 +51,7 @@ function run (ipc, requiredFile, projectRoot) { ipc.on('load', (functionNames) => { try { + debug('try loading', requiredFile) const exp = require(requiredFile) const result = exp.default || exp From 3959fc3b2149c3eb92deb25bfd805d276e4344d0 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 16 Jul 2021 22:33:22 -0500 Subject: [PATCH 062/204] scaffold an empty es6 config file --- packages/server/lib/util/settings.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 2f8f41cb00e3..e2723c5d8f57 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -183,7 +183,12 @@ module.exports = { return this._write(file, {}) } - return fs.writeFile(file, 'module.exports = {}').then(() => ({})) + return fs.writeFile(file, `import { defineConfig } from 'cypress' + +export default defineConfig({ + +}) +`).then(() => ({})) }) .then(({ result: configObject, functionNames }) => { const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' From 7b62c9d430026dd1e5152a25a0f847df26558a38 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 16 Jul 2021 22:53:21 -0500 Subject: [PATCH 063/204] add more logs --- packages/server/lib/util/require_async_child.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/lib/util/require_async_child.js b/packages/server/lib/util/require_async_child.js index f5e20203fd3d..e7c5098567ed 100644 --- a/packages/server/lib/util/require_async_child.js +++ b/packages/server/lib/util/require_async_child.js @@ -27,6 +27,7 @@ function run (ipc, requiredFile, projectRoot) { } if (!tsRegistered) { + debug('register typescript for required file') tsNodeUtil.register(projectRoot, requiredFile) // ensure typescript is only registered once From 1c053b7bc1577f2e160d1198e7313c8a1eaef3e3 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 17 Jul 2021 09:53:29 -0500 Subject: [PATCH 064/204] add some output to childprocess --- packages/server/lib/util/require_async.ts | 53 ++++++++++++++--------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/packages/server/lib/util/require_async.ts b/packages/server/lib/util/require_async.ts index a7adf21abdf3..00885aaac9c5 100644 --- a/packages/server/lib/util/require_async.ts +++ b/packages/server/lib/util/require_async.ts @@ -8,7 +8,7 @@ import Debug from 'debug' const debug = Debug('cypress:server:require_async') -let configProcess: cp.ChildProcess +let requireProcess: cp.ChildProcess | null interface RequireAsyncOptions{ projectRoot: string @@ -24,40 +24,51 @@ interface ChildOptions{ execArgv?: string[] } +const killChildProcess = () => { + requireProcess && requireProcess.kill() + requireProcess = null +} + export default async function requireAsync (filePath: string, options: RequireAsyncOptions): Promise { - if (configProcess) { - debug('kill existing config process') - configProcess.kill() - } + return new Promise((resolve, reject) => { + if (requireProcess) { + debug('kill existing config process') + killChildProcess() + } - const childOptions: ChildOptions = { - stdio: 'inherit', - } + const childOptions: ChildOptions = { + stdio: 'inherit', + } - if (inspector.url()) { - childOptions.execArgv = _.chain(process.execArgv.slice(0)) - .remove('--inspect-brk') - .push(`--inspect=${process.debugPort + 1}`) - .value() - } + if (inspector.url()) { + childOptions.execArgv = _.chain(process.execArgv.slice(0)) + .remove('--inspect-brk') + .push(`--inspect=${process.debugPort + 1}`) + .value() + } - const childArguments = ['--projectRoot', options.projectRoot, '--file', filePath, '--loadErrorCode', options.loadErrorCode] + const childArguments = ['--projectRoot', options.projectRoot, '--file', filePath, '--loadErrorCode', options.loadErrorCode] - debug('fork child process', path.join(__dirname, 'require_async_child.js'), childArguments, childOptions) - configProcess = cp.fork(path.join(__dirname, 'require_async_child.js'), childArguments, childOptions) - const ipc = util.wrapIpc(configProcess) + debug('fork child process', path.join(__dirname, 'require_async_child.js'), childArguments, childOptions) + requireProcess = cp.fork(path.join(__dirname, 'require_async_child.js'), childArguments, childOptions) + const ipc = util.wrapIpc(requireProcess) + + if (requireProcess.stdout && requireProcess.stderr) { + // manually pipe plugin stdout and stderr for dashboard capture + // @see https://github.com/cypress-io/cypress/issues/7434 + requireProcess.stdout.on('data', (data) => process.stdout.write(data)) + requireProcess.stderr.on('data', (data) => process.stderr.write(data)) + } - return new Promise((resolve, reject) => { ipc.on('loaded', ({ result, functionNames }) => { debug('resolving with result %o', result) debug('resolving with functions %o', functionNames) - configProcess.kill() resolve({ result, functionNames }) }) ipc.on('load:error', (type, ...args) => { debug('load:error %s, rejecting', type) - configProcess.kill() + killChildProcess() reject(errors.get(type, ...args)) }) From 075b3f4be598a4b2cdf32bed6aa5a465d6294ad2 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 17 Jul 2021 10:59:33 -0500 Subject: [PATCH 065/204] rename registered variable --- packages/server/lib/util/ts_node.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/server/lib/util/ts_node.js b/packages/server/lib/util/ts_node.js index ca673032458b..96f4dce380de 100644 --- a/packages/server/lib/util/ts_node.js +++ b/packages/server/lib/util/ts_node.js @@ -3,30 +3,30 @@ const path = require('path') const tsnode = require('ts-node') const resolve = require('./resolve') -const getTsNodeOptions = (tsPath, pluginsFile) => { +const getTsNodeOptions = (tsPath, registeredFile) => { return { compiler: tsPath, // use the user's installed typescript compilerOptions: { - module: 'CommonJS', + module: 'es2018', }, // resolves tsconfig.json starting from the plugins directory // instead of the cwd (the project root) - dir: path.dirname(pluginsFile), + dir: path.dirname(registeredFile), transpileOnly: true, // transpile only (no type-check) for speed } } -const register = (projectRoot, pluginsFile) => { +const register = (projectRoot, registeredFile) => { try { debug('projectRoot path: %s', projectRoot) - debug('pluginsFile: %s', pluginsFile) + debug('registeredFile: %s', registeredFile) const tsPath = resolve.typescript(projectRoot) if (!tsPath) return debug('typescript path: %s', tsPath) - const tsOptions = getTsNodeOptions(tsPath, pluginsFile) + const tsOptions = getTsNodeOptions(tsPath, registeredFile) debug('registering project TS with options %o', tsOptions) From 8531751c0f664dd4b6b0d898be9d006e51a160c3 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 17 Jul 2021 14:57:17 -0500 Subject: [PATCH 066/204] fix: avoid running the cypress.config twice with the same code --- packages/server/lib/modes/run.js | 12 +++++++----- packages/server/lib/project-base.ts | 8 ++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/server/lib/modes/run.js b/packages/server/lib/modes/run.js index 84586809368d..556e01814510 100644 --- a/packages/server/lib/modes/run.js +++ b/packages/server/lib/modes/run.js @@ -462,7 +462,7 @@ const iterateThroughSpecs = function (options = {}) { return serial() } -const getProjectId = Promise.method((project, id) => { +const getProjectId = Promise.method(async (project, id) => { if (id == null) { id = env.get('CYPRESS_PROJECT_ID') } @@ -472,10 +472,12 @@ const getProjectId = Promise.method((project, id) => { return id } - return project.getProjectId() - .catch(() => { - // no id no problem - return null + return project.getConfig().then((conf) => { + return project.getProjectId(conf) + .catch(() => { + // no id no problem + return null + }) }) }) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 6ad29272f6f3..fb0a8e788b36 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -39,7 +39,7 @@ import { checkSupportFile } from './project_utils' // TODO: Figure out how to type this better. type ReceivedCypressOptions = Partial> - & Partial> + & Partial> export interface Cfg extends ReceivedCypressOptions { projectRoot: string @@ -819,15 +819,15 @@ export class ProjectBase extends EE { // These methods are not related to start server/sockets/runners - async getProjectId () { + async getProjectId (config?: Cfg): Promise { await this.verifyExistence() - const readSettings = await settings.read(this.projectRoot, this.options) + const readSettings = config || await settings.read(this.projectRoot, this.options) if (readSettings && readSettings.projectId) { return readSettings.projectId } - errors.throw('NO_PROJECT_ID', settings.configFile(this.projectRoot, this.options), this.projectRoot) + return errors.throw('NO_PROJECT_ID', settings.configFile(this.projectRoot, this.options), this.projectRoot) } async verifyExistence () { From 28e896e53a0359e2d8610e8cfd0abfa1da280566 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 17 Jul 2021 15:22:09 -0500 Subject: [PATCH 067/204] rollback change on ts_node --- packages/server/lib/util/ts_node.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/util/ts_node.js b/packages/server/lib/util/ts_node.js index 96f4dce380de..cc9306dce336 100644 --- a/packages/server/lib/util/ts_node.js +++ b/packages/server/lib/util/ts_node.js @@ -7,7 +7,7 @@ const getTsNodeOptions = (tsPath, registeredFile) => { return { compiler: tsPath, // use the user's installed typescript compilerOptions: { - module: 'es2018', + module: 'CommonJS', }, // resolves tsconfig.json starting from the plugins directory // instead of the cwd (the project root) From 99518d2036ef69ac61a3cbe078d64a9b8b68dbf4 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 17 Jul 2021 22:47:09 -0500 Subject: [PATCH 068/204] protect project base against configFile:false --- packages/server/lib/project-base.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index fb0a8e788b36..9a01267a1779 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -402,7 +402,9 @@ export class ProjectBase extends EE { // desktop-gui receives the proper config file, even if it is one of the defaults // we can't know in advance which one json, js or ts file will be present // but we need it to be forwarded to the gui for display and help - finalConfig.configFile = path.relative(this.projectRoot, finalConfig.configFile ?? configFile) + if (configFile !== false) { + finalConfig.configFile = path.relative(this.projectRoot, finalConfig.configFile ?? configFile) + } return finalConfig } From bb675d3494417ffcfec65956629770e26b469ee9 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 17 Jul 2021 22:48:55 -0500 Subject: [PATCH 069/204] add missing snapshot for plugins --- .../server/__snapshots__/3_plugins_spec.js | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/packages/server/__snapshots__/3_plugins_spec.js b/packages/server/__snapshots__/3_plugins_spec.js index 0646319276a8..92b9abd1b481 100644 --- a/packages/server/__snapshots__/3_plugins_spec.js +++ b/packages/server/__snapshots__/3_plugins_spec.js @@ -662,3 +662,63 @@ Available browsers found on your system are: - electron ` + +exports['e2e plugins can config plugins directly in the cypress.config.js 1'] = ` + +==================================================================================================== + + (Run Starting) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Cypress: 1.2.3 │ + │ Browser: FooBrowser 88 │ + │ Specs: 1 found (app_spec.js) │ + │ Searched: cypress/integration/app_spec.js │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +──────────────────────────────────────────────────────────────────────────────────────────────────── + + Running: app_spec.js (1 of 1) + + + ✓ overrides config + ✓ overrides env + + 2 passing + + + (Results) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Tests: 2 │ + │ Passing: 2 │ + │ Failing: 0 │ + │ Pending: 0 │ + │ Skipped: 0 │ + │ Screenshots: 0 │ + │ Video: true │ + │ Duration: X seconds │ + │ Spec Ran: app_spec.js │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + (Video) + + - Started processing: Compressing to 20 CRF + - Finished processing: /XXX/XXX/XXX/cypress/videos/app_spec.js.mp4 (X second) + + +==================================================================================================== + + (Run Finished) + + + Spec Tests Passing Failing Pending Skipped + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ ✔ app_spec.js XX:XX 2 2 - - - │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + ✔ All specs passed! XX:XX 2 2 - - - + + +` From 40575fa0dd18febc2686806ecc10a735ee4c51cf Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 17 Jul 2021 23:03:35 -0500 Subject: [PATCH 070/204] fix mismatching validations --- packages/server/lib/config_options.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/lib/config_options.ts b/packages/server/lib/config_options.ts index 047e8694c66f..05b499ed4295 100644 --- a/packages/server/lib/config_options.ts +++ b/packages/server/lib/config_options.ts @@ -44,7 +44,7 @@ export const options = [ name: 'component', // runner-ct overrides defaultValue: null, - validation: v.functionOverrides, + validation: v.isOverrideFunction, }, { name: 'componentFolder', defaultValue: 'cypress/component', @@ -74,7 +74,7 @@ export const options = [ name: 'e2e', // e2e runner overrides defaultValue: null, - validation: v.functionOverrides, + validation: v.isOverrideFunction, }, { name: 'env', validation: v.isPlainObject, From 8e0380ca9edc80065e6f20453bcb55697c78ca92 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 17 Jul 2021 23:23:32 -0500 Subject: [PATCH 071/204] validation fixes on e2e and component --- packages/server/lib/util/validation.js | 10 +++++++--- packages/server/test/unit/config_spec.js | 12 ++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/server/lib/util/validation.js b/packages/server/lib/util/validation.js index 6ae9908b5a8f..af31a7633780 100644 --- a/packages/server/lib/util/validation.js +++ b/packages/server/lib/util/validation.js @@ -186,12 +186,16 @@ const isOneOf = (...values) => { } } -const isOverrideFunction = (key, config) => { - if (typeof config === 'function') { +const isOverrideFunction = (key, value) => { + if (typeof config !== 'function' && !_.isPlainObject(value) && value !== null) { + return errMsg(key, value, `a plain object or a function`) + } + + if (value === null || typeof value === 'function') { return true } - return isValidConfig(key, config) + return isValidConfig(key, value) } /** diff --git a/packages/server/test/unit/config_spec.js b/packages/server/test/unit/config_spec.js index 2f32f7ecf236..cb74b9e11aa2 100644 --- a/packages/server/test/unit/config_spec.js +++ b/packages/server/test/unit/config_spec.js @@ -245,9 +245,9 @@ describe('lib/config', () => { return this.expectValidationPasses() }) - it('fails if not a plain object', function () { + it('fails if not a plain object or a function', function () { this.setup({ component: false }) - this.expectValidationFails('to be a plain object') + this.expectValidationFails('to be a plain object or a function') return this.expectValidationFails('the value was: `false`') }) @@ -1455,11 +1455,11 @@ describe('lib/config', () => { browsers: { value: [], from: 'default' }, chromeWebSecurity: { value: true, from: 'default' }, clientCertificates: { value: [], from: 'default' }, - component: { from: 'default', value: {} }, + component: { from: 'default', value: null }, componentFolder: { value: 'cypress/component', from: 'default' }, defaultCommandTimeout: { value: 4000, from: 'default' }, downloadsFolder: { value: 'cypress/downloads', from: 'default' }, - e2e: { from: 'default', value: {} }, + e2e: { from: 'default', value: null }, env: {}, execTimeout: { value: 60000, from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, @@ -1539,12 +1539,12 @@ describe('lib/config', () => { blockHosts: { value: null, from: 'default' }, browsers: { value: [], from: 'default' }, chromeWebSecurity: { value: true, from: 'default' }, - component: { from: 'default', value: {} }, + component: { from: 'default', value: null }, clientCertificates: { value: [], from: 'default' }, componentFolder: { value: 'cypress/component', from: 'default' }, defaultCommandTimeout: { value: 4000, from: 'default' }, downloadsFolder: { value: 'cypress/downloads', from: 'default' }, - e2e: { from: 'default', value: {} }, + e2e: { from: 'default', value: null }, execTimeout: { value: 60000, from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, experimentalInteractiveRunEvents: { value: false, from: 'default' }, From b8f845f398817e53ad6b3beba1d6fa39e21007b8 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 19 Jul 2021 10:33:33 -0400 Subject: [PATCH 072/204] deal with unmentionned e2e and component functions --- packages/server/lib/util/validation.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/lib/util/validation.js b/packages/server/lib/util/validation.js index af31a7633780..d4c64fd1a878 100644 --- a/packages/server/lib/util/validation.js +++ b/packages/server/lib/util/validation.js @@ -187,11 +187,11 @@ const isOneOf = (...values) => { } const isOverrideFunction = (key, value) => { - if (typeof config !== 'function' && !_.isPlainObject(value) && value !== null) { + if (typeof config !== 'function' && !_.isPlainObject(value) && value !== null && value !== undefined) { return errMsg(key, value, `a plain object or a function`) } - if (value === null || typeof value === 'function') { + if (value === undefined || value === null || typeof value === 'function') { return true } From 62b0200e6e933763c4ae089511a2f958f4d26bf5 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 20 Jul 2021 10:40:35 -0400 Subject: [PATCH 073/204] fi tests --- packages/server/lib/util/validation.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/lib/util/validation.js b/packages/server/lib/util/validation.js index d4c64fd1a878..945fce2d7cd0 100644 --- a/packages/server/lib/util/validation.js +++ b/packages/server/lib/util/validation.js @@ -187,11 +187,11 @@ const isOneOf = (...values) => { } const isOverrideFunction = (key, value) => { - if (typeof config !== 'function' && !_.isPlainObject(value) && value !== null && value !== undefined) { + if (typeof value !== 'function' && !_.isPlainObject(value) && value !== null) { return errMsg(key, value, `a plain object or a function`) } - if (value === undefined || value === null || typeof value === 'function') { + if (value === null || typeof value === 'function') { return true } From 1063c0139f1da780805aedb17115a80ac3d5cd65 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 20 Jul 2021 11:26:01 -0400 Subject: [PATCH 074/204] test: make path ponint to an existing path --- packages/server/test/unit/project_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/test/unit/project_spec.js b/packages/server/test/unit/project_spec.js index 1af5d8af5f0a..a56a2255e1f0 100644 --- a/packages/server/test/unit/project_spec.js +++ b/packages/server/test/unit/project_spec.js @@ -92,7 +92,7 @@ describe('lib/project-base', () => { sinon.stub(ServerE2E.prototype, 'open').resolves([]) sinon.stub(ProjectBase.prototype, 'startCtDevServer').resolves({ port: 9999 }) - const projectCt = new ProjectBase({ projectRoot: '../foo/bar', projectType: 'ct' }) + const projectCt = new ProjectBase({ projectRoot: this.todosPath, projectType: 'ct' }) await projectCt.initializeConfig() From 3b3d75528eaf3bd93b2b81d144b02345135d06f3 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 20 Jul 2021 12:01:13 -0400 Subject: [PATCH 075/204] allow for no cypress config to be found --- packages/server/lib/util/require_async_child.js | 8 +++++++- packages/server/lib/util/settings.js | 9 ++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/server/lib/util/require_async_child.js b/packages/server/lib/util/require_async_child.js index e7c5098567ed..6f667f9dee9d 100644 --- a/packages/server/lib/util/require_async_child.js +++ b/packages/server/lib/util/require_async_child.js @@ -74,7 +74,13 @@ function run (ipc, requiredFile, projectRoot) { debug('config %o', result) } catch (err) { - debug('failed to load requiredFile:\n%s', err.stack) + debug('failed to load requiredFile:\n%s', err.code, err.stack) + if (err.code === 'ENOENT' || err.code === 'MODULE_NOT_FOUND') { + ipc.send('load:error', err.code, requiredFile, err.stack) + + return + } + ipc.send('load:error', loadErrorCode, requiredFile, err.stack) return diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index e2723c5d8f57..b8596fcf3489 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -149,7 +149,7 @@ module.exports = { // directory is writable return fs.accessAsync(projectRoot, fs.W_OK) }).catch({ code: 'ENOENT' }, () => { - // cypress.json does not exist, we missing project + // cypress.config.js does not exist, we missing project log('cannot find file %s', file) return this._err('CONFIG_FILE_NOT_FOUND', this.configFile(projectRoot, options), projectRoot) @@ -178,7 +178,9 @@ module.exports = { loadErrorCode: 'CONFIG_FILE_ERROR', functionNames: ['e2e', 'component'], }) - .catch({ code: 'ENOENT' }, (e) => { + .catch((e) => e.type === 'MODULE_NOT_FOUND' || e.code === 'ENOENT', (err) => { + debug('file not found', file, err) + if (/\.json$/.test(file)) { return this._write(file, {}) } @@ -190,7 +192,7 @@ export default defineConfig({ }) `).then(() => ({})) }) - .then(({ result: configObject, functionNames }) => { + .then(({ result: configObject = {}, functionNames = [] }) => { const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' debug('resolved configObject', configObject) @@ -237,6 +239,7 @@ export default defineConfig({ return config }) }).catch((err) => { + debug('an error occured when reading config', err) if (errors.isCypressErr(err)) { throw err } From 1b825a61ebdb826eb0411fca54bd786a82b024cf Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 20 Jul 2021 12:10:41 -0400 Subject: [PATCH 076/204] make better mocks for getProjectId --- packages/server/test/unit/modes/run_spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/test/unit/modes/run_spec.js b/packages/server/test/unit/modes/run_spec.js index 8083cde61f03..d889602ab52f 100644 --- a/packages/server/test/unit/modes/run_spec.js +++ b/packages/server/test/unit/modes/run_spec.js @@ -47,6 +47,7 @@ describe('lib/modes/run', () => { it('is null when no projectId', () => { const project = { getProjectId: sinon.stub().rejects(new Error), + getConfig: () => Promise.resolve({}), } return runMode.getProjectId(project) From 359cda1d38dfb13607da917d7adba4a2352cb3c9 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 20 Jul 2021 12:13:07 -0400 Subject: [PATCH 077/204] test: fix events comps --- packages/server/test/unit/gui/events_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/test/unit/gui/events_spec.js b/packages/server/test/unit/gui/events_spec.js index 6d8a80e2dece..6bd9550ac758 100644 --- a/packages/server/test/unit/gui/events_spec.js +++ b/packages/server/test/unit/gui/events_spec.js @@ -675,7 +675,7 @@ describe('lib/gui/events', () => { .then((assert) => { expect(this.send.firstCall.args[0]).to.eq('response') // [1].id).to.match(/setup:dashboard:project-/) expect(this.send.firstCall.args[1].id).to.match(/open:project-/) - expect(this.send.firstCall.args[1].data).to.eql({ some: 'config' }) + expect(this.send.firstCall.args[1].data).to.eql({ config: { some: 'config' }, functions: [] }) }) }) From 7321ec87c0d0d944cbc10ad4c24ca746213a3374 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 20 Jul 2021 13:32:29 -0400 Subject: [PATCH 078/204] test: files repairs --- packages/server/lib/gui/files.ts | 10 ++++++++++ packages/server/test/unit/gui/files_spec.ts | 7 +++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/server/lib/gui/files.ts b/packages/server/lib/gui/files.ts index 64be43622765..900bd63aef5b 100644 --- a/packages/server/lib/gui/files.ts +++ b/packages/server/lib/gui/files.ts @@ -1,10 +1,15 @@ +import Debug from 'debug' import openProject from '../open_project' import { createFile } from '../util/spec_writer' import { showSaveDialog } from './dialog' +const debug = Debug('cypress:server:gui:files') + export const showDialogAndCreateSpec = () => { return openProject.getConfig() .then((cfg) => { + debug('got config') + return showSaveDialog(cfg.integrationFolder).then((path) => { return { cfg, @@ -15,6 +20,8 @@ export const showDialogAndCreateSpec = () => { .then((opt) => { const { path } = opt + debug('got opt', opt) + // only create file if they selected a file if (path) { return createFile(path) @@ -23,6 +30,7 @@ export const showDialogAndCreateSpec = () => { return opt }) .then(({ cfg, path }) => { + debug('file potentially created', path) if (!path) { return { specs: null, @@ -34,6 +42,8 @@ export const showDialogAndCreateSpec = () => { // we reload here so we can update ui immediately instead of // waiting for file watching to send updated spec list return openProject.getSpecs(cfg).then((specs) => { + debug('found specs', specs) + return { specs, path, diff --git a/packages/server/test/unit/gui/files_spec.ts b/packages/server/test/unit/gui/files_spec.ts index 5b007a953af4..9802722e41d2 100644 --- a/packages/server/test/unit/gui/files_spec.ts +++ b/packages/server/test/unit/gui/files_spec.ts @@ -1,4 +1,4 @@ -import '../../spec_helper' +import { sinon } from '../../spec_helper' import { expect } from 'chai' import 'sinon-chai' @@ -7,6 +7,7 @@ import { showDialogAndCreateSpec } from '../../../lib/gui/files' import openProject from '../../../lib/open_project' import { ProjectBase } from '../../../lib/project-base' import * as dialog from '../../../lib/gui/dialog' +import { fs } from '../../../lib/util/fs' import * as specWriter from '../../../lib/util/spec_writer' describe('gui/files', () => { @@ -30,13 +31,15 @@ describe('gui/files', () => { ], } + sinon.stub(fs, 'readdirSync').returns(['cypress.json']) + this.err = new Error('foo') sinon.stub(ProjectBase.prototype, 'open').resolves() sinon.stub(ProjectBase.prototype, 'getConfig').resolves(this.config) this.showSaveDialog = sinon.stub(dialog, 'showSaveDialog').resolves(this.selectedPath) - this.createFile = sinon.stub(specWriter, 'createFile').resolves({}) + this.createFile = sinon.stub(specWriter, 'createFile').resolves({ path: this.selectedPath }) this.getSpecs = sinon.stub(openProject, 'getSpecs').resolves(this.specs) return openProject.create('/_test-output/path/to/project-e2e') From 2b9e2c3746db0f5b0b9952030742984a00ac52af Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 20 Jul 2021 16:34:40 -0400 Subject: [PATCH 079/204] test: plugins files --- packages/server/lib/plugins/child/run_plugins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js index f5742dd2d14a..0b5d3812ac0f 100644 --- a/packages/server/lib/plugins/child/run_plugins.js +++ b/packages/server/lib/plugins/child/run_plugins.js @@ -142,7 +142,7 @@ let tsRegistered = false function getPluginsFunction (pluginsFile, functionName) { const exp = require(pluginsFile) - const resolvedExport = exp.default || exp + const resolvedExport = exp && exp.default ? exp.default : exp return functionName ? resolvedExport[functionName] : resolvedExport } From 9841b01ec174e8ad4562b076d64fac196b958b07 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 20 Jul 2021 16:43:33 -0400 Subject: [PATCH 080/204] fix some more tests --- packages/server/test/unit/plugins/index_spec.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/server/test/unit/plugins/index_spec.js b/packages/server/test/unit/plugins/index_spec.js index 7a8bdae6c74f..e2aff475f8d7 100644 --- a/packages/server/test/unit/plugins/index_spec.js +++ b/packages/server/test/unit/plugins/index_spec.js @@ -57,10 +57,10 @@ describe('lib/plugins/index', () => { const args = cp.fork.lastCall.args[1] - expect(args[0]).to.equal('--file') - expect(args[1]).to.include('plugins/child/default_plugins_file.js') - expect(args[2]).to.equal('--projectRoot') - expect(args[3]).to.equal('/path/to/project/root') + expect(args[0]).to.equal('--projectRoot') + expect(args[1]).to.equal('/path/to/project/root') + expect(args[2]).to.equal('--file') + expect(args[3]).to.include('plugins/child/default_plugins_file.js') }) }) @@ -73,7 +73,7 @@ describe('lib/plugins/index', () => { expect(cp.fork).to.be.called expect(cp.fork.lastCall.args[0]).to.contain('plugins/child/index.js') - expect(cp.fork.lastCall.args[1]).to.eql(['--file', 'cypress-plugin', '--projectRoot', '/path/to/project/root']) + expect(cp.fork.lastCall.args[1]).to.eql(['--projectRoot', '/path/to/project/root', '--file', 'cypress-plugin']) }) }) From 360d0be3f7d8347c26417932419de61968903e39 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 20 Jul 2021 16:52:34 -0400 Subject: [PATCH 081/204] test: fix project test --- packages/server/test/unit/project_utils_spec.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/server/test/unit/project_utils_spec.ts b/packages/server/test/unit/project_utils_spec.ts index 009b92e93588..1f7c0703e5e3 100644 --- a/packages/server/test/unit/project_utils_spec.ts +++ b/packages/server/test/unit/project_utils_spec.ts @@ -1,7 +1,9 @@ import Chai from 'chai' +import path from 'path' +const { sinon } = require('../spec_helper') import { getSpecUrl, checkSupportFile } from '../../lib/project_utils' import Fixtures from '../support/helpers/fixtures' -import path from 'path' +import settings from '../../lib/util/settings' const todosPath = Fixtures.projectPath('todos') @@ -97,6 +99,10 @@ describe('lib/project_utils', () => { }) describe('checkSupportFile', () => { + beforeEach(() => { + sinon.stub(settings, 'configFile').returns({}) + }) + it('does nothing when {supportFile: false}', async () => { const ret = await checkSupportFile({ configFile: 'cypress.json', From 34c77bc4ad1eca1521c4fd877dc93a17b8a3d5a8 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 21 Jul 2021 09:29:06 -0400 Subject: [PATCH 082/204] test: fix settings tests --- packages/server/lib/util/settings.js | 11 ++++++++--- packages/server/test/unit/settings_spec.js | 13 ++++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index b8596fcf3489..f81d9b6d0d7f 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -12,6 +12,7 @@ const debug = require('debug')('cypress:server:settings') // here since we can read + write the // settings at the same time something else // is potentially reading it + const flattenCypress = (obj) => { return obj.cypress ? obj.cypress : undefined } @@ -149,7 +150,7 @@ module.exports = { // directory is writable return fs.accessAsync(projectRoot, fs.W_OK) }).catch({ code: 'ENOENT' }, () => { - // cypress.config.js does not exist, we missing project + // cypress.config.js does not exist, completely new project log('cannot find file %s', file) return this._err('CONFIG_FILE_NOT_FOUND', this.configFile(projectRoot, options), projectRoot) @@ -179,12 +180,16 @@ module.exports = { functionNames: ['e2e', 'component'], }) .catch((e) => e.type === 'MODULE_NOT_FOUND' || e.code === 'ENOENT', (err) => { - debug('file not found', file, err) + debug('file not found', file) if (/\.json$/.test(file)) { + debug('seeding a json config file') + return this._write(file, {}) } + debug('seeding a js/ts config file') + return fs.writeFile(file, `import { defineConfig } from 'cypress' export default defineConfig({ @@ -195,7 +200,7 @@ export default defineConfig({ .then(({ result: configObject = {}, functionNames = [] }) => { const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' - debug('resolved configObject', configObject) + debug('resolved that configObject', configObject) if ((testingType in configObject)) { if (typeof configObject[testingType] === 'object') { diff --git a/packages/server/test/unit/settings_spec.js b/packages/server/test/unit/settings_spec.js index 2373b3c74618..0824d409fb69 100644 --- a/packages/server/test/unit/settings_spec.js +++ b/packages/server/test/unit/settings_spec.js @@ -31,7 +31,7 @@ describe('lib/settings', () => { .then(() => { return settings.read(projectRoot) }).then((obj) => { - expect(obj).to.deep.eq({ foo: 'bar' }) + expect(obj).to.deep.eq({ configFile: 'cypress.json', foo: 'bar' }) return fs.readJsonAsync('cypress.json') }).then((obj) => { @@ -123,6 +123,7 @@ describe('lib/settings', () => { component: { component_setting: 'peep', }, + configFile: 'cypress.config.js', }) }) }) @@ -146,6 +147,7 @@ describe('lib/settings', () => { e2e: { e2e_setting: 'e2e_setting', }, + configFile: 'cypress.config.js', }) }) }) @@ -169,6 +171,7 @@ describe('lib/settings', () => { e2e: { e2e_setting: 'e2e_setting', }, + configFile: 'cypress.config.js', }) }) }) @@ -205,7 +208,7 @@ describe('lib/settings', () => { .then(() => { return settings.read(projectRoot) }).then((obj) => { - expect(obj).to.deep.eq({ defaultCommandTimeout: 30000, foo: 'bar' }) + expect(obj).to.deep.eq({ configFile: 'cypress.json', defaultCommandTimeout: 30000, foo: 'bar' }) }) }) @@ -214,7 +217,7 @@ describe('lib/settings', () => { .then(() => { return settings.read(projectRoot) }).then((obj) => { - expect(obj).to.deep.eq({ supportFile: 'foo', foo: 'bar' }) + expect(obj).to.deep.eq({ configFile: 'cypress.json', supportFile: 'foo', foo: 'bar' }) }) }) @@ -223,7 +226,7 @@ describe('lib/settings', () => { .then(() => { return settings.read(projectRoot) }).then((obj) => { - expect(obj).to.deep.eq({ pageLoadTimeout: 30000, foo: 'bar' }) + expect(obj).to.deep.eq({ configFile: 'cypress.json', pageLoadTimeout: 30000, foo: 'bar' }) }) }) @@ -232,7 +235,7 @@ describe('lib/settings', () => { .then(() => { return settings.read(projectRoot) }).then((obj) => { - expect(obj).to.deep.eq({ pageLoadTimeout: 30000, foo: 'bar' }) + expect(obj).to.deep.eq({ configFile: 'cypress.json', pageLoadTimeout: 30000, foo: 'bar' }) }) }) }) From 4b55c3f5af7ae158a49140f1b9ed0e611ba1019f Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 21 Jul 2021 12:27:16 -0400 Subject: [PATCH 083/204] make sure we load ts code and js code separately --- packages/server/lib/util/settings.js | 38 +++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index f81d9b6d0d7f..73343bc30121 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -7,6 +7,20 @@ const { fs } = require('./fs') const requireAsync = require('./require_async').default const debug = require('debug')('cypress:server:settings') +const jsCode = `const { defineConfig } = require('cypress') + +module.export = defineConfig({ + +}) +` + +const tsCode = `import { defineConfig } from 'cypress' + +export default defineConfig({ + +}) +` + // TODO: // think about adding another PSemaphore // here since we can read + write the @@ -140,9 +154,18 @@ module.exports = { }) }, + /** + * Ensures the project at this root has a config file + * that is readable and writable by the node process + * @param {string} projectRoot root of the project + * @param {object} options + * @returns + */ exists (projectRoot, options = {}) { const file = this.pathToConfigFile(projectRoot, options) + debug('find out if "%s" exists', file) + // first check if cypress.json exists return maybeVerifyConfigFile(file) .then(() => { @@ -150,15 +173,19 @@ module.exports = { // directory is writable return fs.accessAsync(projectRoot, fs.W_OK) }).catch({ code: 'ENOENT' }, () => { + debug('no module error') // cypress.config.js does not exist, completely new project log('cannot find file %s', file) return this._err('CONFIG_FILE_NOT_FOUND', this.configFile(projectRoot, options), projectRoot) }).catch({ code: 'EACCES' }, () => { + debug('no access error') + // we cannot write due to folder permissions return errors.warning('FOLDER_NOT_WRITABLE', projectRoot) }).catch((err) => { if (errors.isCypressErr(err)) { + debug('throwing error') throw err } @@ -190,17 +217,14 @@ module.exports = { debug('seeding a js/ts config file') - return fs.writeFile(file, `import { defineConfig } from 'cypress' + const code = /\.ts$/.test(file) ? tsCode : jsCode -export default defineConfig({ - -}) -`).then(() => ({})) + return fs.writeFile(file, code).then(() => ({})) }) .then(({ result: configObject = {}, functionNames = [] }) => { const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' - debug('resolved that configObject', configObject) + debug('resolved configObject', configObject) if ((testingType in configObject)) { if (typeof configObject[testingType] === 'object') { @@ -274,6 +298,8 @@ export default defineConfig({ return Promise.resolve({}) } + debug('write a file') + return this.read(projectRoot, options) .then((settings) => { _.extend(settings, obj) From 5cb69a9fd864832900b55f3f39e922a30070a7b1 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 21 Jul 2021 14:40:11 -0400 Subject: [PATCH 084/204] fix build --- packages/server/lib/modes/run.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/server/lib/modes/run.js b/packages/server/lib/modes/run.js index b23d437eec3e..e48a09090048 100644 --- a/packages/server/lib/modes/run.js +++ b/packages/server/lib/modes/run.js @@ -472,12 +472,12 @@ const getProjectId = Promise.method(async (project, id) => { return id } - return project.getConfig().then((conf) => { - return project.getProjectId(conf) - .catch(() => { - // no id no problem - return null - }) + const conf = project.getConfig() + + return project.getProjectId(conf) + .catch(() => { + // no id no problem + return null }) }) From 5c4d6ebe61b4fa524ec2b32ab54586740d0d738c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 21 Jul 2021 16:52:43 -0400 Subject: [PATCH 085/204] test: fix the crasher ? --- packages/server/test/unit/socket_spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/test/unit/socket_spec.js b/packages/server/test/unit/socket_spec.js index 3ed60fb6c563..1b62f3f45250 100644 --- a/packages/server/test/unit/socket_spec.js +++ b/packages/server/test/unit/socket_spec.js @@ -85,7 +85,7 @@ describe('lib/socket', () => { }) // https://github.com/cypress-io/cypress/issues/4346 - it('can emit a circular object without crashing', function (done) { + /* it('can emit a circular object without crashing', function (done) { const foo = { bar: {}, } @@ -101,7 +101,7 @@ describe('lib/socket', () => { return done() }) - }) + }) */ context('on(automation:request)', () => { describe('#onAutomation', () => { From da79d58972d52e4d4e80209633361b9446e0dcdf Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 21 Jul 2021 17:00:11 -0400 Subject: [PATCH 086/204] return objects when creating settings file --- packages/server/lib/util/settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 73343bc30121..8d97c6cc3334 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -219,9 +219,9 @@ module.exports = { const code = /\.ts$/.test(file) ? tsCode : jsCode - return fs.writeFile(file, code).then(() => ({})) + return fs.writeFile(file, code).then(() => ({ configObject: {}, functionNames: [] })) }) - .then(({ result: configObject = {}, functionNames = [] }) => { + .then(({ result: configObject, functionNames }) => { const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' debug('resolved configObject', configObject) From 20dd33b5924cd0f9ad4e3eb9ec59a6456a184310 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 21 Jul 2021 17:33:59 -0400 Subject: [PATCH 087/204] test: fix some tests --- packages/server/lib/util/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 8d97c6cc3334..30b712a40b2e 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -221,7 +221,7 @@ module.exports = { return fs.writeFile(file, code).then(() => ({ configObject: {}, functionNames: [] })) }) - .then(({ result: configObject, functionNames }) => { + .then(({ result: configObject = {}, functionNames = [] }) => { const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' debug('resolved configObject', configObject) From 49b9a82c026a32b4ad65632d09b67558d70ed37d Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 21 Jul 2021 17:41:38 -0400 Subject: [PATCH 088/204] remove changes to socket --- packages/server/test/unit/socket_spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/test/unit/socket_spec.js b/packages/server/test/unit/socket_spec.js index 1b62f3f45250..3ed60fb6c563 100644 --- a/packages/server/test/unit/socket_spec.js +++ b/packages/server/test/unit/socket_spec.js @@ -85,7 +85,7 @@ describe('lib/socket', () => { }) // https://github.com/cypress-io/cypress/issues/4346 - /* it('can emit a circular object without crashing', function (done) { + it('can emit a circular object without crashing', function (done) { const foo = { bar: {}, } @@ -101,7 +101,7 @@ describe('lib/socket', () => { return done() }) - }) */ + }) context('on(automation:request)', () => { describe('#onAutomation', () => { From 90e3816ee270c78a2bea68c4ce0f0944e86a5e04 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 22 Jul 2021 14:48:54 -0500 Subject: [PATCH 089/204] fix: if closeProcessor is false --- packages/server/lib/project-base.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 131a523d37fe..69d79593c2bf 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -337,7 +337,7 @@ export class ProjectBase extends EE { await Promise.all([ this.server?.close(), this.watchers?.close(), - closePreprocessor?.(), + typeof closePreprocessor === 'function' ? closePreprocessor() : Promise.resolve(), ]) process.chdir(localCwd) From 615f3680ce8c6fbd9019bc60b54defc85ec770be Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 22 Jul 2021 14:50:17 -0500 Subject: [PATCH 090/204] tests: close the ct server after test --- packages/server/test/unit/project_spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/test/unit/project_spec.js b/packages/server/test/unit/project_spec.js index 1486ac044ecd..a4bff21692d3 100644 --- a/packages/server/test/unit/project_spec.js +++ b/packages/server/test/unit/project_spec.js @@ -101,6 +101,7 @@ describe('lib/project-base', () => { expect(projectCt._cfg.viewportWidth).to.eq(500) expect(projectCt._cfg.baseUrl).to.eq('http://localhost:9999') expect(projectCt.startCtDevServer).to.have.beenCalled + projectCt.close() }) }) From bc8e74fe1aaa79829b1d7b695941f84a3ea1be64 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 22 Jul 2021 21:35:14 -0500 Subject: [PATCH 091/204] feat: detect tsconfig.json and create ts file --- packages/server/lib/util/settings.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 30b712a40b2e..3e5a2f3ab0ce 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -140,6 +140,12 @@ module.exports = { return 'cypress.json' } + // if we find a tsconfig.json let's make users life easy + // and create a TypeScript file. + if (ls.includes('tsconfig.json')) { + return 'cypress.config.ts' + } + // Default is to create a new `cypress.config.js` file if one does not exist. return 'cypress.config.js' }, From 5ca271c91fac5d206da7c5af0bb860be93b11f4c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 22 Jul 2021 22:32:17 -0500 Subject: [PATCH 092/204] make sure we scaffold properly --- packages/server/lib/util/settings.js | 40 ++++++++++++++++------------ 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 3e5a2f3ab0ce..25c1faa41cfc 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -206,26 +206,39 @@ module.exports = { const file = this.pathToConfigFile(projectRoot, options) + const handleNormalError = (err) => { + debug('an error occured when reading config', err) + if (errors.isCypressErr(err)) { + throw err + } + + return this._logReadErr(file, err) + } + return requireAsync(file, { projectRoot, loadErrorCode: 'CONFIG_FILE_ERROR', functionNames: ['e2e', 'component'], }) - .catch((e) => e.type === 'MODULE_NOT_FOUND' || e.code === 'ENOENT', (err) => { - debug('file not found', file) + .catch((err) => { + if (err.type === 'MODULE_NOT_FOUND' || err.code === 'ENOENT') { + debug('file not found', file) - if (/\.json$/.test(file)) { - debug('seeding a json config file') + if (/\.json$/.test(file)) { + debug('seeding a json config file') - return this._write(file, {}) - } + return this._write(file, {}) + } - debug('seeding a js/ts config file') + debug('seeding a js/ts config file') - const code = /\.ts$/.test(file) ? tsCode : jsCode + const code = /\.ts$/.test(file) ? tsCode : jsCode - return fs.writeFile(file, code).then(() => ({ configObject: {}, functionNames: [] })) + return fs.writeFile(file, code).then(() => ({ configObject: {}, functionNames: [] })) + } + + handleNormalError(err) }) .then(({ result: configObject = {}, functionNames = [] }) => { const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' @@ -273,14 +286,7 @@ module.exports = { return config }) - }).catch((err) => { - debug('an error occured when reading config', err) - if (errors.isCypressErr(err)) { - throw err - } - - return this._logReadErr(file, err) - }) + }).catch(handleNormalError) }, readEnv (projectRoot) { From 3ddbccceea8403cac7e92314e1d967d1f4478a30 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 23 Jul 2021 10:51:23 -0500 Subject: [PATCH 093/204] add the path to the config file that we could not read --- packages/server/lib/errors.js | 6 ++++-- packages/server/lib/util/settings.js | 20 +++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 16700fa626c5..1bae5cec39c3 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -561,9 +561,11 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { Learn more at https://on.cypress.io/support-file-missing-or-invalid` case 'CONFIG_FILE_ERROR': msg = stripIndent`\ - Error when loading the config file. + Error when loading the config file at the following location: + + ${arg1} - You might have renamed the extension of your config file. If that's the case, restart the test runner.`.trim() + If you renamed the extension of your config file, restart the test runner.`.trim() if (arg2) { return { msg, details: arg2 } diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 25c1faa41cfc..cc72c3152ef3 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -206,15 +206,6 @@ module.exports = { const file = this.pathToConfigFile(projectRoot, options) - const handleNormalError = (err) => { - debug('an error occured when reading config', err) - if (errors.isCypressErr(err)) { - throw err - } - - return this._logReadErr(file, err) - } - return requireAsync(file, { projectRoot, @@ -238,7 +229,7 @@ module.exports = { return fs.writeFile(file, code).then(() => ({ configObject: {}, functionNames: [] })) } - handleNormalError(err) + return Promise.reject(err) }) .then(({ result: configObject = {}, functionNames = [] }) => { const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' @@ -286,7 +277,14 @@ module.exports = { return config }) - }).catch(handleNormalError) + }).catch((err) => { + debug('an error occured when reading config', err) + if (errors.isCypressErr(err)) { + throw err + } + + return this._logReadErr(file, err) + }) }, readEnv (projectRoot) { From a13395fb5b63e2aca476d90413b0b47d8bc8a283 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 23 Jul 2021 11:23:39 -0500 Subject: [PATCH 094/204] allow to scaffold js/ts config file --- packages/server/lib/util/settings.js | 48 ++++++++++++++++------------ 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index cc72c3152ef3..880982cbc771 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -7,19 +7,27 @@ const { fs } = require('./fs') const requireAsync = require('./require_async').default const debug = require('debug')('cypress:server:settings') -const jsCode = `const { defineConfig } = require('cypress') +function jsCode (obj) { + const objJSON = obj && obj !== {} ? JSON.stringify(obj, null, 2) : `{ -module.export = defineConfig({ +}` -}) + return `const { defineConfig } = require('cypress') + +module.export = defineConfig(${objJSON}) ` +} -const tsCode = `import { defineConfig } from 'cypress' +function tsCode (obj) { + const objJSON = obj && obj !== {} ? JSON.stringify(obj, null, 2) : `{ -export default defineConfig({ +}` -}) + return `import { defineConfig } from 'cypress' + +export default defineConfig(${objJSON}) ` +} // TODO: // think about adding another PSemaphore @@ -94,11 +102,21 @@ module.exports = { }, _write (file, obj = {}) { - if (!/\.json$/.test(file)) { - return Promise.resolve(obj) + if (/\.json$/.test(file)) { + debug('writing json file') + + return fs.outputJsonAsync(file, obj, { spaces: 2 }) + .return(obj) + .catch((err) => { + return this._logWriteErr(file, err) + }) } - return fs.outputJsonAsync(file, obj, { spaces: 2 }) + debug('writing javascript file') + + const code = /\.ts$/.test(file) ? tsCode : jsCode + + return fs.writeFileAsync(file, code(obj)) .return(obj) .catch((err) => { return this._logWriteErr(file, err) @@ -216,17 +234,7 @@ module.exports = { if (err.type === 'MODULE_NOT_FOUND' || err.code === 'ENOENT') { debug('file not found', file) - if (/\.json$/.test(file)) { - debug('seeding a json config file') - - return this._write(file, {}) - } - - debug('seeding a js/ts config file') - - const code = /\.ts$/.test(file) ? tsCode : jsCode - - return fs.writeFile(file, code).then(() => ({ configObject: {}, functionNames: [] })) + return this._write(file, {}) } return Promise.reject(err) From b3a837a549a6bca3b9b119b4cce206b443e93522 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 23 Jul 2021 11:24:47 -0500 Subject: [PATCH 095/204] test: fix one more cypress integration test --- packages/server/test/integration/cypress_spec.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/server/test/integration/cypress_spec.js b/packages/server/test/integration/cypress_spec.js index 0ca56a2d702b..a75125122aca 100644 --- a/packages/server/test/integration/cypress_spec.js +++ b/packages/server/test/integration/cypress_spec.js @@ -512,7 +512,9 @@ describe('lib/cypress', () => { return fs.statAsync(path.join(this.pristinePath, 'cypress', 'integration')) }).then(() => { throw new Error('integration folder should not exist!') - }).catch({ code: 'ENOENT' }, () => {}) + }).catch((e) => { + expect(e.code).to.equal('ENOENT') + }) }) it('scaffolds out fixtures + files if they do not exist', function () { @@ -768,7 +770,7 @@ describe('lib/cypress', () => { .then(() => { return cypress.start([`--run-project=${this.todosPath}`]) }).then(() => { - this.expectExitWithErr('ERROR_READING_FILE', this.todosPath) + this.expectExitWithErr('CONFIG_FILE_ERROR', this.todosPath) }) }) From 6850f021fee2938b1454106883587c52cbc335be Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 23 Jul 2021 12:15:04 -0500 Subject: [PATCH 096/204] test: setup config the right way (gui) --- packages/desktop-gui/cypress/integration/project_nav_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop-gui/cypress/integration/project_nav_spec.js b/packages/desktop-gui/cypress/integration/project_nav_spec.js index c96be0a943ee..7d362013b8ee 100644 --- a/packages/desktop-gui/cypress/integration/project_nav_spec.js +++ b/packages/desktop-gui/cypress/integration/project_nav_spec.js @@ -385,7 +385,7 @@ describe('Project Nav', function () { // 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) + this.openProject.resolve({ config: this.config }) }) it('defaults to first browser', () => { From 1a26d856327a9889ef756f9b27394c00023c5072 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 23 Jul 2021 18:21:59 -0500 Subject: [PATCH 097/204] feat: deal with functions in wrapIpc --- packages/server/lib/plugins/util.js | 52 +++++++++++++- .../server/test/unit/plugins/util_spec.js | 69 ++++++++++++++++++- 2 files changed, 117 insertions(+), 4 deletions(-) diff --git a/packages/server/lib/plugins/util.js b/packages/server/lib/plugins/util.js index eb78eaa57c7f..3d580d6da7fa 100644 --- a/packages/server/lib/plugins/util.js +++ b/packages/server/lib/plugins/util.js @@ -4,11 +4,46 @@ const debug = require('debug')('cypress:server:plugins') const Promise = require('bluebird') const UNDEFINED_SERIALIZED = '__cypress_undefined__' +const FUNCTION_SERIALIZED = '__cypress_function__' const serializeError = (err) => { return _.pick(err, 'name', 'message', 'stack', 'code', 'annotated', 'type') } +function serializeArgument (arg) { + if (typeof arg === 'function') { + return FUNCTION_SERIALIZED + } + + if (typeof arg === 'object') { + return Object.keys(arg).reduce(function (acc, key) { + acc[key] = serializeArgument(arg[key]) + + return acc + }, {}) + } + + return arg +} + +function deserializeArgument (arg) { + if (arg === FUNCTION_SERIALIZED) { + return function () { + throw Error('this function is not meant to be used on it\'s own') + } + } + + if (typeof arg === 'object') { + return Object.keys(arg).reduce(function (acc, key) { + acc[key] = deserializeArgument(arg[key]) + + return acc + }, {}) + } + + return arg +} + module.exports = { serializeError, @@ -24,19 +59,30 @@ module.exports = { emitter.setMaxListeners(Infinity) return { - send (event, ...args) { + send (event, ...rawArgs) { if (aProcess.killed) { return } + const args = rawArgs.map((arg) => serializeArgument(arg)) + return aProcess.send({ event, args, }) }, - on: emitter.on.bind(emitter), - removeListener: emitter.removeListener.bind(emitter), + on (event, handler) { + function wrappedHandler (...rawArgs) { + return handler(...rawArgs.map((arg) => deserializeArgument(arg))) + } + handler.__realHandlerFunction__ = wrappedHandler + emitter.on(event, wrappedHandler) + }, + + removeListener (event, handler) { + emitter.removeListener(event, handler.__realHandlerFunction__) + }, } }, diff --git a/packages/server/test/unit/plugins/util_spec.js b/packages/server/test/unit/plugins/util_spec.js index af64da26c552..693d95039e1a 100644 --- a/packages/server/test/unit/plugins/util_spec.js +++ b/packages/server/test/unit/plugins/util_spec.js @@ -43,7 +43,7 @@ describe('lib/plugins/util', () => { expect(handler).to.be.calledWith('arg1', 'arg2') }) - it('#removeListener emoves handler', function () { + it('#removeListener removes handler', function () { const handler = sinon.spy() this.ipc.on('event-name', handler) @@ -55,6 +55,73 @@ describe('lib/plugins/util', () => { expect(handler).not.to.be.called }) + + context('#arguments-serialization', () => { + it('#send should send functions arguments as a serialized string', function () { + this.ipc.send('event-singe-arg', function () {}) + + expect(this.theProcess.send).to.be.calledWith({ + event: 'event-singe-arg', + args: ['__cypress_function__'], + }) + }) + + it('#send should send functions in object arguments as a serialized string', function () { + this.ipc.send('event-obj-arg', { e2e () {} }) + + expect(this.theProcess.send).to.be.calledWith({ + event: 'event-obj-arg', + args: [{ e2e: '__cypress_function__' }], + }) + }) + + it('#send should send functions in nested object arguments as a serialized string', function () { + this.ipc.send('event-obj-arg', { config: { e2e () {} } }) + + expect(this.theProcess.send).to.be.calledWith({ + event: 'event-obj-arg', + args: [{ config: { e2e: '__cypress_function__' } }], + }) + }) + }) + + context('#arguments-deserialization', () => { + it('#on should deserialise function strings into function', function () { + const handler = sinon.spy() + + this.ipc.on('event-obj-arg', handler) + this.theProcess.on.yield({ + event: 'event-obj-arg', + args: ['__cypress_function__'], + }) + + expect(handler).to.be.calledWith(sinon.match.func) + }) + + it('#on should deserialise function strings in object into function', function () { + const handler = sinon.spy() + + this.ipc.on('event-obj-arg', handler) + this.theProcess.on.yield({ + event: 'event-obj-arg', + args: [{ e2e: '__cypress_function__' }], + }) + + expect(handler).to.be.calledWith({ e2e: sinon.match.func }) + }) + + it('#on should deserialise function strings in nested object into function', function () { + const handler = sinon.spy() + + this.ipc.on('event-obj-arg', handler) + this.theProcess.on.yield({ + event: 'event-obj-arg', + args: [{ config: { e2e: '__cypress_function__' } }], + }) + + expect(handler).to.be.calledWith({ config: { e2e: sinon.match.func } }) + }) + }) }) context('#wrapChildPromise', () => { From 1e1e97475621694acfbcd46cb7d7cad06dd7c0a3 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 23 Jul 2021 18:46:33 -0500 Subject: [PATCH 098/204] accept null values in wrapIpc --- packages/server/lib/plugins/util.js | 8 +++++++ .../server/test/unit/plugins/util_spec.js | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/packages/server/lib/plugins/util.js b/packages/server/lib/plugins/util.js index 3d580d6da7fa..4dc4688feb80 100644 --- a/packages/server/lib/plugins/util.js +++ b/packages/server/lib/plugins/util.js @@ -11,6 +11,10 @@ const serializeError = (err) => { } function serializeArgument (arg) { + if (arg === null || arg === undefined) { + return arg + } + if (typeof arg === 'function') { return FUNCTION_SERIALIZED } @@ -27,6 +31,10 @@ function serializeArgument (arg) { } function deserializeArgument (arg) { + if (arg === null || arg === undefined) { + return arg + } + if (arg === FUNCTION_SERIALIZED) { return function () { throw Error('this function is not meant to be used on it\'s own') diff --git a/packages/server/test/unit/plugins/util_spec.js b/packages/server/test/unit/plugins/util_spec.js index 693d95039e1a..6052af260ddf 100644 --- a/packages/server/test/unit/plugins/util_spec.js +++ b/packages/server/test/unit/plugins/util_spec.js @@ -83,6 +83,15 @@ describe('lib/plugins/util', () => { args: [{ config: { e2e: '__cypress_function__' } }], }) }) + + it('#send should not crash with null values', function () { + this.ipc.send('event-arg-null', { config: null }) + + expect(this.theProcess.send).to.be.calledWith({ + event: 'event-arg-null', + args: [{ config: null }], + }) + }) }) context('#arguments-deserialization', () => { @@ -121,6 +130,18 @@ describe('lib/plugins/util', () => { expect(handler).to.be.calledWith({ config: { e2e: sinon.match.func } }) }) + + it('#on should not crash with null values', function () { + const handler = sinon.spy() + + this.ipc.on('event-arg-null', handler) + this.theProcess.on.yield({ + event: 'event-arg-null', + args: [{ config: null }], + }) + + expect(handler).to.be.calledWith({ config: null }) + }) }) }) From e009586380bb507d5bc6ca3d5ca0a40615f05bf6 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 23 Jul 2021 20:08:24 -0500 Subject: [PATCH 099/204] remove need for function names in ipc --- packages/server/lib/util/require_async.ts | 7 +++--- .../server/lib/util/require_async_child.js | 17 ++----------- packages/server/lib/util/settings.js | 25 ++++--------------- packages/server/test/unit/settings_spec.js | 14 +++++------ 4 files changed, 17 insertions(+), 46 deletions(-) diff --git a/packages/server/lib/util/require_async.ts b/packages/server/lib/util/require_async.ts index 00885aaac9c5..5cf00300c772 100644 --- a/packages/server/lib/util/require_async.ts +++ b/packages/server/lib/util/require_async.ts @@ -60,10 +60,9 @@ export default async function requireAsync (filePath: string, options: RequireAs requireProcess.stderr.on('data', (data) => process.stderr.write(data)) } - ipc.on('loaded', ({ result, functionNames }) => { + ipc.on('loaded', (result) => { debug('resolving with result %o', result) - debug('resolving with functions %o', functionNames) - resolve({ result, functionNames }) + resolve(result) }) ipc.on('load:error', (type, ...args) => { @@ -74,6 +73,6 @@ export default async function requireAsync (filePath: string, options: RequireAs }) debug('trigger the load of the file') - ipc.send('load', options.functionNames) + ipc.send('load') }) } diff --git a/packages/server/lib/util/require_async_child.js b/packages/server/lib/util/require_async_child.js index 6f667f9dee9d..c9ad98be0a0c 100644 --- a/packages/server/lib/util/require_async_child.js +++ b/packages/server/lib/util/require_async_child.js @@ -50,27 +50,14 @@ function run (ipc, requiredFile, projectRoot) { return false }) - ipc.on('load', (functionNames) => { + ipc.on('load', () => { try { debug('try loading', requiredFile) const exp = require(requiredFile) const result = exp.default || exp - const functionsNamesOut = [] - - // Since functions cannot be transmitted through ipc - // register if they are indeed function to inform parent process - // that something is missing from the transmitted object - functionNames.forEach((name) => { - debug('check if %s is a function (%s)', name, typeof result[name]) - if (typeof result[name] === 'function') { - debug('%s is a function', name) - functionsNamesOut.push(name) - } - }) - - ipc.send('loaded', { result, functionNames: functionsNamesOut }) + ipc.send('loaded', result) debug('config %o', result) } catch (err) { diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 880982cbc771..3260b3808257 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -228,7 +228,6 @@ module.exports = { { projectRoot, loadErrorCode: 'CONFIG_FILE_ERROR', - functionNames: ['e2e', 'component'], }) .catch((err) => { if (err.type === 'MODULE_NOT_FOUND' || err.code === 'ENOENT') { @@ -239,7 +238,7 @@ module.exports = { return Promise.reject(err) }) - .then(({ result: configObject = {}, functionNames = [] }) => { + .then((configObject = {}) => { const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' debug('resolved configObject', configObject) @@ -250,37 +249,23 @@ module.exports = { } } - if (!/\.json$/.test(file)) { - // Tell the system we detected plugin functions - // for e2e or component that we cannot get this way. - // They will never be executed but will match the - // expected type instead of being not there. - functionNames.forEach((name) => { - configObject[name] = function () { - throw Error('This function whould always be executed within the plugins context') - } - }) - - configObject.configFile = path.relative(projectRoot, file) - - return configObject - } - const changed = this._applyRewriteRules(configObject) // if our object is unchanged // then just return it if (_.isEqual(configObject, changed)) { + configObject.configFile = path.relative(projectRoot, file) + return configObject } // else write the new reduced obj return this._write(file, changed) .then(function (config) { - // when json configfile is written, update the value of the configfile + // when configfile is written, update the value of the configfile // with the value found. // NOTE: it does not have to be cypress.json. - // it could be ./e2e/custom-config.json + // it could be ./e2e/custom-config.json or cypress.config.js config.configFile = path.relative(projectRoot, file) return config diff --git a/packages/server/test/unit/settings_spec.js b/packages/server/test/unit/settings_spec.js index 0824d409fb69..c395e0600563 100644 --- a/packages/server/test/unit/settings_spec.js +++ b/packages/server/test/unit/settings_spec.js @@ -181,7 +181,7 @@ describe('lib/settings', () => { .then(() => { return settings.read(projectRoot) }).then((obj) => { - expect(obj).to.deep.eq({ foo: 'bar' }) + expect(obj).to.deep.eq({ foo: 'bar', configFile: 'cypress.json' }) }) }) @@ -190,7 +190,7 @@ describe('lib/settings', () => { .then(() => { return settings.read(projectRoot, { testingType: 'component' }) }).then((obj) => { - expect(obj).to.deep.eq({ a: 'c', component: { a: 'c' } }) + expect(obj).to.deep.eq({ a: 'c', component: { a: 'c' }, configFile: 'cypress.json' }) }) }) @@ -199,7 +199,7 @@ describe('lib/settings', () => { .then(() => { return settings.read(projectRoot) }).then((obj) => { - expect(obj).to.deep.eq({ a: 'c', e2e: { a: 'c' } }) + expect(obj).to.deep.eq({ a: 'c', e2e: { a: 'c' }, configFile: 'cypress.json' }) }) }) @@ -245,7 +245,7 @@ describe('lib/settings', () => { return this.setup().then(() => { return settings.write(projectRoot, { foo: 'bar' }) }).then((obj) => { - expect(obj).to.deep.eq({ foo: 'bar' }) + expect(obj).to.deep.eq({ foo: 'bar', configFile: 'cypress.json' }) }) }) @@ -254,7 +254,7 @@ describe('lib/settings', () => { .then(() => { return settings.write(projectRoot, { projectId: 'abc123' }) }).then((obj) => { - expect(obj).to.deep.eq({ projectId: 'abc123', autoOpen: true }) + expect(obj).to.deep.eq({ projectId: 'abc123', autoOpen: true, configFile: 'cypress.json' }) }) }) }) @@ -321,7 +321,7 @@ describe('lib/settings', () => { .then(() => { return fs.readJsonAsync(path.join(this.projectRoot, this.options.configFile)) .then((json) => { - expect(json).to.deep.equal({ foo: 'bar' }) + expect(json).to.deep.equal({ foo: 'bar', configFile: 'my-test-config-file.json' }) }) }) }) @@ -331,7 +331,7 @@ describe('lib/settings', () => { .then(() => { return settings.read(this.projectRoot, this.options) .then((settings) => { - expect(settings).to.deep.equal({ foo: 'bar' }) + expect(settings).to.deep.equal({ foo: 'bar', configFile: 'my-test-config-file.json' }) }) }) }) From 83c86e6ac898228f851108c941617ba81da713f4 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 23 Jul 2021 20:30:11 -0500 Subject: [PATCH 100/204] allow array serialization --- packages/server/lib/plugins/util.js | 8 +++++++ .../server/test/unit/plugins/util_spec.js | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/packages/server/lib/plugins/util.js b/packages/server/lib/plugins/util.js index 4dc4688feb80..c6ff8598f3fb 100644 --- a/packages/server/lib/plugins/util.js +++ b/packages/server/lib/plugins/util.js @@ -20,6 +20,10 @@ function serializeArgument (arg) { } if (typeof arg === 'object') { + if (_.isArray(arg)) { + return arg.map((argElement) => serializeArgument(argElement)) + } + return Object.keys(arg).reduce(function (acc, key) { acc[key] = serializeArgument(arg[key]) @@ -42,6 +46,10 @@ function deserializeArgument (arg) { } if (typeof arg === 'object') { + if (_.isArray(arg)) { + return arg.map((argElement) => deserializeArgument(argElement)) + } + return Object.keys(arg).reduce(function (acc, key) { acc[key] = deserializeArgument(arg[key]) diff --git a/packages/server/test/unit/plugins/util_spec.js b/packages/server/test/unit/plugins/util_spec.js index 6052af260ddf..cb7b6f43a892 100644 --- a/packages/server/test/unit/plugins/util_spec.js +++ b/packages/server/test/unit/plugins/util_spec.js @@ -92,6 +92,15 @@ describe('lib/plugins/util', () => { args: [{ config: null }], }) }) + + it('#send should work with arrays', function () { + this.ipc.send('event-arg-array', { browsers: ['chrome', 'firefox'], handlers: [function () {}, function () {}] }) + + expect(this.theProcess.send).to.be.calledWith({ + event: 'event-arg-array', + args: [{ browsers: ['chrome', 'firefox'], handlers: ['__cypress_function__', '__cypress_function__'] }], + }) + }) }) context('#arguments-deserialization', () => { @@ -142,6 +151,18 @@ describe('lib/plugins/util', () => { expect(handler).to.be.calledWith({ config: null }) }) + + it('#on should work with arrays', function () { + const handler = sinon.spy() + + this.ipc.on('event-arg-array', handler) + this.theProcess.on.yield({ + event: 'event-arg-array', + args: [{ browsers: ['chrome', 'firefox'], handlers: ['__cypress_function__', '__cypress_function__'] }], + }) + + expect(handler).to.be.calledWith({ browsers: ['chrome', 'firefox'], handlers: [sinon.match.func, sinon.match.func] }) + }) }) }) From a5f38c8f3bbbd5b10a99980e8c12c4193d882212 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 23 Jul 2021 20:37:40 -0500 Subject: [PATCH 101/204] avoid useless changes to desktop gui --- .../cypress/integration/error_message_spec.js | 2 +- .../cypress/integration/global_mode_spec.js | 2 +- .../cypress/integration/login_spec.js | 2 +- .../cypress/integration/nav_spec.js | 12 ++--- .../cypress/integration/project_mode_spec.js | 2 +- .../cypress/integration/project_nav_spec.js | 24 +++++----- .../cypress/integration/project_spec.js | 2 +- .../cypress/integration/projects_list_spec.js | 2 +- .../cypress/integration/runs_list_spec.js | 36 +++++++------- .../cypress/integration/settings_spec.js | 48 +++++++++---------- .../cypress/integration/setup_project_spec.js | 2 +- .../cypress/integration/specs_list_spec.js | 44 ++++++++--------- .../integration/warning_message_spec.js | 2 +- packages/desktop-gui/cypress/support/index.js | 7 --- .../desktop-gui/src/projects/projects-api.js | 10 ++-- packages/server/lib/gui/events.js | 7 --- 16 files changed, 96 insertions(+), 108 deletions(-) diff --git a/packages/desktop-gui/cypress/integration/error_message_spec.js b/packages/desktop-gui/cypress/integration/error_message_spec.js index e11ac7360153..f0a21e48030d 100644 --- a/packages/desktop-gui/cypress/integration/error_message_spec.js +++ b/packages/desktop-gui/cypress/integration/error_message_spec.js @@ -35,7 +35,7 @@ describe('Error Message', function () { cy.stub(this.ipc, 'getOptions').resolves({ projectRoot: '/foo/bar' }) cy.stub(this.ipc, 'getCurrentUser').resolves(this.user) - cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) + cy.stub(this.ipc, 'openProject').resolves(this.config) cy.stub(this.ipc, 'closeProject').resolves() cy.stub(this.ipc, 'onConfigChanged') cy.stub(this.ipc, 'externalOpen') diff --git a/packages/desktop-gui/cypress/integration/global_mode_spec.js b/packages/desktop-gui/cypress/integration/global_mode_spec.js index 91f999958f23..6cb37962f937 100644 --- a/packages/desktop-gui/cypress/integration/global_mode_spec.js +++ b/packages/desktop-gui/cypress/integration/global_mode_spec.js @@ -28,7 +28,7 @@ describe('Global Mode', function () { cy.stub(this.ipc, 'updaterCheck').resolves(false) cy.stub(this.ipc, 'logOut').resolves({}) cy.stub(this.ipc, 'addProject').resolves(this.projects[0]) - cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) + cy.stub(this.ipc, 'openProject').resolves(this.config) cy.stub(this.ipc, 'getSpecs').yields(null, this.specs) cy.stub(this.ipc, 'offOpenProject') cy.stub(this.ipc, 'offGetSpecs') diff --git a/packages/desktop-gui/cypress/integration/login_spec.js b/packages/desktop-gui/cypress/integration/login_spec.js index 7e165ae36db2..5a9f25433628 100644 --- a/packages/desktop-gui/cypress/integration/login_spec.js +++ b/packages/desktop-gui/cypress/integration/login_spec.js @@ -15,7 +15,7 @@ describe('Login', function () { cy.stub(this.ipc, 'updaterCheck').resolves(false) cy.stub(this.ipc, 'getProjects').resolves([]) cy.stub(this.ipc, 'getProjectStatuses').resolves([]) - cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) + cy.stub(this.ipc, 'openProject').resolves(this.config) cy.stub(this.ipc, 'getSpecs').yields(null, this.specs) cy.stub(this.ipc, 'externalOpen') cy.stub(this.ipc, 'logOut').resolves() diff --git a/packages/desktop-gui/cypress/integration/nav_spec.js b/packages/desktop-gui/cypress/integration/nav_spec.js index 48b206438bbb..6dcaf8d85e2a 100644 --- a/packages/desktop-gui/cypress/integration/nav_spec.js +++ b/packages/desktop-gui/cypress/integration/nav_spec.js @@ -91,7 +91,7 @@ describe('Navigation', function () { }) it('closes off hover', function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) cy.get('.docs-menu').trigger('mouseover') cy.get('.docs-dropdown').should('be.visible') @@ -104,7 +104,7 @@ describe('Navigation', function () { describe('ci1', function () { context('opens on click', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) cy.get('.docs-menu').trigger('mouseover') cy.get('.docs-dropdown').should('be.visible') @@ -160,27 +160,27 @@ describe('Navigation', function () { }) it('opens when after 4 days from first open, no projectId, and not already shown', function () { - this.openProject.resolve({ config: { + this.openProject.resolve({ ...this.config, projectId: null, state: { ...this.config.state, promptsShown: {}, }, - } }) + }) cy.get('.prompt-ci1').should('be.visible') }) it('sends correct utm_content when opened', function () { - this.openProject.resolve({ config: { + this.openProject.resolve({ ...this.config, projectId: null, state: { ...this.config.state, promptsShown: {}, }, - } }) + }) cy.get('.see-other-guides').click() cy.wrap(this.ipc.externalOpen).should('have.been.calledWithMatch', { diff --git a/packages/desktop-gui/cypress/integration/project_mode_spec.js b/packages/desktop-gui/cypress/integration/project_mode_spec.js index 4aa92aa35f39..cdf17ab1a833 100644 --- a/packages/desktop-gui/cypress/integration/project_mode_spec.js +++ b/packages/desktop-gui/cypress/integration/project_mode_spec.js @@ -14,7 +14,7 @@ describe('Project Mode', function () { cy.stub(this.ipc, 'onFocusTests') cy.stub(this.ipc, 'getOptions').resolves({ projectRoot: '/foo/bar' }) cy.stub(this.ipc, 'updaterCheck').resolves(false) - cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) + cy.stub(this.ipc, 'openProject').resolves(this.config) cy.stub(this.ipc, 'getSpecs') this.getCurrentUser = this.util.deferred() diff --git a/packages/desktop-gui/cypress/integration/project_nav_spec.js b/packages/desktop-gui/cypress/integration/project_nav_spec.js index 7d362013b8ee..378ab6cece37 100644 --- a/packages/desktop-gui/cypress/integration/project_nav_spec.js +++ b/packages/desktop-gui/cypress/integration/project_nav_spec.js @@ -40,7 +40,7 @@ describe('Project Nav', function () { context('project nav', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('displays projects nav', function () { @@ -99,7 +99,7 @@ describe('Project Nav', function () { context('browsers dropdown', function () { describe('browsers available', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) context('normal browser list behavior', function () { @@ -338,7 +338,7 @@ describe('Project Nav', function () { channel: 'canary', })) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) cy.get('.browsers-list .dropdown-chosen') .should('contain', 'Canary').and('not.contain', 'Edge') @@ -349,7 +349,7 @@ describe('Project Nav', function () { it('displays chosen browser with old string-style id in localStorage', function () { localStorage.setItem('chosenBrowser', 'chrome') - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) cy.get('.browsers-list .dropdown-chosen') .should('contain', 'Chrome') @@ -358,7 +358,7 @@ describe('Project Nav', function () { it('displays default browser with null localStorage', function () { localStorage.removeItem('chosenBrowser') - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) cy.get('.browsers-list .dropdown-chosen') .should('contain', this.config.browsers[0].displayName) @@ -369,7 +369,7 @@ describe('Project Nav', function () { beforeEach(function () { localStorage.setItem('chosenBrowser', 'netscape-navigator') - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('defaults to first browser', () => { @@ -385,7 +385,7 @@ describe('Project Nav', function () { // 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({ config: this.config }) + this.openProject.resolve(this.config) }) it('defaults to first browser', () => { @@ -405,7 +405,7 @@ describe('Project Nav', function () { majorVersion: '50', }] - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('displays no dropdown btn', () => { @@ -430,7 +430,7 @@ describe('Project Nav', function () { }, ] - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('shows warning icon with linkified tooltip', function () { @@ -449,7 +449,7 @@ describe('Project Nav', function () { beforeEach(function () { this.config.browsers[this.config.browsers.length - 1].custom = true - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('displays generic icon', () => { @@ -484,7 +484,7 @@ describe('Project Nav', function () { info: this.info, }] - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('shows info icon with linkified tooltip', function () { @@ -500,7 +500,7 @@ describe('Project Nav', function () { context('issue #869 - nav responsiveness', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('main nav does not block project nav when long project name pushes it to multiple lines', () => { diff --git a/packages/desktop-gui/cypress/integration/project_spec.js b/packages/desktop-gui/cypress/integration/project_spec.js index ff84f7532f63..5a5fb428bd0a 100644 --- a/packages/desktop-gui/cypress/integration/project_spec.js +++ b/packages/desktop-gui/cypress/integration/project_spec.js @@ -10,7 +10,7 @@ describe('Project', function () { cy.stub(this.ipc, 'getOptions').resolves({ projectRoot: '/foo/bar' }) cy.stub(this.ipc, 'getCurrentUser').resolves(this.user) - cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) + cy.stub(this.ipc, 'openProject').resolves(this.config) cy.stub(this.ipc, 'getSpecs').yields(null, this.specs) cy.stub(this.ipc, 'closeProject').resolves() cy.stub(this.ipc, 'onConfigChanged') diff --git a/packages/desktop-gui/cypress/integration/projects_list_spec.js b/packages/desktop-gui/cypress/integration/projects_list_spec.js index cdd3607b8198..43caadc00213 100644 --- a/packages/desktop-gui/cypress/integration/projects_list_spec.js +++ b/packages/desktop-gui/cypress/integration/projects_list_spec.js @@ -28,7 +28,7 @@ describe('Projects List', function () { cy.stub(this.ipc, 'getCurrentUser').resolves(this.user) cy.stub(this.ipc, 'logOut').resolves({}) cy.stub(this.ipc, 'addProject').resolves(this.projects[0]) - cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) + cy.stub(this.ipc, 'openProject').resolves(this.config) cy.stub(this.ipc, 'getSpecs').yields(null, this.specs) cy.stub(this.ipc, 'removeProject') diff --git a/packages/desktop-gui/cypress/integration/runs_list_spec.js b/packages/desktop-gui/cypress/integration/runs_list_spec.js index 19f9da5951c1..a90f7a1fcc84 100644 --- a/packages/desktop-gui/cypress/integration/runs_list_spec.js +++ b/packages/desktop-gui/cypress/integration/runs_list_spec.js @@ -59,7 +59,7 @@ describe('Runs List', function () { context('page display', function () { beforeEach(function () { this.getCurrentUser.resolve(this.user) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.getRuns.resolve(this.runs) this.goToRuns() @@ -77,7 +77,7 @@ describe('Runs List', function () { cy.clock(timestamp) this.getCurrentUser.resolve(this.user) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.getRuns.resolve(this.runs) this.goToRuns() @@ -249,7 +249,7 @@ describe('Runs List', function () { beforeEach(function () { this.getCurrentUser.resolve(this.user) this.config.projectId = this.projects[0].id - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.pingApiServer.resolve() const timestamp = new Date(2016, 11, 19, 10, 0, 0).valueOf() @@ -299,7 +299,7 @@ describe('Runs List', function () { context('without a current user', function () { beforeEach(function () { this.getCurrentUser.resolve(null) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.pingApiServer.resolve() this.goToRuns() @@ -325,7 +325,7 @@ describe('Runs List', function () { beforeEach(function () { this.getCurrentUser.resolve(this.user) this.config.projectId = undefined - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.pingApiServer.resolve() this.goToRuns() @@ -341,7 +341,7 @@ describe('Runs List', function () { beforeEach(function () { this.getCurrentUser.resolve(null) this.config.projectId = undefined - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.pingApiServer.resolve() this.goToRuns() @@ -368,7 +368,7 @@ describe('Runs List', function () { describe('polling runs', function () { beforeEach(function () { this.getCurrentUser.resolve(this.user) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.pingApiServer.resolve() this.getRunsAgain = this.util.deferred() this.ipc.getRuns.onCall(1).returns(this.getRunsAgain.promise) @@ -459,7 +459,7 @@ describe('Runs List', function () { describe('manually refreshing runs', function () { beforeEach(function () { this.getCurrentUser.resolve(this.user) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.pingApiServer.resolve() this.getRunsAgain = this.util.deferred() this.ipc.getRuns.onCall(1).returns(this.getRunsAgain.promise) @@ -510,7 +510,7 @@ describe('Runs List', function () { describe('permissions error', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.goToRuns() }) @@ -674,7 +674,7 @@ describe('Runs List', function () { describe('timed out error', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.goToRuns().then(() => { this.getRuns.reject({ name: 'foo', message: 'There\'s an error', type: 'TIMED_OUT' }) @@ -689,7 +689,7 @@ describe('Runs List', function () { describe('not found error', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.goToRuns().then(() => { this.getRuns.reject({ name: 'foo', message: 'There\'s an error', type: 'NOT_FOUND' }) @@ -704,7 +704,7 @@ describe('Runs List', function () { describe('unauthenticated error', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.goToRuns().then(() => { this.getRuns.reject({ name: '', message: '', statusCode: 401 }) @@ -722,7 +722,7 @@ describe('Runs List', function () { describe('no project id error', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.ipc.setupDashboardProject.resolves(this.validCiProject) this.ipc.getRuns.onCall(1).resolves([]) @@ -753,7 +753,7 @@ describe('Runs List', function () { describe('unexpected error', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.goToRuns().then(() => { this.getRuns.reject({ name: 'foo', message: `{"no runs": "for you"}`, type: 'UNKNOWN' }) @@ -769,7 +769,7 @@ describe('Runs List', function () { describe('unauthorized project', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.getProjectStatus.resolve({ state: 'UNAUTHORIZED', }) @@ -784,7 +784,7 @@ describe('Runs List', function () { describe('invalid project', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.ipc.setupDashboardProject.resolves(this.validCiProject) this.getProjectStatus.resolve({ @@ -821,7 +821,7 @@ describe('Runs List', function () { context('having never setup CI', function () { beforeEach(function () { this.config.projectId = null - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.goToRuns().then(() => { this.getRuns.resolve([]) @@ -845,7 +845,7 @@ describe('Runs List', function () { context('having previously set up CI', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.goToRuns().then(() => { this.getRuns.resolve([]) diff --git a/packages/desktop-gui/cypress/integration/settings_spec.js b/packages/desktop-gui/cypress/integration/settings_spec.js index ddac865a40bf..6891dbea6812 100644 --- a/packages/desktop-gui/cypress/integration/settings_spec.js +++ b/packages/desktop-gui/cypress/integration/settings_spec.js @@ -52,7 +52,7 @@ describe('Settings', () => { describe('general functionality', () => { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -99,7 +99,7 @@ describe('Settings', () => { describe('configuration panel', () => { describe('displays config', () => { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -225,7 +225,7 @@ describe('Settings', () => { this.ipc.onConfigChanged.yield() cy.contains('.line', 'env:null').then(() => { - this.ipc.openProject.resolves({ config: this.config }) + this.ipc.openProject.resolves(this.config) this.ipc.onConfigChanged.yield() cy.contains('.line', 'env:fileServerFolder') @@ -233,7 +233,7 @@ describe('Settings', () => { this.ipc.openProject.resolves(setConfigEnv(this.config, null)) this.ipc.onConfigChanged.yield() cy.contains('.line', 'env:null').then(() => { - this.ipc.openProject.resolves({ config: this.config }) + this.ipc.openProject.resolves(this.config) this.ipc.onConfigChanged.yield() cy.contains('.line', 'env:fileServerFolder') @@ -306,7 +306,7 @@ describe('Settings', () => { newConfig.clientUrl = 'http://localhost:8888' newConfig.clientUrlDisplay = 'http://localhost:8888' newConfig.browsers = this.browsers - this.openProject.resolve({ config: newConfig }) + this.openProject.resolve(newConfig) this.goToSettings() @@ -317,7 +317,7 @@ describe('Settings', () => { const newConfig = this.util.deepClone(this.config) newConfig.resolved.baseUrl.value = 'http://localhost:7777' - this.ipc.openProject.onCall(1).resolves({ config: newConfig }) + this.ipc.openProject.onCall(1).resolves(newConfig) this.ipc.onConfigChanged.yield() cy.contains('http://localhost:7777') @@ -326,9 +326,9 @@ describe('Settings', () => { context('when configFile is false', () => { beforeEach(function () { - this.openProject.resolve({ config: Cypress._.assign({ + this.openProject.resolve(Cypress._.assign({ configFile: false, - }, this.config) }) + }, this.config)) this.goToSettings() @@ -342,9 +342,9 @@ describe('Settings', () => { context('when configFile is set', function () { beforeEach(function () { - this.openProject.resolve({ config: Cypress._.assign({ + this.openProject.resolve(Cypress._.assign({ configFile: 'special-cypress.json', - }, this.config) }) + }, this.config)) this.goToSettings() @@ -359,7 +359,7 @@ describe('Settings', () => { describe('project id panel', () => { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -393,7 +393,7 @@ describe('Settings', () => { describe('record key panel', () => { context('when project is set up and you have access', () => { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -523,7 +523,7 @@ describe('Settings', () => { }) it('does not show ci Keys section when project is invalid', function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.projectStatuses[0].state = 'INVALID' this.getProjectStatus.resolve(this.projectStatuses[0]) this.goToSettings() @@ -535,7 +535,7 @@ describe('Settings', () => { context('when you are not a user of this projects org', () => { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('does not show record key', function () { @@ -555,7 +555,7 @@ describe('Settings', () => { beforeEach(function () { this.navigateWithConfig = function (config) { - this.openProject.resolve({ config: _.defaults(config, this.config) }) + this.openProject.resolve(_.defaults(config, this.config)) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) this.goToSettings() @@ -590,7 +590,7 @@ describe('Settings', () => { describe('proxy settings panel', () => { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.config.resolved.baseUrl.value = 'http://localhost:7777' this.projectStatuses[0].id = this.config.projectId @@ -654,7 +654,7 @@ describe('Settings', () => { describe('no experimental features turned on', () => { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -674,7 +674,7 @@ describe('Settings', () => { value: true, } - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -708,7 +708,7 @@ describe('Settings', () => { value: true, } - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -736,7 +736,7 @@ describe('Settings', () => { from: 'default', } - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -770,7 +770,7 @@ describe('Settings', () => { cy.stub(this.ipc, 'getUserEditor').returns(this.getUserEditor.promise) cy.stub(this.ipc, 'setUserEditor').resolves() - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) @@ -847,7 +847,7 @@ describe('Settings', () => { this.projectStatuses[0].id = this.config.projectId this.getProjectStatus.resolve(this.projectStatuses[0]) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.goToSettings() openConfiguration() @@ -877,8 +877,8 @@ describe('Settings', () => { // -- function setConfigEnv (config, v) { - return { config: flow([ + return flow([ merge(config), set('resolved.env', v), - ])({}) } + ])({}) } diff --git a/packages/desktop-gui/cypress/integration/setup_project_spec.js b/packages/desktop-gui/cypress/integration/setup_project_spec.js index d624eb78de3b..130513e72abd 100644 --- a/packages/desktop-gui/cypress/integration/setup_project_spec.js +++ b/packages/desktop-gui/cypress/integration/setup_project_spec.js @@ -108,7 +108,7 @@ describe('Connect to Dashboard', function () { cy.stub(this.ipc, 'updaterCheck').resolves(false) cy.stub(this.ipc, 'closeBrowser').resolves(null) this.config.projectId = null - cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) + cy.stub(this.ipc, 'openProject').resolves(this.config) cy.stub(this.ipc, 'getSpecs').yields(null, this.specs) cy.stub(this.ipc, 'getRuns').resolves([]) cy.stub(this.ipc, 'getRecordKeys').resolves(this.keys) diff --git a/packages/desktop-gui/cypress/integration/specs_list_spec.js b/packages/desktop-gui/cypress/integration/specs_list_spec.js index 7261d826753c..212157271ba9 100644 --- a/packages/desktop-gui/cypress/integration/specs_list_spec.js +++ b/packages/desktop-gui/cypress/integration/specs_list_spec.js @@ -41,7 +41,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, []) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('displays empty message', () => { @@ -80,7 +80,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.componentSpecs) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('shows both types of specs', () => { @@ -103,7 +103,7 @@ describe('Specs List', function () { beforeEach(function () { this.config.isNewProject = true - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) context('banner', function () { @@ -145,7 +145,7 @@ describe('Specs List', function () { describe('first time user in existing project', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) this.ipc.hasOpenedCypress.resolves(false) }) @@ -179,7 +179,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specsWindows) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) context('displays list of specs', function () { @@ -212,7 +212,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) context('run all specs', function () { @@ -360,7 +360,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('collapsing root spec will keep root itself expanded', function () { @@ -439,7 +439,7 @@ describe('Specs List', function () { unit: [], }) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('hides expand/collapse buttons when there are no folders', function () { @@ -451,7 +451,7 @@ describe('Specs List', function () { context('filtering specs', function () { it('scrolls the specs and not the filter', function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) cy.contains('last_list_spec').scrollIntoView() cy.get('.filter').should('be.visible') @@ -470,7 +470,7 @@ describe('Specs List', function () { this.numSpecs = 16 this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) cy.contains('.all-tests', runAllIntegrationSpecsLabel) cy.get('.filter').type('new') @@ -616,7 +616,7 @@ describe('Specs List', function () { }) it('applies it for the appropriate project', function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) cy.get('.filter').should('have.value', 'app') cy.contains('.all-tests', 'Run 1 integration spec') @@ -624,7 +624,7 @@ describe('Specs List', function () { it('does not apply it for a different project', function () { this.config.projectId = 'different' - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) cy.get('.filter').should('have.value', '') }) @@ -637,7 +637,7 @@ describe('Specs List', function () { }) it('saves the filter to local storage', function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) cy.get('.filter').type('my-filter') cy.window().then((win) => { @@ -650,7 +650,7 @@ describe('Specs List', function () { it('applies the saved filter when returning to the project', function () { cy.window().then(function (win) { win.localStorage[`specsFilter--/foo/bar`] = JSON.stringify('my-filter') - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) cy.get('.filter').should('have.value', 'my-filter') @@ -661,7 +661,7 @@ describe('Specs List', function () { context('click on spec', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) cy.contains('.file .file-name-wrapper', 'app_spec.coffee').as('firstSpec') }) @@ -708,7 +708,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) context('choose shallow spec', function () { @@ -744,7 +744,7 @@ describe('Specs List', function () { context('switching specs', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) cy.get('.file').contains('a', 'app_spec.coffee').as('firstSpec') .click() @@ -767,7 +767,7 @@ describe('Specs List', function () { context('with component tests', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('shows separate run specs buttons', function () { @@ -818,7 +818,7 @@ describe('Specs List', function () { context('returning to specs tab', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) // https://github.com/cypress-io/cypress/issues/9151 @@ -845,7 +845,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('updates spec list selected on specChanged', function () { @@ -872,7 +872,7 @@ describe('Specs List', function () { beforeEach(function () { this.ipc.getSpecs.yields(null, this.specs) - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) cy.get('.file').contains('a', 'app_spec.coffee').parent().as('spec') cy.get('@spec').contains('Open in IDE').as('button') @@ -1026,7 +1026,7 @@ describe('Specs List', function () { describe('new spec file', function () { beforeEach(function () { - this.openProject.resolve({ config: this.config }) + this.openProject.resolve(this.config) }) it('launches system save dialog', function () { diff --git a/packages/desktop-gui/cypress/integration/warning_message_spec.js b/packages/desktop-gui/cypress/integration/warning_message_spec.js index ab333b1f9a4c..e322983da51e 100644 --- a/packages/desktop-gui/cypress/integration/warning_message_spec.js +++ b/packages/desktop-gui/cypress/integration/warning_message_spec.js @@ -12,7 +12,7 @@ describe('Warning Message', function () { cy.stub(this.ipc, 'getOptions').resolves({ projectRoot: '/foo/bar' }) cy.stub(this.ipc, 'getCurrentUser').resolves(this.user) - cy.stub(this.ipc, 'openProject').resolves({ config: this.config }) + cy.stub(this.ipc, 'openProject').resolves(this.config) cy.stub(this.ipc, 'getSpecs').yields(null, this.specs) cy.stub(this.ipc, 'closeProject').resolves() cy.stub(this.ipc, 'onConfigChanged') diff --git a/packages/desktop-gui/cypress/support/index.js b/packages/desktop-gui/cypress/support/index.js index 92feb213406e..9bc56cde2438 100644 --- a/packages/desktop-gui/cypress/support/index.js +++ b/packages/desktop-gui/cypress/support/index.js @@ -9,13 +9,6 @@ require('cypress-real-events/support') const BluebirdPromise = require('bluebird') beforeEach(function () { - this.config = { - testFiles: '**/*.spec.js', - resolved: { - testFiles: '**/*.spec.js', - }, - } - this.util = { deferred (Promise = BluebirdPromise) { const deferred = {} diff --git a/packages/desktop-gui/src/projects/projects-api.js b/packages/desktop-gui/src/projects/projects-api.js index 69e959d8dde5..4028d366832d 100644 --- a/packages/desktop-gui/src/projects/projects-api.js +++ b/packages/desktop-gui/src/projects/projects-api.js @@ -156,7 +156,7 @@ const openProject = (project) => { }) } - const updateConfig = (config, functions) => { + const updateConfig = (config, hasE2Eproperty) => { project.update({ id: config.projectId, name: config.projectName, @@ -167,7 +167,7 @@ const openProject = (project) => { project.setOnBoardingConfig(config) project.setBrowsers(config.browsers) project.setResolvedConfig(config.resolved) - project.setE2EFunction(functions.indexOf('e2e') !== -1) + project.setE2EFunction(hasE2Eproperty) project.prompts.setPromptStates(config) } @@ -192,7 +192,9 @@ const openProject = (project) => { }) return ipc.openProject(project.path) - .then(({ config = {}, functions = [] } = {}) => { + .then((config = {}) => { + const hasE2Eproperty = !!config.e2e + // In this context we know we are in e2e. // The configuration in e2e has already been merged with the main. // It is not useful to display component/e2e fields explicitely. @@ -205,7 +207,7 @@ const openProject = (project) => { config = _.omit(config, 'e2e', 'component') - updateConfig(config, functions) + updateConfig(config, hasE2Eproperty) const projectIdAndPath = { id: config.projectId, path: project.path } specsStore.setFilter(projectIdAndPath, localData.get(specsStore.getSpecsFilterId(projectIdAndPath))) diff --git a/packages/server/lib/gui/events.js b/packages/server/lib/gui/events.js index 6034bf6781b6..7cb23d66752a 100644 --- a/packages/server/lib/gui/events.js +++ b/packages/server/lib/gui/events.js @@ -325,13 +325,6 @@ const handleEvent = function (options, bus, event, id, type, arg) { }).then((project) => { return project.getConfig() }) - .then((cfg) => { - const functions = ['e2e', 'component'].filter((func) => { - return typeof cfg[func] === 'function' - }) - - return { config: cfg, functions } - }) .then(send) .catch(sendErr) From 7f2c9a50b85797cefc6766a8a3d291b25698b9d1 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 23 Jul 2021 21:00:43 -0500 Subject: [PATCH 102/204] move function in testing type --- cli/types/cypress.d.ts | 4 +-- npm/vue/cypress.config.ts | 36 +++++++++---------- .../server/lib/plugins/child/run_plugins.js | 13 ++++++- packages/server/lib/plugins/index.js | 6 ++-- packages/server/lib/util/settings.js | 6 ++-- 5 files changed, 38 insertions(+), 27 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 083bcf9963d1..a761264fa111 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2717,13 +2717,13 @@ declare namespace Cypress { * Override default config options for Component Testing runner. * @default {} */ - component: Omit | TestingTypeFunctions + component: Omit & { plugins?: TestingTypeFunctions } /** * Override default config options for E2E Testing runner. * @default {} */ - e2e: Omit | TestingTypeFunctions + e2e: Omit & { plugins?: TestingTypeFunctions } } type TestingTypeFunctions = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptions | undefined) diff --git a/npm/vue/cypress.config.ts b/npm/vue/cypress.config.ts index 3588788d2e8e..a792e0798ea7 100644 --- a/npm/vue/cypress.config.ts +++ b/npm/vue/cypress.config.ts @@ -10,28 +10,28 @@ export default defineConfig({ projectId: '134ej7', testFiles: '**/*spec.js', experimentalFetchPolyfill: true, - component: (on, config) => { - if (config.testingType !== 'component') { - throw Error(`This is a component testing project. testingType should be 'component'. Received ${config.testingType}`) - } + component: { + plugins (on, config) { + if (config.testingType !== 'component') { + throw Error(`This is a component testing project. testingType should be 'component'. Received ${config.testingType}`) + } - if (!webpackConfig.resolve) { - webpackConfig.resolve = {} - } + if (!webpackConfig.resolve) { + webpackConfig.resolve = {} + } - webpackConfig.resolve.alias = { - ...webpackConfig.resolve.alias, - '@vue/compiler-core$': '@vue/compiler-core/dist/compiler-core.cjs.js', - } + webpackConfig.resolve.alias = { + ...webpackConfig.resolve.alias, + '@vue/compiler-core$': '@vue/compiler-core/dist/compiler-core.cjs.js', + } - require('@cypress/code-coverage/task')(on, config) - on('dev-server:start', (options) => startDevServer({ options, webpackConfig })) + require('@cypress/code-coverage/task')(on, config) + on('dev-server:start', (options) => startDevServer({ options, webpackConfig })) - return config + return config + }, }, - e2e (_, config) { - config.includeShadowDom = true - - return config + e2e: { + includeShadowDom: true, }, }) diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js index 0b5d3812ac0f..c8996805ba3f 100644 --- a/packages/server/lib/plugins/child/run_plugins.js +++ b/packages/server/lib/plugins/child/run_plugins.js @@ -144,7 +144,18 @@ function getPluginsFunction (pluginsFile, functionName) { const exp = require(pluginsFile) const resolvedExport = exp && exp.default ? exp.default : exp - return functionName ? resolvedExport[functionName] : resolvedExport + if (functionName) { + const functionNameArray = functionName.split('.') + let finalFunction = resolvedExport + + while (functionNameArray.length) { + finalFunction = finalFunction[functionNameArray.shift()] + } + + return finalFunction + } + + return resolvedExport } const runPlugins = (ipc, pluginsFile, projectRoot, functionName) => { diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index c731b8732cb8..564ce47384af 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -13,6 +13,8 @@ let pluginsProcess = null let registeredEvents = {} let handlers = [] +const PLUGINS_FUNCTION_NAME = 'plugins' + const register = (event, callback) => { debug(`register event '${event}'`) @@ -92,9 +94,9 @@ const init = (config, options) => { const testingType = options.testingType || 'e2e' - if (typeof config[testingType] === 'function') { + if (typeof config[testingType][PLUGINS_FUNCTION_NAME] === 'function') { childArguments.push( - '--functionName', testingType, + '--functionName', `${testingType}.${PLUGINS_FUNCTION_NAME}`, '--file', options.configFile, ) } else { diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 3260b3808257..dc3344a69acf 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -243,10 +243,8 @@ module.exports = { debug('resolved configObject', configObject) - if ((testingType in configObject)) { - if (typeof configObject[testingType] === 'object') { - configObject = { ...configObject, ...configObject[testingType] } - } + if (testingType in configObject && typeof configObject[testingType] === 'object') { + configObject = { ...configObject, ...configObject[testingType] } } const changed = this._applyRewriteRules(configObject) From 9db9e1656bb5122fdc0909277342e5d2ef9121f8 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 23 Jul 2021 21:08:46 -0500 Subject: [PATCH 103/204] protect against noe e2e object --- packages/server/lib/plugins/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index 564ce47384af..35931149cb1a 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -94,7 +94,7 @@ const init = (config, options) => { const testingType = options.testingType || 'e2e' - if (typeof config[testingType][PLUGINS_FUNCTION_NAME] === 'function') { + if (config[testingType] && typeof config[testingType][PLUGINS_FUNCTION_NAME] === 'function') { childArguments.push( '--functionName', `${testingType}.${PLUGINS_FUNCTION_NAME}`, '--file', options.configFile, From 94446050d0e93703f4ae7990374a436407726d31 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 23 Jul 2021 21:12:05 -0500 Subject: [PATCH 104/204] test adjust e2e test for function --- .../plugin-config-js/cypress.config.js | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js b/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js index ff739f4d7009..5502d6982e88 100644 --- a/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js +++ b/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js @@ -1,15 +1,17 @@ module.exports = { - e2e: (on, config) => { - return new Promise((resolve) => { - setTimeout(resolve, 100) - }) - .then(() => { - config.defaultCommandTimeout = 500 - config.videoCompression = 20 - config.env = config.env || {} - config.env.foo = 'bar' + e2e: { + plugins (on, config) { + return new Promise((resolve) => { + setTimeout(resolve, 100) + }) + .then(() => { + config.defaultCommandTimeout = 500 + config.videoCompression = 20 + config.env = config.env || {} + config.env.foo = 'bar' - return config - }) + return config + }) + }, }, } From 633e4f73c843cb8ad92250034e602b02a3d58a48 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 23 Jul 2021 22:10:45 -0500 Subject: [PATCH 105/204] fix browser options API --- packages/server/lib/browsers/utils.ts | 8 ++++++++ packages/server/lib/plugins/util.js | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/server/lib/browsers/utils.ts b/packages/server/lib/browsers/utils.ts index 90eb0044ea61..53df13b023d7 100644 --- a/packages/server/lib/browsers/utils.ts +++ b/packages/server/lib/browsers/utils.ts @@ -149,6 +149,14 @@ function extendLaunchOptionsFromPlugins (launchOptions, pluginConfigResult, opti extensions: [], }) } else { + // First, remove all dummy functions created on the plugins thread, + // they don't need to be checked + Object.keys(pluginConfigResult).forEach((key) => { + if (typeof pluginConfigResult[key] === 'function') { + delete pluginConfigResult[key] + } + }) + // either warn about the array or potentially error on invalid props, but not both // strip out all the known launch option properties from the resulting object diff --git a/packages/server/lib/plugins/util.js b/packages/server/lib/plugins/util.js index c6ff8598f3fb..471ec6f16c68 100644 --- a/packages/server/lib/plugins/util.js +++ b/packages/server/lib/plugins/util.js @@ -12,7 +12,7 @@ const serializeError = (err) => { function serializeArgument (arg) { if (arg === null || arg === undefined) { - return arg + return null } if (typeof arg === 'function') { @@ -36,12 +36,12 @@ function serializeArgument (arg) { function deserializeArgument (arg) { if (arg === null || arg === undefined) { - return arg + return null } if (arg === FUNCTION_SERIALIZED) { return function () { - throw Error('this function is not meant to be used on it\'s own') + throw Error('this function is not meant to be used on it\'s own. It is the result of a deserialization and can be used to check the type of a returned object.') } } From c12c633b4dbf90ec400504ceb15446bd489bf566 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 24 Jul 2021 11:30:21 -0500 Subject: [PATCH 106/204] allow null and subfunctions in clean up for browser event --- packages/server/lib/browsers/utils.ts | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/server/lib/browsers/utils.ts b/packages/server/lib/browsers/utils.ts index 53df13b023d7..ade9466bc551 100644 --- a/packages/server/lib/browsers/utils.ts +++ b/packages/server/lib/browsers/utils.ts @@ -132,6 +132,24 @@ async function executeBeforeBrowserLaunch (browser, launchOptions: typeof defaul return launchOptions } +function removeFunctionsInObject (pluginConfigResult) { + if (pluginConfigResult) { + Object.keys(pluginConfigResult).forEach((key) => { + if (typeof pluginConfigResult[key] === 'function') { + delete pluginConfigResult[key] + } else if (typeof pluginConfigResult[key] === 'object') { + if (Array.isArray(pluginConfigResult[key])) { + pluginConfigResult[key] = pluginConfigResult[key].filter((obj) => typeof obj !== 'function') + } else { + removeFunctionsInObject(pluginConfigResult[key]) + } + } + }) + } + + return pluginConfigResult +} + function extendLaunchOptionsFromPlugins (launchOptions, pluginConfigResult, options) { // if we returned an array from the plugin // then we know the user is using the deprecated @@ -151,11 +169,7 @@ function extendLaunchOptionsFromPlugins (launchOptions, pluginConfigResult, opti } else { // First, remove all dummy functions created on the plugins thread, // they don't need to be checked - Object.keys(pluginConfigResult).forEach((key) => { - if (typeof pluginConfigResult[key] === 'function') { - delete pluginConfigResult[key] - } - }) + removeFunctionsInObject(pluginConfigResult) // either warn about the array or potentially error on invalid props, but not both From 821bef6e7923e61ca3863778e09164b6fd4ab25d Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 24 Jul 2021 12:14:08 -0500 Subject: [PATCH 107/204] fix server unit test --- packages/server/test/unit/gui/events_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/test/unit/gui/events_spec.js b/packages/server/test/unit/gui/events_spec.js index 6bd9550ac758..6d8a80e2dece 100644 --- a/packages/server/test/unit/gui/events_spec.js +++ b/packages/server/test/unit/gui/events_spec.js @@ -675,7 +675,7 @@ describe('lib/gui/events', () => { .then((assert) => { expect(this.send.firstCall.args[0]).to.eq('response') // [1].id).to.match(/setup:dashboard:project-/) expect(this.send.firstCall.args[1].id).to.match(/open:project-/) - expect(this.send.firstCall.args[1].data).to.eql({ config: { some: 'config' }, functions: [] }) + expect(this.send.firstCall.args[1].data).to.eql({ some: 'config' }) }) }) From 17f26c85df99f436adca1a22a5286fc1a16e4a49 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 24 Jul 2021 14:07:43 -0500 Subject: [PATCH 108/204] no creating the pluginsFile when converted to js --- packages/server/lib/project-base.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 69d79593c2bf..d499bfee15c0 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -182,11 +182,13 @@ export class ProjectBase extends EE { process.chdir(this.projectRoot) + const testingType = this.projectType === 'ct' ? 'component' : 'e2e' + // TODO: we currently always scaffold the plugins file // even when headlessly or else it will cause an error when // we try to load it and it's not there. We must do this here // else initialing the plugins will instantly fail. - if (cfg.pluginsFile) { + if (cfg.pluginsFile && !cfg[testingType]?.plugins) { debug('scaffolding with plugins file %s', cfg.pluginsFile) await scaffold.plugins(path.dirname(cfg.pluginsFile), cfg) From d43d473bffcc8cc461141a55381e3225c3407977 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 26 Jul 2021 16:56:03 -0500 Subject: [PATCH 109/204] fix: merge issue --- packages/server/lib/project-base.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 7b75ef91e0ea..e41b508182e8 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -182,13 +182,11 @@ export class ProjectBase extends EE { process.chdir(this.projectRoot) - const testingType = this.projectType === 'ct' ? 'component' : 'e2e' - // TODO: we currently always scaffold the plugins file // even when headlessly or else it will cause an error when // we try to load it and it's not there. We must do this here // else initialing the plugins will instantly fail. - if (cfg.pluginsFile && !cfg[testingType]?.plugins) { + if (cfg.pluginsFile && !cfg[this.testingType]?.plugins) { debug('scaffolding with plugins file %s', cfg.pluginsFile) await scaffold.plugins(path.dirname(cfg.pluginsFile), cfg) From 38e76acf70d4a9e3432c5f79a9cb02e41eae8d19 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 29 Jul 2021 16:46:10 -0500 Subject: [PATCH 110/204] update types --- cli/types/cypress.d.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index f87292fdd051..1582f199a997 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2467,6 +2467,10 @@ declare namespace Cypress { cmdKey: boolean } + type PluginsFunction = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptions | undefined) + + type TestingTypeConfig = Omit & { plugins?: PluginsFunction } + interface ResolvedConfigOptions { /** * Url used as prefix for [cy.visit()](https://on.cypress.io/visit) or [cy.request()](https://on.cypress.io/request) command’s url @@ -2714,20 +2718,18 @@ declare namespace Cypress { experimentalFetchPolyfill: boolean /** - * Override default config options for Component Testing runner. + * Override default config options for E2E Testing runner. * @default {} */ - component: Omit & { plugins?: TestingTypeFunctions } + e2e: TestingTypeConfig /** - * Override default config options for E2E Testing runner. + * Override default config options for Component Testing runner. * @default {} */ - e2e: Omit & { plugins?: TestingTypeFunctions } + component: TestingTypeConfig } - type TestingTypeFunctions = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptions | undefined) - /** * Options appended to config object on runtime. */ From b1b2b8ecf608fcb2042ec0423cd174588705cd28 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 29 Jul 2021 16:47:25 -0500 Subject: [PATCH 111/204] fix text typo "in the" instead of "if the" --- packages/desktop-gui/src/settings/configuration.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop-gui/src/settings/configuration.jsx b/packages/desktop-gui/src/settings/configuration.jsx index 158fe4ce2b0a..d5e2211dc88f 100644 --- a/packages/desktop-gui/src/settings/configuration.jsx +++ b/packages/desktop-gui/src/settings/configuration.jsx @@ -184,7 +184,7 @@ const Configuration = observer(({ project }) => ( {project.hasE2EFunction ? <> function - set in the e2e function if the {configFileFormatted(project.configFile)} + set in the e2e function in the {configFileFormatted(project.configFile)} : <> From 4c73fc1f80aa89bf5c8e5a93e79315fa4c8d5063 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 29 Jul 2021 16:48:40 -0500 Subject: [PATCH 112/204] remove return statement since we mutated passed object --- packages/server/lib/browsers/utils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/server/lib/browsers/utils.ts b/packages/server/lib/browsers/utils.ts index ade9466bc551..f5e7411f833f 100644 --- a/packages/server/lib/browsers/utils.ts +++ b/packages/server/lib/browsers/utils.ts @@ -146,8 +146,6 @@ function removeFunctionsInObject (pluginConfigResult) { } }) } - - return pluginConfigResult } function extendLaunchOptionsFromPlugins (launchOptions, pluginConfigResult, options) { From 5ec842e7eee1d7f950aa8323cd8cabf638860bef Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 29 Jul 2021 17:00:49 -0500 Subject: [PATCH 113/204] make the getPluginsFunction function easier to read --- packages/server/lib/plugins/child/run_plugins.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js index c8996805ba3f..1afcbec8f23e 100644 --- a/packages/server/lib/plugins/child/run_plugins.js +++ b/packages/server/lib/plugins/child/run_plugins.js @@ -140,16 +140,25 @@ const execute = (ipc, event, ids, args = []) => { let tsRegistered = false +/** + * the plugins function can be either at the top level + * in a pluginsFile or in a non-dedicated cypress.config.js file + * This functions normalizes where the function is and runs it. + * + * @param {string} pluginsFile absolute path of the targeted file conatining the plugins function + * @param {string} functionName path to the function in the exported object like `"e2e.plugins"` + * @returns the plugins function found + */ function getPluginsFunction (pluginsFile, functionName) { const exp = require(pluginsFile) const resolvedExport = exp && exp.default ? exp.default : exp if (functionName) { - const functionNameArray = functionName.split('.') + // follow the object path given in parameter to the function sought let finalFunction = resolvedExport - while (functionNameArray.length) { - finalFunction = finalFunction[functionNameArray.shift()] + for (const part of functionName.split('.')) { + finalFunction = finalFunction[part] } return finalFunction From 00c799d5f03467ce535043e459acd72c6d89ebab Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 29 Jul 2021 17:07:08 -0500 Subject: [PATCH 114/204] clearer serialization of objects --- packages/server/lib/plugins/util.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/server/lib/plugins/util.js b/packages/server/lib/plugins/util.js index 471ec6f16c68..0b84df127ada 100644 --- a/packages/server/lib/plugins/util.js +++ b/packages/server/lib/plugins/util.js @@ -21,14 +21,16 @@ function serializeArgument (arg) { if (typeof arg === 'object') { if (_.isArray(arg)) { - return arg.map((argElement) => serializeArgument(argElement)) + return arg.map(serializeArgument) } - return Object.keys(arg).reduce(function (acc, key) { - acc[key] = serializeArgument(arg[key]) + const serializedObject = {} - return acc - }, {}) + for (const [key, val] of Object.entries(arg)) { + serializedObject[key] = serializeArgument(val) + } + + return serializedObject } return arg From 878035d1d5cbbded3ebbed1c88cffe4e49f2b81e Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 29 Jul 2021 17:17:19 -0500 Subject: [PATCH 115/204] make a list of accepted default config files --- packages/server/lib/configFiles.ts | 1 + packages/server/lib/util/settings.js | 23 ++++++----------------- 2 files changed, 7 insertions(+), 17 deletions(-) create mode 100644 packages/server/lib/configFiles.ts diff --git a/packages/server/lib/configFiles.ts b/packages/server/lib/configFiles.ts new file mode 100644 index 000000000000..56556205b345 --- /dev/null +++ b/packages/server/lib/configFiles.ts @@ -0,0 +1 @@ +export const CYPRESS_CONFIG_FILES = ['cypress.config.js', 'cypress.config.ts', 'cypress.json'] diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index dc3344a69acf..66d788ec68bd 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -5,6 +5,7 @@ const errors = require('../errors') const log = require('../log') const { fs } = require('./fs') const requireAsync = require('./require_async').default +const { CYPRESS_CONFIG_FILES } = require('../configFiles') const debug = require('debug')('cypress:server:settings') function jsCode (obj) { @@ -146,26 +147,14 @@ module.exports = { return options.configFile } - if (ls.includes('cypress.config.ts')) { - return 'cypress.config.ts' - } - - if (ls.includes('cypress.config.js')) { - return 'cypress.config.js' - } - - if (ls.includes('cypress.json')) { - return 'cypress.json' - } - - // if we find a tsconfig.json let's make users life easy - // and create a TypeScript file. - if (ls.includes('tsconfig.json')) { - return 'cypress.config.ts' + for (const configFile of CYPRESS_CONFIG_FILES) { + if (ls.includes(configFile)) { + return configFile + } } // Default is to create a new `cypress.config.js` file if one does not exist. - return 'cypress.config.js' + return CYPRESS_CONFIG_FILES[0] }, id (projectRoot, options = {}) { From a8db11281f9235bcdd20cb491b7f99373b1fe908 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 29 Jul 2021 17:23:43 -0500 Subject: [PATCH 116/204] avoid repeating the pattern of finding config file --- packages/server/lib/saved_state.js | 51 ++++++++++-------------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/packages/server/lib/saved_state.js b/packages/server/lib/saved_state.js index cce88e639045..77689d078a09 100644 --- a/packages/server/lib/saved_state.js +++ b/packages/server/lib/saved_state.js @@ -2,6 +2,7 @@ const _ = require('lodash') const debug = require('debug')('cypress:server:saved_state') const path = require('path') const Promise = require('bluebird') +const { CYPRESS_CONFIG_FILES } = require('./configFiles') const appData = require('./util/app_data') const cwd = require('./cwd') const FileUtil = require('./util/file') @@ -49,43 +50,23 @@ const formStatePath = (projectRoot) => { debug('missing project path, looking for project here') - const cypressConfigTsPath = cwd('cypress.config.ts') - - return fs.pathExistsAsync(cypressConfigTsPath) - .then((foundTs) => { - if (foundTs) { - debug('found cypress file %s', cypressConfigTsPath) - projectRoot = cwd() - } else { - const cypressConfigJsPath = cwd('cypress.config.js') - - // if not found with js, try with json - return fs.pathExistsAsync(cypressConfigJsPath) - .then((foundJs) => { - if (foundJs) { - debug('found cypress file %s', cypressConfigJsPath) - projectRoot = cwd() - } else { - const cypressJsonPath = cwd('cypress.json') - - // if not found with js, try with json - return fs.pathExistsAsync(cypressJsonPath) - .then((foundJson) => { - if (foundJson) { - debug('found cypress file %s', cypressJsonPath) - projectRoot = cwd() - } - - return projectRoot - }) - } - + return CYPRESS_CONFIG_FILES.reduce((acc, filename) => { + return acc.then((projectRoot) => { + if (projectRoot) { return projectRoot - }) - } + } - return projectRoot - }) + const cypressConfigPath = cwd(filename) + + return fs.pathExistsAsync(cypressConfigPath).then((found) => { + if (found) { + debug('found cypress file %s', cypressConfigPath) + + return cwd() + } + }) + }) + }, Promise.resolve()) }).then((projectRoot) => { const fileName = 'state.json' From 27260813afa7dab1b0a8d0cdb1dc9937fd9c7dac Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 30 Jul 2021 09:52:20 -0500 Subject: [PATCH 117/204] update comment in javascript function --- cli/lib/cypress.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cli/lib/cypress.js b/cli/lib/cypress.js index d5278ac85fe3..1d6e560b8aa2 100644 --- a/cli/lib/cypress.js +++ b/cli/lib/cypress.js @@ -69,6 +69,15 @@ const cypressModuleApi = { }, }, + /** + * This function should not be used in pure JavaScript + * By the type of its argument it allows autocomplettion and + * type chekcing of the configuration given by users. + * + * @see ../types/cypress-npm-api.d.ts + * @param {Cypress.ConfigOptions} config + * @returns {Cypress.ConfigOptions} the configuration passed in parameter + */ defineConfig (config) { return config }, From 0dd3649ab1d8a773ac460eef2d8b2618b01f4ea0 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 30 Jul 2021 09:53:31 -0500 Subject: [PATCH 118/204] fix isEmpy object --- packages/server/lib/util/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 66d788ec68bd..6cbf9aa3bd45 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -9,7 +9,7 @@ const { CYPRESS_CONFIG_FILES } = require('../configFiles') const debug = require('debug')('cypress:server:settings') function jsCode (obj) { - const objJSON = obj && obj !== {} ? JSON.stringify(obj, null, 2) : `{ + const objJSON = obj && !_.isEmpty(obj) ? JSON.stringify(obj, null, 2) : `{ }` From 147a1a93058ef6f28589d0b0ee1799ea94966a77 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 30 Jul 2021 10:14:17 -0500 Subject: [PATCH 119/204] lint: remove white-space (!!!%%^&^%) --- cli/types/cypress.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 174b3649d7a2..8383f010e614 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2727,7 +2727,7 @@ declare namespace Cypress { * Override default config options for Component Testing runner. * @default {} */ - component: TestingTypeConfig + component: TestingTypeConfig } /** From 601f0d797a050bdff45859d54665bcbef909a706 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 30 Jul 2021 13:35:14 -0500 Subject: [PATCH 120/204] refactor: merge e2e in config instead of settings for validation --- packages/server/lib/config.js | 14 +++- packages/server/lib/util/settings.js | 10 --- packages/server/test/unit/config_spec.js | 42 ++++++++++++ packages/server/test/unit/settings_spec.js | 75 +--------------------- 4 files changed, 55 insertions(+), 86 deletions(-) diff --git a/packages/server/lib/config.js b/packages/server/lib/config.js index 96c77003f4c2..582daf49ed8d 100644 --- a/packages/server/lib/config.js +++ b/packages/server/lib/config.js @@ -180,6 +180,10 @@ const utils = { }, } +function isComponentTesting (options = {}) { + return options.testingType === 'component' +} + module.exports = { utils, @@ -207,11 +211,17 @@ module.exports = { settings.read(projectRoot, options).then(validateFile(configFilename)), settings.readEnv(projectRoot).then(validateFile('cypress.env.json')), ]) - .spread((settings, envFile) => { + .spread((configObject, envFile) => { + const testingType = isComponentTesting(options) ? 'component' : 'e2e' + + if (testingType in configObject) { + configObject = { ...configObject, ...configObject[testingType] } + } + return this.set({ projectName: this.getNameFromRoot(projectRoot), projectRoot, - config: _.cloneDeep(settings), + config: _.cloneDeep(configObject), envFile: _.cloneDeep(envFile), options, }) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 6cbf9aa3bd45..d4db78466348 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -132,10 +132,6 @@ module.exports = { }, _.cloneDeep(obj)) }, - isComponentTesting (options = {}) { - return options.testingType === 'component' - }, - configFile (projectRoot, options = {}) { const ls = fs.readdirSync(projectRoot) @@ -228,14 +224,8 @@ module.exports = { return Promise.reject(err) }) .then((configObject = {}) => { - const testingType = this.isComponentTesting(options) ? 'component' : 'e2e' - debug('resolved configObject', configObject) - if (testingType in configObject && typeof configObject[testingType] === 'object') { - configObject = { ...configObject, ...configObject[testingType] } - } - const changed = this._applyRewriteRules(configObject) // if our object is unchanged diff --git a/packages/server/test/unit/config_spec.js b/packages/server/test/unit/config_spec.js index 4e4412dbea1b..bd40ac83480c 100644 --- a/packages/server/test/unit/config_spec.js +++ b/packages/server/test/unit/config_spec.js @@ -258,6 +258,20 @@ describe('lib/config', () => { return this.expectValidationFails('the value was: `false`') }) + + it('merges component specific config in the root object', function () { + this.setup({ + viewportWidth: 1024, + component: { + viewportWidth: 300, + }, + }) + + return config.get(this.projectRoot, { testingType: 'component' }) + .then((obj) => { + expect(obj.viewportWidth).to.eq(300) + }) + }) }) context('e2e', () => { @@ -290,6 +304,34 @@ describe('lib/config', () => { return this.expectValidationFails('the value was: `false`') }) + + it('merges e2e specific config in the root object', function () { + this.setup({ + viewportWidth: 300, + e2e: { + viewportWidth: 1024, + }, + }) + + return config.get(this.projectRoot, { testingType: 'e2e' }) + .then((obj) => { + expect(obj.viewportWidth).to.eq(1024) + }) + }) + + it('default to e2e config if none is specified', function () { + this.setup({ + viewportWidth: 300, + e2e: { + viewportWidth: 1024, + }, + }) + + return config.get(this.projectRoot) + .then((obj) => { + expect(obj.viewportWidth).to.eq(1024) + }) + }) }) context('defaultCommandTimeout', () => { diff --git a/packages/server/test/unit/settings_spec.js b/packages/server/test/unit/settings_spec.js index c395e0600563..b5cf12b2d21e 100644 --- a/packages/server/test/unit/settings_spec.js +++ b/packages/server/test/unit/settings_spec.js @@ -104,13 +104,10 @@ describe('lib/settings', () => { }) context('.read', () => { - it('promises cypress.config.js -> `component` key for component testing runner', function () { + it('promises cypress.config.js', function () { return this.setup(` module.exports = { commmon_setting: true, - component: { - component_setting: 'peep' - } } `, 'cypress.config.js') @@ -119,58 +116,6 @@ describe('lib/settings', () => { }).then((obj) => { expect(obj).to.deep.eq({ commmon_setting: true, - component_setting: 'peep', - component: { - component_setting: 'peep', - }, - configFile: 'cypress.config.js', - }) - }) - }) - - it('promises cypress.config.js -> `e2e` key for component testing runner', function () { - return this.setup(` - module.exports = { - commmon_setting: true, - e2e: { - e2e_setting: 'e2e_setting' - } - } - `, - 'cypress.config.js') - .then(() => { - return settings.read(projectRoot, { testingType: 'e2e' }) - }).then((obj) => { - expect(obj).to.deep.eq({ - commmon_setting: true, - e2e_setting: 'e2e_setting', - e2e: { - e2e_setting: 'e2e_setting', - }, - configFile: 'cypress.config.js', - }) - }) - }) - - it('promises cypress.config.js assumes e2e if no runner specific keys are configured', function () { - return this.setup(` - module.exports = { - commmon_setting: true, - e2e: { - e2e_setting: 'e2e_setting', - }, - } - `, - 'cypress.config.js') - .then(() => { - return settings.read(projectRoot, {}) - }).then((obj) => { - expect(obj).to.deep.eq({ - commmon_setting: true, - e2e_setting: 'e2e_setting', - e2e: { - e2e_setting: 'e2e_setting', - }, configFile: 'cypress.config.js', }) }) @@ -185,24 +130,6 @@ describe('lib/settings', () => { }) }) - it('promises cypress.json and merges CT specific properties for via testingType: component', function () { - return this.setup({ a: 'b', component: { a: 'c' } }) - .then(() => { - return settings.read(projectRoot, { testingType: 'component' }) - }).then((obj) => { - expect(obj).to.deep.eq({ a: 'c', component: { a: 'c' }, configFile: 'cypress.json' }) - }) - }) - - it('promises cypress.json and merges e2e specific properties', function () { - return this.setup({ a: 'b', e2e: { a: 'c' } }) - .then(() => { - return settings.read(projectRoot) - }).then((obj) => { - expect(obj).to.deep.eq({ a: 'c', e2e: { a: 'c' }, configFile: 'cypress.json' }) - }) - }) - it('renames commandTimeout -> defaultCommandTimeout', function () { return this.setup({ commandTimeout: 30000, foo: 'bar' }) .then(() => { From 48239ee81005325597abcaaecdb055e37922b34b Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 30 Jul 2021 14:15:39 -0500 Subject: [PATCH 121/204] rename plugins to setupNodeServer for finding --- cli/types/cypress.d.ts | 2 +- packages/server/lib/plugins/index.js | 2 +- .../fixtures/projects/plugin-config-js/cypress.config.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 8383f010e614..5616f9799431 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2469,7 +2469,7 @@ declare namespace Cypress { type PluginsFunction = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptions | undefined) - type TestingTypeConfig = Omit & { plugins?: PluginsFunction } + type TestingTypeConfig = Omit & { setupNodeServer?: PluginsFunction } interface ResolvedConfigOptions { /** diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index 35931149cb1a..7b6d5048538a 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -13,7 +13,7 @@ let pluginsProcess = null let registeredEvents = {} let handlers = [] -const PLUGINS_FUNCTION_NAME = 'plugins' +const PLUGINS_FUNCTION_NAME = 'setupNodeServer' const register = (event, callback) => { debug(`register event '${event}'`) diff --git a/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js b/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js index 5502d6982e88..baf106d1fd5c 100644 --- a/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js +++ b/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js @@ -1,6 +1,6 @@ module.exports = { e2e: { - plugins (on, config) { + setupNodeServer (on, config) { return new Promise((resolve) => { setTimeout(resolve, 100) }) From 322e350541b94ada2888eb9d2f0301c25d1d29b4 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 30 Jul 2021 14:17:00 -0500 Subject: [PATCH 122/204] validate setupnodeserver config --- packages/server/lib/config.js | 5 +++++ packages/server/lib/config_options.ts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/packages/server/lib/config.js b/packages/server/lib/config.js index 582daf49ed8d..261b96d33090 100644 --- a/packages/server/lib/config.js +++ b/packages/server/lib/config.js @@ -44,6 +44,7 @@ const breakingKeys = _.map(breakingOptions, 'name') const folders = _(options).filter({ isFolder: true }).map('name').value() const validationRules = createIndex(options, 'name', 'validation') const defaultValues = createIndex(options, 'name', 'defaultValue') +const onlyInOverrideValues = createIndex(options, 'name', 'onlyInOverride') const isCypressEnvLike = (key) => { return _.chain(key) @@ -86,6 +87,10 @@ const validate = (cfg, onErr) => { return _.each(cfg, (value, key) => { const validationFn = validationRules[key] + if (onlyInOverrideValues[key]) { + return `key ${key} is only valid in a testingType object, it is invalid to use it in the root` + } + // does this key have a validation rule? if (validationFn) { // and is the value different from the default? diff --git a/packages/server/lib/config_options.ts b/packages/server/lib/config_options.ts index a3cc9e5c0872..3549842b8b20 100644 --- a/packages/server/lib/config_options.ts +++ b/packages/server/lib/config_options.ts @@ -165,6 +165,11 @@ export const options = [ defaultValue: 'cypress/plugins', validation: v.isStringOrFalse, isFolder: true, + }, { + name: 'setupNodeServer', + defaultvalue: null, + validation: v.isFunction, + onlyInOverride: true, }, { name: 'port', defaultValue: null, From 97655d18b6dd3bce421c95793180c1c281f7ad99 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 30 Jul 2021 14:49:35 -0500 Subject: [PATCH 123/204] error when using pluginsFile in cypress.config.jsts --- packages/server/lib/config.js | 9 +++++++-- packages/server/lib/errors.js | 8 ++++++++ packages/server/lib/plugins/index.js | 2 +- packages/server/test/unit/config_spec.js | 17 +++++++++++------ 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/packages/server/lib/config.js b/packages/server/lib/config.js index 261b96d33090..ddda23ebc555 100644 --- a/packages/server/lib/config.js +++ b/packages/server/lib/config.js @@ -106,8 +106,13 @@ const validate = (cfg, onErr) => { } const validateFile = (file) => { - return (settings) => { - return validate(settings, (errMsg) => { + return (configObject) => { + // disallow use of pluginFile in evaluated configuration files + if (/\.(ts|js)$/.test(file) && configObject.pluginsFile) { + errors.throw('CONFLICT_PLUGINSFILE_CONFIGJS', file) + } + + return validate(configObject, (errMsg) => { return errors.throw('SETTINGS_VALIDATION_ERROR', file, errMsg) }) } diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 1bae5cec39c3..2ce4935311df 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -1000,6 +1000,14 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { https://on.cypress.io/component-testing ` + case 'CONFLICT_PLUGINSFILE_CONFIGJS': + return stripIndent` + \`pluginsFile\` cannot be set in a \`${arg1}\` file. + + \`pluginsFile\` is deprecated and will error in cypress 9.0, prefer using the \`setupNodeServer\` function instead. + + https://on.cypress.io/setupNodeServer + ` case 'UNSUPPORTED_BROWSER_VERSION': return arg1 default: diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index 7b6d5048538a..3391614e150c 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -94,7 +94,7 @@ const init = (config, options) => { const testingType = options.testingType || 'e2e' - if (config[testingType] && typeof config[testingType][PLUGINS_FUNCTION_NAME] === 'function') { + if (typeof config[PLUGINS_FUNCTION_NAME] === 'function') { childArguments.push( '--functionName', `${testingType}.${PLUGINS_FUNCTION_NAME}`, '--file', options.configFile, diff --git a/packages/server/test/unit/config_spec.js b/packages/server/test/unit/config_spec.js index bd40ac83480c..f6d9e62d4e7f 100644 --- a/packages/server/test/unit/config_spec.js +++ b/packages/server/test/unit/config_spec.js @@ -7,7 +7,6 @@ const debug = require('debug')('test') const config = require(`${root}lib/config`) const errors = require(`${root}lib/errors`) const configUtil = require(`${root}lib/util/config`) -const { fs } = require(`${root}lib/util/fs`) const findSystemNode = require(`${root}lib/util/find_system_node`) const scaffold = require(`${root}lib/scaffold`) let settings = require(`${root}lib/util/settings`) @@ -55,8 +54,8 @@ describe('lib/config', () => { beforeEach(function () { this.projectRoot = '/_test-output/path/to/project' - this.setup = (cypressJson = {}, cypressEnvJson = {}) => { - sinon.stub(fs, 'readdirSync').withArgs(this.projectRoot).returns([]) + this.setup = (cypressJson = {}, cypressEnvJson = {}, fileName = 'cypress.config.js') => { + sinon.stub(settings, 'configFile').withArgs(this.projectRoot).returns(fileName) sinon.stub(settings, 'read').withArgs(this.projectRoot).resolves(cypressJson) sinon.stub(settings, 'readEnv').withArgs(this.projectRoot).resolves(cypressEnvJson) } @@ -525,22 +524,28 @@ describe('lib/config', () => { context('pluginsFile', () => { it('passes if a string', function () { - this.setup({ pluginsFile: 'cypress/plugins' }) + this.setup({ pluginsFile: 'cypress/plugins' }, {}, 'cypress.json') return this.expectValidationPasses() }) it('passes if false', function () { - this.setup({ pluginsFile: false }) + this.setup({ pluginsFile: false }, {}, 'cypress.json') return this.expectValidationPasses() }) it('fails if not a string or false', function () { - this.setup({ pluginsFile: 42 }) + this.setup({ pluginsFile: 42 }, {}, 'cypress.json') return this.expectValidationFails('be a string') }) + + it('fails if used in a js file', function () { + this.setup({ pluginsFile: 'cypress/plugins' }, {}, 'cypress.config.js') + + return this.expectValidationFails('cannot be set in a `cypress.config.js`') + }) }) context('port', () => { From d4b1dfbb12e5e6999d8f88475739a7d7fb9f2eec Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 30 Jul 2021 15:56:51 -0500 Subject: [PATCH 124/204] fix: error when both a cypress.config.js and .ts --- packages/server/lib/errors.js | 9 ++++++ packages/server/lib/util/settings.js | 33 +++++++++++++++++----- packages/server/test/unit/settings_spec.js | 18 ++++++++++++ 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 2ce4935311df..ae6076d3bf76 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -1008,6 +1008,15 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { https://on.cypress.io/setupNodeServer ` + + case `CONFIG_FILES_LANGUAGE_CONFLICT`: + return stripIndent` + There is both a \`cypress.config.js\` and a \`cypress.config.ts\` in the location below: + + ${arg1} + + Cypress does not know which one to read for config. Please remove one of the two and try again. + ` case 'UNSUPPORTED_BROWSER_VERSION': return arg1 default: diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index d4db78466348..9e507970ed0d 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -9,7 +9,9 @@ const { CYPRESS_CONFIG_FILES } = require('../configFiles') const debug = require('debug')('cypress:server:settings') function jsCode (obj) { - const objJSON = obj && !_.isEmpty(obj) ? JSON.stringify(obj, null, 2) : `{ + const objJSON = obj && !_.isEmpty(obj) + ? JSON.stringify(_.omit(obj, 'configFile'), null, 2) + : `{ }` @@ -20,7 +22,9 @@ module.export = defineConfig(${objJSON}) } function tsCode (obj) { - const objJSON = obj && obj !== {} ? JSON.stringify(obj, null, 2) : `{ + const objJSON = obj && !_.isEmpty(obj) !== {} + ? JSON.stringify(_.omit(obj, 'configFile'), null, 2) + : `{ }` @@ -133,8 +137,6 @@ module.exports = { }, configFile (projectRoot, options = {}) { - const ls = fs.readdirSync(projectRoot) - if (options.configFile === false) { return false } @@ -143,10 +145,27 @@ module.exports = { return options.configFile } - for (const configFile of CYPRESS_CONFIG_FILES) { - if (ls.includes(configFile)) { - return configFile + const ls = fs.readdirSync(projectRoot) + + const foundConfigFiles = CYPRESS_CONFIG_FILES.filter((file) => ls.includes(file)) + + // if we only found one default file, it is the one + if (foundConfigFiles.length === 1) { + return foundConfigFiles[0] + } + + // if we found more than one, it might just be the json that is still migrating + if (foundConfigFiles.length > 1) { + const evaluatedFiles = foundConfigFiles.filter((file) => file !== 'cypress.json') + + // if we found more than one evaluated file (js or ts) + // we can't trust that migration has been doen properly + if (evaluatedFiles.length > 1) { + return errors.throw('CONFIG_FILES_LANGUAGE_CONFLICT', projectRoot) } + + // else, just ignore the cypress.json and return the other one + return evaluatedFiles[0] } // Default is to create a new `cypress.config.js` file if one does not exist. diff --git a/packages/server/test/unit/settings_spec.js b/packages/server/test/unit/settings_spec.js index b5cf12b2d21e..32e7d20873bd 100644 --- a/packages/server/test/unit/settings_spec.js +++ b/packages/server/test/unit/settings_spec.js @@ -22,6 +22,7 @@ describe('lib/settings', () => { afterEach(() => { return fs.removeAsync('cypress.json') .then(() => fs.removeAsync('cypress.config.js')) + .then(() => fs.removeAsync('cypress.config.ts')) .then(clearCypressJsonCache) }) @@ -104,6 +105,23 @@ describe('lib/settings', () => { }) context('.read', () => { + it('fails if both a cypress.config.js and cypress.config.ts are added', function () { + return Promise.all([ + fs.outputFileAsync('cypress.config.ts', ''), + fs.outputFileAsync('cypress.config.js', ''), + ]).then(() => { + return settings.read(projectRoot) + }) + .then(() => { + throw Error('should throw wehn 2 files are created') + }) + .catch((err) => { + expect(err.type).to.eq('CONFIG_FILES_LANGUAGE_CONFLICT') + + expect(err.message).to.include(projectRoot) + }) + }) + it('promises cypress.config.js', function () { return this.setup(` module.exports = { From b8a1aee723f310af4817180211f5d1a18f9d6aea Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 30 Jul 2021 16:03:13 -0500 Subject: [PATCH 125/204] fix types and test it out on cypress vue --- cli/types/cypress.d.ts | 6 +++--- npm/vue/cypress.config.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 5616f9799431..74bb2e988b6f 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -302,7 +302,7 @@ declare namespace Cypress { // 60000 ``` */ - config(key: K): ResolvedConfigOptions[K] + config(key: K): ResolvedConfigOptions[K] /** * Sets one configuration value. * @see https://on.cypress.io/config @@ -311,7 +311,7 @@ declare namespace Cypress { Cypress.config('viewportWidth', 800) ``` */ - config(key: K, value: ResolvedConfigOptions[K]): void + config(key: K, value: ResolvedConfigOptions[K]): void /** * Sets multiple configuration values at once. * @see https://on.cypress.io/config @@ -2813,7 +2813,7 @@ declare namespace Cypress { /** * All configuration items are optional. */ - type CoreConfigOptions = Partial> + type CoreConfigOptions = Partial type ConfigOptions = CoreConfigOptions & {e2e?: CoreConfigOptions, component?: CoreConfigOptions } interface PluginConfigOptions extends ResolvedConfigOptions { diff --git a/npm/vue/cypress.config.ts b/npm/vue/cypress.config.ts index a792e0798ea7..ddc734a6505d 100644 --- a/npm/vue/cypress.config.ts +++ b/npm/vue/cypress.config.ts @@ -8,10 +8,10 @@ export default defineConfig({ video: false, responseTimeout: 2500, projectId: '134ej7', - testFiles: '**/*spec.js', experimentalFetchPolyfill: true, component: { - plugins (on, config) { + testFiles: '**/*spec.js', + setupNodeServer (on, config) { if (config.testingType !== 'component') { throw Error(`This is a component testing project. testingType should be 'component'. Received ${config.testingType}`) } From b6108eac1a8620f3c23bfbd898e850551ea18ddf Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 30 Jul 2021 16:16:46 -0500 Subject: [PATCH 126/204] fix: comment on the location of the plugins function --- packages/desktop-gui/src/settings/configuration.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop-gui/src/settings/configuration.jsx b/packages/desktop-gui/src/settings/configuration.jsx index d5e2211dc88f..f1de2d1b3130 100644 --- a/packages/desktop-gui/src/settings/configuration.jsx +++ b/packages/desktop-gui/src/settings/configuration.jsx @@ -184,7 +184,7 @@ const Configuration = observer(({ project }) => ( {project.hasE2EFunction ? <> function - set in the e2e function in the {configFileFormatted(project.configFile)} + set in the e2e.setupNodeServer() function in the {configFileFormatted(project.configFile)} : <> From 59c33bcb250e6ab3df94bd3dc944823e338860d8 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 30 Jul 2021 16:29:14 -0500 Subject: [PATCH 127/204] fix: update watcher to watch dependencies of config --- packages/server/lib/project-base.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 36c7726ec9b7..3101fbfb4996 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -535,7 +535,7 @@ export class ProjectBase extends EE { } if (configFile !== false) { - this.watchers.watch(settings.pathToConfigFile(projectRoot, { configFile }), obj) + this.watchers.watchTree(settings.pathToConfigFile(projectRoot, { configFile }), obj) } return this.watchers.watch(settings.pathToCypressEnvJson(projectRoot), obj) From 2b8086c2b147948b3d881ed9c592c89f46aa8bb9 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 31 Jul 2021 11:33:33 -0500 Subject: [PATCH 128/204] add the setupDevServer function --- cli/types/cypress.d.ts | 13 ++++-- npm/vue/cypress.config.ts | 14 +++--- npm/vue/cypress/plugins/index.js | 22 +++++++++ packages/server/lib/plugins/child/index.js | 4 +- .../server/lib/plugins/child/run_plugins.js | 45 ++++++++++++------- packages/server/lib/plugins/index.js | 3 +- 6 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 npm/vue/cypress/plugins/index.js diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 74bb2e988b6f..fa245b5516c6 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2470,6 +2470,13 @@ declare namespace Cypress { type PluginsFunction = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptions | undefined) type TestingTypeConfig = Omit & { setupNodeServer?: PluginsFunction } + type TestingTypeConfigComponent = TestingTypeConfig & { + /** + * Return the setup of your server + * @arg options the dev server options to pass directly to the dev-server + */ + setupDevServer(options: DevServerOptions): Promise + } interface ResolvedConfigOptions { /** @@ -2727,7 +2734,7 @@ declare namespace Cypress { * Override default config options for Component Testing runner. * @default {} */ - component: TestingTypeConfig + component: TestingTypeConfigComponent } /** @@ -2813,8 +2820,8 @@ declare namespace Cypress { /** * All configuration items are optional. */ - type CoreConfigOptions = Partial - type ConfigOptions = CoreConfigOptions & {e2e?: CoreConfigOptions, component?: CoreConfigOptions } + type DeepPartial = T extends Function ? T : (T extends object ? { [P in keyof T]?: DeepPartial; } : T); + type ConfigOptions = DeepPartial interface PluginConfigOptions extends ResolvedConfigOptions { /** diff --git a/npm/vue/cypress.config.ts b/npm/vue/cypress.config.ts index 763642492ce3..0dfd097099c4 100644 --- a/npm/vue/cypress.config.ts +++ b/npm/vue/cypress.config.ts @@ -11,11 +11,7 @@ export default defineConfig({ experimentalFetchPolyfill: true, component: { testFiles: '**/*spec.{js,ts,tsx}', - setupNodeServer (on, config) { - if (config.testingType !== 'component') { - throw Error(`This is a component testing project. testingType should be 'component'. Received ${config.testingType}`) - } - + setupDevServer (options) { if (!webpackConfig.resolve) { webpackConfig.resolve = {} } @@ -25,8 +21,14 @@ export default defineConfig({ '@vue/compiler-core$': '@vue/compiler-core/dist/compiler-core.cjs.js', } + return startDevServer({ options, webpackConfig }) + }, + setupNodeServer (on, config) { + if (config.testingType !== 'component') { + throw Error(`This is a component testing project. testingType should be 'component'. Received ${config.testingType}`) + } + require('@cypress/code-coverage/task')(on, config) - on('dev-server:start', (options) => startDevServer({ options, webpackConfig })) return config }, diff --git a/npm/vue/cypress/plugins/index.js b/npm/vue/cypress/plugins/index.js new file mode 100644 index 000000000000..59b2bab6e4e6 --- /dev/null +++ b/npm/vue/cypress/plugins/index.js @@ -0,0 +1,22 @@ +/// +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +/** + * @type {Cypress.PluginConfig} + */ +// eslint-disable-next-line no-unused-vars +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +} diff --git a/packages/server/lib/plugins/child/index.js b/packages/server/lib/plugins/child/index.js index 4d0922e44584..73e99d1fb1b8 100644 --- a/packages/server/lib/plugins/child/index.js +++ b/packages/server/lib/plugins/child/index.js @@ -3,6 +3,6 @@ require('graceful-fs').gracefulify(require('fs')) require('../../util/suppress_warnings').suppress() const ipc = require('../util').wrapIpc(process) -const { file: pluginsFile, projectRoot, functionName } = require('minimist')(process.argv.slice(2)) +const { file: pluginsFile, projectRoot, testingType, functionName } = require('minimist')(process.argv.slice(2)) -require('./run_plugins')(ipc, pluginsFile, projectRoot, functionName) +require('./run_plugins')(ipc, pluginsFile, projectRoot, testingType, functionName) diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js index 1afcbec8f23e..6c946b38920a 100644 --- a/packages/server/lib/plugins/child/run_plugins.js +++ b/packages/server/lib/plugins/child/run_plugins.js @@ -36,6 +36,7 @@ const getDefaultPreprocessor = function (config) { } let plugins +let setupDevServerFunction const load = (ipc, config, pluginsFile) => { debug('run plugins function') @@ -87,6 +88,10 @@ const load = (ipc, config, pluginsFile) => { .try(() => { debug('run plugins function') + if (setupDevServerFunction) { + register('dev-server:start', setupDevServerFunction) + } + return plugins(register, config) }) .tap(() => { @@ -138,7 +143,11 @@ const execute = (ipc, event, ids, args = []) => { } } -let tsRegistered = false +function interopRequire (pluginsFile) { + const exp = require(pluginsFile) + + return exp && exp.default ? exp.default : exp +} /** * the plugins function can be either at the top level @@ -149,27 +158,24 @@ let tsRegistered = false * @param {string} functionName path to the function in the exported object like `"e2e.plugins"` * @returns the plugins function found */ -function getPluginsFunction (pluginsFile, functionName) { - const exp = require(pluginsFile) - const resolvedExport = exp && exp.default ? exp.default : exp +function getPluginsFunction (resolvedExport, testingType, functionName) { + if (testingType) { + resolvedExport = resolvedExport[testingType] + } if (functionName) { - // follow the object path given in parameter to the function sought - let finalFunction = resolvedExport - - for (const part of functionName.split('.')) { - finalFunction = finalFunction[part] - } - - return finalFunction + resolvedExport = resolvedExport[functionName] } return resolvedExport } -const runPlugins = (ipc, pluginsFile, projectRoot, functionName) => { - debug('pluginsFile:', pluginsFile) - debug('functionName:', functionName) +let tsRegistered = false + +const runPlugins = (ipc, pluginsFile, projectRoot, testingType, functionName) => { + debug('plugins file:', pluginsFile) + debug('testingType:', testingType) + debug('function name:', functionName) debug('project root:', projectRoot) if (!projectRoot) { throw new Error('Unexpected: projectRoot should be a string') @@ -200,9 +206,16 @@ const runPlugins = (ipc, pluginsFile, projectRoot, functionName) => { try { debug('require pluginsFile "%s", functionName "%s"', pluginsFile, functionName) - plugins = getPluginsFunction(pluginsFile, functionName) + const pluginsObject = interopRequire(pluginsFile) + + plugins = getPluginsFunction(pluginsObject, testingType, functionName) + + if (testingType === 'component') { + setupDevServerFunction = pluginsObject[testingType].setupDevServer + } debug('plugins %o', plugins) + debug('setupDevServerFunction %o', setupDevServerFunction) } catch (err) { debug('failed to require pluginsFile:\n%s', err.stack) ipc.send('load:error', 'PLUGINS_FILE_ERROR', pluginsFile, err.stack) diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index 3391614e150c..000e0d845670 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -96,7 +96,8 @@ const init = (config, options) => { if (typeof config[PLUGINS_FUNCTION_NAME] === 'function') { childArguments.push( - '--functionName', `${testingType}.${PLUGINS_FUNCTION_NAME}`, + '--testingType', testingType, + '--functionName', PLUGINS_FUNCTION_NAME, '--file', options.configFile, ) } else { From 34dc08a2e9fba2a7b5c4e51f8ce1129210b58589 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 31 Jul 2021 11:34:54 -0500 Subject: [PATCH 129/204] rename setupServer => setupEvents --- cli/types/cypress.d.ts | 2 +- npm/vue/cypress.config.ts | 2 +- packages/desktop-gui/src/settings/configuration.jsx | 2 +- packages/server/lib/config_options.ts | 2 +- packages/server/lib/errors.js | 4 ++-- packages/server/lib/plugins/index.js | 2 +- .../fixtures/projects/plugin-config-js/cypress.config.js | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index fa245b5516c6..99cb57d06edd 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2469,7 +2469,7 @@ declare namespace Cypress { type PluginsFunction = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptions | undefined) - type TestingTypeConfig = Omit & { setupNodeServer?: PluginsFunction } + type TestingTypeConfig = Omit & { setupNodeEvents?: PluginsFunction } type TestingTypeConfigComponent = TestingTypeConfig & { /** * Return the setup of your server diff --git a/npm/vue/cypress.config.ts b/npm/vue/cypress.config.ts index 0dfd097099c4..2f1c8ba51474 100644 --- a/npm/vue/cypress.config.ts +++ b/npm/vue/cypress.config.ts @@ -23,7 +23,7 @@ export default defineConfig({ return startDevServer({ options, webpackConfig }) }, - setupNodeServer (on, config) { + setupNodeEvents (on, config) { if (config.testingType !== 'component') { throw Error(`This is a component testing project. testingType should be 'component'. Received ${config.testingType}`) } diff --git a/packages/desktop-gui/src/settings/configuration.jsx b/packages/desktop-gui/src/settings/configuration.jsx index f1de2d1b3130..dc6512823258 100644 --- a/packages/desktop-gui/src/settings/configuration.jsx +++ b/packages/desktop-gui/src/settings/configuration.jsx @@ -184,7 +184,7 @@ const Configuration = observer(({ project }) => ( {project.hasE2EFunction ? <> function - set in the e2e.setupNodeServer() function in the {configFileFormatted(project.configFile)} + set in the e2e.setupNodeEvents() function in the {configFileFormatted(project.configFile)} : <> diff --git a/packages/server/lib/config_options.ts b/packages/server/lib/config_options.ts index 3549842b8b20..48b3583ddc04 100644 --- a/packages/server/lib/config_options.ts +++ b/packages/server/lib/config_options.ts @@ -166,7 +166,7 @@ export const options = [ validation: v.isStringOrFalse, isFolder: true, }, { - name: 'setupNodeServer', + name: 'setupNodeEvents', defaultvalue: null, validation: v.isFunction, onlyInOverride: true, diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index ae6076d3bf76..67bfa0cdfdee 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -1004,9 +1004,9 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { return stripIndent` \`pluginsFile\` cannot be set in a \`${arg1}\` file. - \`pluginsFile\` is deprecated and will error in cypress 9.0, prefer using the \`setupNodeServer\` function instead. + \`pluginsFile\` is deprecated and will error in cypress 9.0, prefer using the \`setupNodeEvents\` function instead. - https://on.cypress.io/setupNodeServer + https://on.cypress.io/setupNodeEvents ` case `CONFIG_FILES_LANGUAGE_CONFLICT`: diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index 000e0d845670..8c6fc3cafd62 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -13,7 +13,7 @@ let pluginsProcess = null let registeredEvents = {} let handlers = [] -const PLUGINS_FUNCTION_NAME = 'setupNodeServer' +const PLUGINS_FUNCTION_NAME = 'setupNodeEvents' const register = (event, callback) => { debug(`register event '${event}'`) diff --git a/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js b/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js index baf106d1fd5c..abed01437463 100644 --- a/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js +++ b/packages/server/test/support/fixtures/projects/plugin-config-js/cypress.config.js @@ -1,6 +1,6 @@ module.exports = { e2e: { - setupNodeServer (on, config) { + setupNodeEvents (on, config) { return new Promise((resolve) => { setTimeout(resolve, 100) }) From e5838e18912ba61234c6d137914e477f8658626c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 2 Aug 2021 11:00:17 -0500 Subject: [PATCH 130/204] fix: types --- cli/types/cypress.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 99cb57d06edd..37d2c1c5167d 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2470,12 +2470,12 @@ declare namespace Cypress { type PluginsFunction = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptions | undefined) type TestingTypeConfig = Omit & { setupNodeEvents?: PluginsFunction } - type TestingTypeConfigComponent = TestingTypeConfig & { + type TestingTypeConfigComponent = TestingTypeConfig & { /** * Return the setup of your server * @arg options the dev server options to pass directly to the dev-server */ - setupDevServer(options: DevServerOptions): Promise + setupDevServer(options: DevServerOptions): Promise } interface ResolvedConfigOptions { @@ -2734,7 +2734,7 @@ declare namespace Cypress { * Override default config options for Component Testing runner. * @default {} */ - component: TestingTypeConfigComponent + component: TestingTypeConfigComponent } /** @@ -2820,7 +2820,7 @@ declare namespace Cypress { /** * All configuration items are optional. */ - type DeepPartial = T extends Function ? T : (T extends object ? { [P in keyof T]?: DeepPartial; } : T); + type DeepPartial = T extends object ? { [P in keyof T]?: DeepPartial; } : T type ConfigOptions = DeepPartial interface PluginConfigOptions extends ResolvedConfigOptions { From 46b90d667d5242f07eb791b49ef2e9c43ce1ef6f Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 2 Aug 2021 11:09:24 -0500 Subject: [PATCH 131/204] better validation of setupDevServer --- packages/server/lib/config.js | 6 +++++- packages/server/lib/config_options.ts | 19 ++++++++++++------- packages/server/lib/util/validation.js | 22 ++++++---------------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/packages/server/lib/config.js b/packages/server/lib/config.js index ddda23ebc555..ba06b63d20b1 100644 --- a/packages/server/lib/config.js +++ b/packages/server/lib/config.js @@ -88,7 +88,11 @@ const validate = (cfg, onErr) => { const validationFn = validationRules[key] if (onlyInOverrideValues[key]) { - return `key ${key} is only valid in a testingType object, it is invalid to use it in the root` + if (onlyInOverrideValues[key] === true) { + return `key \`${key}\` is only valid in a testingType object, it is invalid to use it in the root` + } + + return `key \`${key}\` is only valid in the \`${onlyInOverrideValues[key]}\` object, it is invalid to use it in the root` } // does this key have a validation rule? diff --git a/packages/server/lib/config_options.ts b/packages/server/lib/config_options.ts index 48b3583ddc04..ebeabefdbd1c 100644 --- a/packages/server/lib/config_options.ts +++ b/packages/server/lib/config_options.ts @@ -44,7 +44,7 @@ export const options = [ name: 'component', // runner-ct overrides defaultValue: null, - validation: v.isOverrideFunction, + validation: v.isValidTestingTypeConfig, }, { name: 'componentFolder', defaultValue: 'cypress/component', @@ -74,7 +74,7 @@ export const options = [ name: 'e2e', // e2e runner overrides defaultValue: null, - validation: v.isOverrideFunction, + validation: v.isValidTestingTypeConfig, }, { name: 'env', validation: v.isPlainObject, @@ -165,11 +165,6 @@ export const options = [ defaultValue: 'cypress/plugins', validation: v.isStringOrFalse, isFolder: true, - }, { - name: 'setupNodeEvents', - defaultvalue: null, - validation: v.isFunction, - onlyInOverride: true, }, { name: 'port', defaultValue: null, @@ -220,6 +215,16 @@ export const options = [ defaultValue: 'cypress/screenshots', validation: v.isStringOrFalse, isFolder: true, + }, { + name: 'setupNodeEvents', + defaultvalue: null, + validation: v.isFunction, + onlyInOverride: true, + }, { + name: 'setupDevServer', + defaultvalue: null, + validation: v.isFunction, + onlyInOverride: 'component', }, { name: 'socketId', defaultValue: null, diff --git a/packages/server/lib/util/validation.js b/packages/server/lib/util/validation.js index 079a655c62fb..800e12eec767 100644 --- a/packages/server/lib/util/validation.js +++ b/packages/server/lib/util/validation.js @@ -129,7 +129,7 @@ const isPlainObject = (key, value) => { return errMsg(key, value, 'a plain object') } -const isValidConfig = (key, config) => { +const isValidTestingTypeConfig = (key, config) => { const status = isPlainObject(key, config) if (status !== true) { @@ -138,6 +138,10 @@ const isValidConfig = (key, config) => { for (const rule of configOptions.options) { if (rule.name in config && rule.validation) { + if (typeof rule.onlyInOverride === 'string' && rule.onlyInOverride !== key) { + return `key \`${rule.name}\` is only valid in the \`${rule.onlyInOverride}\` object, invalid use of this key in the \`${key}\` object` + } + const status = rule.validation(`${key}.${rule.name}`, config[rule.name]) if (status !== true) { @@ -167,18 +171,6 @@ const isOneOf = (...values) => { } } -const isOverrideFunction = (key, value) => { - if (typeof value !== 'function' && !_.isPlainObject(value) && value !== null) { - return errMsg(key, value, `a plain object or a function`) - } - - if (value === null || typeof value === 'function') { - return true - } - - return isValidConfig(key, value) -} - /** * Validates whether the supplied set of cert information is valid * @returns {string|true} Returns `true` if the information set is valid. Returns an error message if it is not. @@ -278,9 +270,7 @@ module.exports = { isValidRetriesConfig, - isValidConfig, - - isOverrideFunction, + isValidTestingTypeConfig, isPlainObject, From 81516d79152897f4ec25f05f5a330fa5af05cff5 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 2 Aug 2021 12:02:39 -0500 Subject: [PATCH 132/204] better validation for config --- packages/server/lib/config.js | 22 ++++++++----- packages/server/lib/util/validation.js | 10 +++--- packages/server/test/unit/config_spec.js | 42 +++++++++++++++++++++++- 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/packages/server/lib/config.js b/packages/server/lib/config.js index ba06b63d20b1..eaf63782f62f 100644 --- a/packages/server/lib/config.js +++ b/packages/server/lib/config.js @@ -89,10 +89,10 @@ const validate = (cfg, onErr) => { if (onlyInOverrideValues[key]) { if (onlyInOverrideValues[key] === true) { - return `key \`${key}\` is only valid in a testingType object, it is invalid to use it in the root` + return onErr(`key \`${key}\` is only valid in a testingType object, it is invalid to use it in the root`) } - return `key \`${key}\` is only valid in the \`${onlyInOverrideValues[key]}\` object, it is invalid to use it in the root` + return onErr(`key \`${key}\` is only valid in the \`${onlyInOverrideValues[key]}\` object, it is invalid to use it in the root`) } // does this key have a validation rule? @@ -226,12 +226,6 @@ module.exports = { settings.readEnv(projectRoot).then(validateFile('cypress.env.json')), ]) .spread((configObject, envFile) => { - const testingType = isComponentTesting(options) ? 'component' : 'e2e' - - if (testingType in configObject) { - configObject = { ...configObject, ...configObject[testingType] } - } - return this.set({ projectName: this.getNameFromRoot(projectRoot), projectRoot, @@ -261,7 +255,17 @@ module.exports = { config.projectRoot = projectRoot config.projectName = projectName - return this.mergeDefaults(config, options) + const testingType = isComponentTesting(options) ? 'component' : 'e2e' + + return this.mergeDefaults(config, options).then((configObject = {}) => { + if (testingType in configObject) { + configObject = { ...configObject, ...configObject[testingType] } + } + + debug('merged config is %o', config) + + return configObject + }) }, mergeDefaults (config = {}, options = {}) { diff --git a/packages/server/lib/util/validation.js b/packages/server/lib/util/validation.js index 800e12eec767..7743a65e85d3 100644 --- a/packages/server/lib/util/validation.js +++ b/packages/server/lib/util/validation.js @@ -137,15 +137,17 @@ const isValidTestingTypeConfig = (key, config) => { } for (const rule of configOptions.options) { - if (rule.name in config && rule.validation) { + if (rule.name in config) { if (typeof rule.onlyInOverride === 'string' && rule.onlyInOverride !== key) { return `key \`${rule.name}\` is only valid in the \`${rule.onlyInOverride}\` object, invalid use of this key in the \`${key}\` object` } - const status = rule.validation(`${key}.${rule.name}`, config[rule.name]) + if (rule.validation) { + const status = rule.validation(`${key}.${rule.name}`, config[rule.name]) - if (status !== true) { - return status + if (status !== true) { + return status + } } } } diff --git a/packages/server/test/unit/config_spec.js b/packages/server/test/unit/config_spec.js index f6d9e62d4e7f..aaed8ea4bba7 100644 --- a/packages/server/test/unit/config_spec.js +++ b/packages/server/test/unit/config_spec.js @@ -246,7 +246,7 @@ describe('lib/config', () => { it('fails if not a plain object or a function', function () { this.setup({ component: false }) - this.expectValidationFails('to be a plain object or a function') + this.expectValidationFails('to be a plain object') return this.expectValidationFails('the value was: `false`') }) @@ -271,6 +271,46 @@ describe('lib/config', () => { expect(obj.viewportWidth).to.eq(300) }) }) + + describe('setupDevServer', function () { + it('allows setupDevServer in component', function () { + this.setup({ + viewportWidth: 1024, + component: { + setupDevServer () { + // noop + }, + }, + }) + + return config.get(this.projectRoot, { testingType: 'component' }) + .then((obj) => { + expect(obj.component.setupDevServer).to.be.a('function') + }) + }) + + it('disallows setupDevServer in root', function () { + this.setup({ + setupDevServer () { + // noop + }, + }) + + return this.expectValidationFails('\`component\`') + }) + + it('disallows setupDevServer in e2e', function () { + this.setup({ + e2e: { + setupDevServer () { + // noop + }, + }, + }) + + return this.expectValidationFails('\`component\`') + }) + }) }) context('e2e', () => { From 677b67ff513e2b6292bb9407a54bdcbd13bb72d6 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 2 Aug 2021 13:56:33 -0500 Subject: [PATCH 133/204] fix validation of config --- packages/server/lib/config.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/server/lib/config.js b/packages/server/lib/config.js index eaf63782f62f..33475a599b1f 100644 --- a/packages/server/lib/config.js +++ b/packages/server/lib/config.js @@ -83,11 +83,18 @@ const validateNoBreakingConfig = (cfg) => { }) } -const validate = (cfg, onErr) => { +/** + * validate a root config object + * @param {object} cfg config object to validate + * @param {(errMsg:string) => void} onErr function run when invalid config is found + * @param {boolean} bypassRootLimitations skip checks related to position when we are working with merged configs + * @returns + */ +function validate (cfg, onErr, bypassRootLimitations = false) { return _.each(cfg, (value, key) => { const validationFn = validationRules[key] - if (onlyInOverrideValues[key]) { + if (!bypassRootLimitations && onlyInOverrideValues[key]) { if (onlyInOverrideValues[key] === true) { return onErr(`key \`${key}\` is only valid in a testingType object, it is invalid to use it in the root`) } @@ -387,7 +394,7 @@ module.exports = { } return errors.throw('CONFIG_VALIDATION_ERROR', errMsg) - }) + }, true) let originalResolvedBrowsers = cfg && cfg.resolved && cfg.resolved.browsers && R.clone(cfg.resolved.browsers) From a1c3a2a1f92fb1884f578686a6df5398b892b9fd Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 2 Aug 2021 14:05:05 -0500 Subject: [PATCH 134/204] fix watch tests --- packages/server/test/unit/project_spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/test/unit/project_spec.js b/packages/server/test/unit/project_spec.js index 007111d00d58..7eb42780f913 100644 --- a/packages/server/test/unit/project_spec.js +++ b/packages/server/test/unit/project_spec.js @@ -688,12 +688,12 @@ This option will not have an effect in Some-other-name. Tests that rely on web s sinon.stub(settings, 'pathToConfigFile').returns('/path/to/cypress.json') sinon.stub(settings, 'pathToCypressEnvJson').returns('/path/to/cypress.env.json') this.watch = sinon.stub(this.project.watchers, 'watch') + this.watchTree = sinon.stub(this.project.watchers, 'watchTree') }) it('watches cypress.json and cypress.env.json', function () { this.project.watchSettings({ onSettingsChanged () {} }, {}) - expect(this.watch).to.be.calledTwice - expect(this.watch).to.be.calledWith('/path/to/cypress.json') + expect(this.watchTree).to.be.calledWith('/path/to/cypress.json') expect(this.watch).to.be.calledWith('/path/to/cypress.env.json') }) From 7c442c579341420c234eda283a146523479b4b8c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 2 Aug 2021 14:21:09 -0500 Subject: [PATCH 135/204] avoid creating pluginsFile when unnecessary --- packages/server/lib/project-base.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 3101fbfb4996..6988a2a42649 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -186,7 +186,7 @@ export class ProjectBase extends EE { // even when headlessly or else it will cause an error when // we try to load it and it's not there. We must do this here // else initialing the plugins will instantly fail. - if (cfg.pluginsFile && !cfg[this.testingType]?.plugins) { + if (cfg.pluginsFile && !cfg[this.testingType]?.setupNodeEvents) { debug('scaffolding with plugins file %s', cfg.pluginsFile) await scaffold.plugins(path.dirname(cfg.pluginsFile), cfg) From 77b21f337426759030aec244db93d0c32f4fb3fe Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 2 Aug 2021 16:00:22 -0500 Subject: [PATCH 136/204] test: weird webpack bug --- npm/react/plugins/utils/get-transpile-folders.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/npm/react/plugins/utils/get-transpile-folders.js b/npm/react/plugins/utils/get-transpile-folders.js index b0b50dbec5ae..4ecf9e6496a5 100644 --- a/npm/react/plugins/utils/get-transpile-folders.js +++ b/npm/react/plugins/utils/get-transpile-folders.js @@ -7,15 +7,15 @@ function getTranspileFolders (config) { // user can disable folders, so check first if (config.componentFolder) { - folders.push(config.componentFolder) + folders.push(path.resolve(config.projectRoot, config.componentFolder)) } if (config.fixturesFolder) { - folders.push(config.fixturesFolder) + folders.push(path.resolve(config.projectRoot, config.fixturesFolder)) } if (config.supportFolder) { - folders.push(config.supportFolder) + folders.push(path.resolve(config.projectRoot, config.supportFolder)) } return folders From 3f9fbeb04e23319d5af2cd985b232a4bbd9ffbf7 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 2 Aug 2021 17:11:16 -0500 Subject: [PATCH 137/204] types: no pluginsFile and supportFile on root config --- cli/types/cypress-npm-api.d.ts | 2 +- cli/types/cypress.d.ts | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/cli/types/cypress-npm-api.d.ts b/cli/types/cypress-npm-api.d.ts index ba07006e5348..84ddd92ebb7b 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: Cypress.ConfigOptions + config: Cypress.ConfigOptionsMergedWithTestingTypes /** * Path to the config file to be used. * diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 37d2c1c5167d..55b59efb2798 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -9,7 +9,7 @@ declare namespace Cypress { type ViewportOrientation = 'portrait' | 'landscape' type PrevSubject = 'optional' | 'element' | 'document' | 'window' type TestingType = 'e2e' | 'component' - type PluginConfig = (on: PluginEvents, config: PluginConfigOptions) => void | ConfigOptions | Promise + type PluginConfig = (on: PluginEvents, config: PluginConfigOptions) => void | ConfigOptionsMergedWithTestingTypes | Promise interface CommandOptions { prevSubject: boolean | PrevSubject | PrevSubject[] @@ -323,7 +323,7 @@ declare namespace Cypress { }) ``` */ - config(Object: ConfigOptions): void + config(Object: ConfigOptionsMergedWithTestingTypes): void // no real way to type without generics /** @@ -2467,7 +2467,7 @@ declare namespace Cypress { cmdKey: boolean } - type PluginsFunction = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptions | undefined) + type PluginsFunction = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptionsMergedWithTestingTypes | undefined) type TestingTypeConfig = Omit & { setupNodeEvents?: PluginsFunction } type TestingTypeConfigComponent = TestingTypeConfig & { @@ -2812,7 +2812,7 @@ declare namespace Cypress { xhrUrl: string } - interface TestConfigOverrides extends Partial> { + interface TestConfigOverrides extends Partial> { browser?: IsBrowserMatcher | IsBrowserMatcher[] keystrokeDelay?: number } @@ -2821,7 +2821,15 @@ declare namespace Cypress { * All configuration items are optional. */ type DeepPartial = T extends object ? { [P in keyof T]?: DeepPartial; } : T - type ConfigOptions = DeepPartial + /** + * ConfigOptions after the current testingType has been merget into the root. + */ + type ConfigOptionsMergedWithTestingTypes = DeepPartial + + /** + * Config model of cypress. To be used in `cypress.config.js` + */ + type ConfigOptions = Omit interface PluginConfigOptions extends ResolvedConfigOptions { /** @@ -5237,7 +5245,7 @@ declare namespace Cypress { interface BeforeRunDetails { browser?: Browser - config: ConfigOptions + config: ConfigOptionsMergedWithTestingTypes cypressVersion: string group?: string parallel?: boolean From c38c2dd02d72e7d48b2914efe687c8df84305c6f Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 2 Aug 2021 18:00:20 -0500 Subject: [PATCH 138/204] fix types --- cli/types/cypress.d.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 55b59efb2798..457bfe99261c 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2473,7 +2473,7 @@ declare namespace Cypress { type TestingTypeConfigComponent = TestingTypeConfig & { /** * Return the setup of your server - * @arg options the dev server options to pass directly to the dev-server + * @param options the dev server options to pass directly to the dev-server */ setupDevServer(options: DevServerOptions): Promise } @@ -2511,7 +2511,7 @@ declare namespace Cypress { reporter: string /** * Some reporters accept [reporterOptions](https://on.cypress.io/reporters) that customize their behavior - * @default "spec" + * @default {} */ reporterOptions: { [key: string]: any } /** @@ -2551,7 +2551,7 @@ declare namespace Cypress { taskTimeout: number /** * Path to folder where application files will attempt to be served from - * @default root project folder + * @default "root project folder" */ fileServerFolder: string /** @@ -2562,6 +2562,7 @@ declare namespace Cypress { /** * Path to folder containing integration test files * @default "cypress/integration" + * @deprecated use the testFiles glob in the e2e object */ integrationFolder: string /** @@ -2572,6 +2573,7 @@ declare namespace Cypress { /** * If set to `system`, Cypress will try to find a `node` executable on your path to use when executing your plugins. Otherwise, Cypress will use the Node version bundled with Cypress. * @default "bundled" + * @deprecated nodeVersion will soon be fixed to "system" to avoid confusion */ nodeVersion: 'system' | 'bundled' /** @@ -2701,6 +2703,7 @@ declare namespace Cypress { blockHosts: null | string | string[] /** * Path to folder containing component test files. + * @deprecated use the testFiles pattern inside the component object instead */ componentFolder: false | string /** @@ -2709,6 +2712,7 @@ declare namespace Cypress { projectId: null | string /** * Path to the support folder. + * @deprecated use supportFile instead */ supportFolder: string /** @@ -2817,19 +2821,21 @@ declare namespace Cypress { keystrokeDelay?: number } + // here we need to use the `Function` type to type setup functions options properly + // if we don't, they will be typed as any + /* tslint:disable-next-line ban-types */ + type DeepPartial = T extends Function ? T : T extends object ? { [P in keyof T]?: DeepPartial; } : T + /** - * All configuration items are optional. - */ - type DeepPartial = T extends object ? { [P in keyof T]?: DeepPartial; } : T - /** - * ConfigOptions after the current testingType has been merget into the root. + * ConfigOptions after the current testingType has been merget into the root. */ type ConfigOptionsMergedWithTestingTypes = DeepPartial /** * Config model of cypress. To be used in `cypress.config.js` */ - type ConfigOptions = Omit + type ConfigOptions = Omit + & {component?: {setupDevServer: TestingTypeConfigComponent['setupDevServer'] }} interface PluginConfigOptions extends ResolvedConfigOptions { /** From 6a06540591c30cd3cd1de934a66b717c0f723db7 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 2 Aug 2021 18:07:38 -0500 Subject: [PATCH 139/204] style: add comment --- cli/types/cypress.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 457bfe99261c..bca7534c2ac5 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2835,6 +2835,7 @@ declare namespace Cypress { * Config model of cypress. To be used in `cypress.config.js` */ type ConfigOptions = Omit + // make setupDevServer required in component & {component?: {setupDevServer: TestingTypeConfigComponent['setupDevServer'] }} interface PluginConfigOptions extends ResolvedConfigOptions { From c1ab0d1d7ed9555370f78249f2d04bb035f1bbcf Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 2 Aug 2021 20:50:14 -0500 Subject: [PATCH 140/204] Add deprecation warning for e2e gui --- packages/server/lib/errors.js | 12 +++++++++++- packages/server/lib/project-base.ts | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 67bfa0cdfdee..075c6eac3f0d 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -1000,6 +1000,7 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { https://on.cypress.io/component-testing ` + // TODO: update with vetted cypress language case 'CONFLICT_PLUGINSFILE_CONFIGJS': return stripIndent` \`pluginsFile\` cannot be set in a \`${arg1}\` file. @@ -1009,7 +1010,8 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { https://on.cypress.io/setupNodeEvents ` - case `CONFIG_FILES_LANGUAGE_CONFLICT`: + // TODO: update with vetted cypress language + case 'CONFIG_FILES_LANGUAGE_CONFLICT': return stripIndent` There is both a \`cypress.config.js\` and a \`cypress.config.ts\` in the location below: @@ -1017,6 +1019,14 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { Cypress does not know which one to read for config. Please remove one of the two and try again. ` + + // TODO: update with vetted cypress language + case 'DEPREACTED_CYPRESS_JSON': + return stripIndent` + in 9.0, cypress.json will not be supported anymore. + please see the docs to migrate it to cypress.config.js + ` + case 'UNSUPPORTED_BROWSER_VERSION': return arg1 default: diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 6988a2a42649..214e6f0f841c 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -180,6 +180,10 @@ export class ProjectBase extends EE { let cfg = this.getConfig() + if (typeof cfg.configFile === 'string' && /\.json$/.test(cfg.configFile)) { + this.options.onWarning(errors.get('DEPREACTED_CYPRESS_JSON', cfg.configFile)) + } + process.chdir(this.projectRoot) // TODO: we currently always scaffold the plugins file From db2f45fa252723f4687b261fe454b1bc7af3a32a Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 2 Aug 2021 22:01:56 -0500 Subject: [PATCH 141/204] add error message display to runnerCT --- packages/runner-ct/src/app/RunnerCt.tsx | 138 ++++++++++-------- .../src/app/WarningMessage.module.scss | 7 + packages/runner-ct/src/app/WarningMessage.tsx | 17 +++ packages/runner-ct/src/lib/state.ts | 14 ++ 4 files changed, 117 insertions(+), 59 deletions(-) create mode 100644 packages/runner-ct/src/app/WarningMessage.module.scss create mode 100644 packages/runner-ct/src/app/WarningMessage.tsx diff --git a/packages/runner-ct/src/app/RunnerCt.tsx b/packages/runner-ct/src/app/RunnerCt.tsx index 849748194dcb..b13cf2700b61 100644 --- a/packages/runner-ct/src/app/RunnerCt.tsx +++ b/packages/runner-ct/src/app/RunnerCt.tsx @@ -20,6 +20,7 @@ import { useGlobalHotKey } from '../lib/useHotKey' import { animationFrameDebounce } from '../lib/debounce' import { LeftNavMenu } from './LeftNavMenu' import { SpecContent } from './SpecContent' +import { WarningMessage } from './WarningMessage' import { hideIfScreenshotting, hideSpecsListIfNecessary } from '../lib/hideGuard' import { NoSpec } from './NoSpec' @@ -176,72 +177,91 @@ const RunnerCt = namedObserver('RunnerCt', state.updateSpecListWidth(width) } + const renderWarnings = () => { + const { warnings } = state + + return warnings.map((warning, i) => ( + removeWarning(warning)} + /> + )) + } + + const removeWarning = (warning) => { + state.removeWarning(warning) + } + return ( - 50)} - minSize={hideIfScreenshotting(state, () => 50)} - defaultSize={hideIfScreenshotting(state, () => 50)} - > - {state.screenshotting - ? - : ( - - )} + <> + {renderWarnings()} state.isSpecsListOpen ? 30 : 0)} - maxSize={hideIfScreenshotting(state, () => state.isSpecsListOpen ? 600 : 0)} - defaultSize={hideIfScreenshotting(state, () => state.isSpecsListOpen ? state.specListWidth : 0)} - className={cs('primary', { isSpecsListClosed: !state.isSpecsListOpen })} - pane2Style={{ - borderLeft: '1px solid rgba(230, 232, 234, 1)' /* $metal-20 */, - }} - onDragFinished={persistWidth('ctSpecListWidth')} - onChange={animationFrameDebounce(updateSpecListWidth)} + allowResize={false} + maxSize={hideIfScreenshotting(state, () => 50)} + minSize={hideIfScreenshotting(state, () => 50)} + defaultSize={hideIfScreenshotting(state, () => 50)} > - { - state.specs.length < 1 ? ( - -

      + {state.screenshotting + ? + : ( + + )} + state.isSpecsListOpen ? 30 : 0)} + maxSize={hideIfScreenshotting(state, () => state.isSpecsListOpen ? 600 : 0)} + defaultSize={hideIfScreenshotting(state, () => state.isSpecsListOpen ? state.specListWidth : 0)} + className={cs('primary', { isSpecsListClosed: !state.isSpecsListOpen })} + pane2Style={{ + borderLeft: '1px solid rgba(230, 232, 234, 1)' /* $metal-20 */, + }} + onDragFinished={persistWidth('ctSpecListWidth')} + onChange={animationFrameDebounce(updateSpecListWidth)} + > + { + state.specs.length < 1 ? ( + +

      Create a new spec file in - {' '} - - { - props.config.componentFolder - ? props.config.componentFolder.replace(props.config.projectRoot, '') - : 'the component specs folder' - } - - {' '} + {' '} + + { + props.config.componentFolder + ? props.config.componentFolder.replace(props.config.projectRoot, '') + : 'the component specs folder' + } + + {' '} and it will immediately appear here. -

      -
      - ) : ( - - ) - } - - +

      + + ) : ( + + ) + } + + +
      - + ) }) diff --git a/packages/runner-ct/src/app/WarningMessage.module.scss b/packages/runner-ct/src/app/WarningMessage.module.scss new file mode 100644 index 000000000000..b74d2c041bc9 --- /dev/null +++ b/packages/runner-ct/src/app/WarningMessage.module.scss @@ -0,0 +1,7 @@ +.warning{ + border-bottom: 2px solid; + color:#8a6d3b; + background-color:#fcf8e3; + border-color:#faebcc; + padding:15px 20px; +} \ No newline at end of file diff --git a/packages/runner-ct/src/app/WarningMessage.tsx b/packages/runner-ct/src/app/WarningMessage.tsx new file mode 100644 index 000000000000..069c16ac9385 --- /dev/null +++ b/packages/runner-ct/src/app/WarningMessage.tsx @@ -0,0 +1,17 @@ +import * as React from 'react' +import { Warning } from '../lib/state' +import styles from './WarningMessage.module.scss' + +interface WarningMessageProps{ + warning: Warning + onDismissWarning: () => void +} + +export const WarningMessage: React.FC = + ({ warning }) => { + return ( +
      +        {JSON.stringify(warning.message)}
      +      
      + ) + } diff --git a/packages/runner-ct/src/lib/state.ts b/packages/runner-ct/src/lib/state.ts index ab8a2bf5eeaf..9ff48872689e 100644 --- a/packages/runner-ct/src/lib/state.ts +++ b/packages/runner-ct/src/lib/state.ts @@ -55,6 +55,10 @@ const _defaults: Defaults = { callbackAfterUpdate: null, } +export interface Warning{ + message: string +} + export default class State extends BaseStore { defaults = _defaults @@ -106,6 +110,8 @@ export default class State extends BaseStore { @observable activePlugin: string | null = null @observable plugins: UIPlugin[] = [] + @observable warnings: Warning[] = [] + constructor ({ spec, specs = [], @@ -192,6 +198,14 @@ export default class State extends BaseStore { } } + @action addWarning (warning: Warning) { + this.warnings.push(warning) + } + + @action removeWarning (warning: Warning) { + this.warnings + } + @action setScreenshotting (screenshotting: boolean) { this.screenshotting = screenshotting } From 2c539f4b46cce6906a0821dd245dd940836282a3 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 3 Aug 2021 09:34:50 -0500 Subject: [PATCH 142/204] types: export configuration types --- cli/types/cypress-npm-api.d.ts | 81 +++++++++++++++++----------------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/cli/types/cypress-npm-api.d.ts b/cli/types/cypress-npm-api.d.ts index 84ddd92ebb7b..410a3e4d2cf2 100644 --- a/cli/types/cypress-npm-api.d.ts +++ b/cli/types/cypress-npm-api.d.ts @@ -335,8 +335,7 @@ declare namespace CypressCommandLine { } } -declare module 'cypress' { - /** +/** * Cypress NPM module interface. * @see https://on.cypress.io/module-api * @example @@ -345,46 +344,46 @@ declare module 'cypress' { cypress.run().then(results => ...) ``` */ - interface CypressNpmApi { - /** - * Execute a headless Cypress test run. - * @see https://on.cypress.io/module-api#cypress-run - * @example - ``` - const cypress = require('cypress') - // runs all spec files matching a wildcard - cypress.run({ - spec: 'cypress/integration/admin*-spec.js' - }).then(results => { - if (results.status === 'failed') { - // Cypress could not run - } else { - // inspect results object - } - }) - ``` - */ - run(options?: Partial): Promise - /** - * Opens Cypress GUI. Resolves with void when the - * GUI is closed. - * @see https://on.cypress.io/module-api#cypress-open - */ - open(options?: Partial): Promise +declare module 'cypress' { + /** + * Execute a headless Cypress test run. + * @see https://on.cypress.io/module-api#cypress-run + * @example + ``` + const cypress = require('cypress') + // runs all spec files matching a wildcard + cypress.run({ + spec: 'cypress/integration/admin*-spec.js' + }).then(results => { + if (results.status === 'failed') { + // Cypress could not run + } else { + // inspect results object + } + }) + ``` + */ + export function run(options?: Partial): Promise + /** + * Opens Cypress GUI. Resolves with void when the + * GUI is closed. + * @see https://on.cypress.io/module-api#cypress-open + */ + export function open(options?: Partial): Promise - /** - * Utility functions for parsing CLI arguments the same way - * Cypress does - */ - cli: CypressCommandLine.CypressCliParser + /** + * Utility functions for parsing CLI arguments the same way + * Cypress does + */ + export const cli: CypressCommandLine.CypressCliParser - /** - * Type helper to make writing `cypress.config.ts` easier - */ - defineConfig(config: Cypress.ConfigOptions): Cypress.ConfigOptions - } + /** + * Type helper to make writing `cypress.config.ts` easier + */ + export function defineConfig(config: Cypress.ConfigOptions): Cypress.ConfigOptions - // export Cypress NPM module interface - const cypress: CypressNpmApi - export = cypress + /** + * Types for configuring cypress in cypress.config.ts + */ + export type ConfigOptions = Cypress.ConfigOptions } From a015c9f05ec035090bf790a4a4ee14d1f69fcf61 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 3 Aug 2021 10:43:40 -0500 Subject: [PATCH 143/204] remove lalala --- npm/vue/cypress/component/advanced/access-component/Message.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/npm/vue/cypress/component/advanced/access-component/Message.vue b/npm/vue/cypress/component/advanced/access-component/Message.vue index 5e258e933413..3989208f29f6 100644 --- a/npm/vue/cypress/component/advanced/access-component/Message.vue +++ b/npm/vue/cypress/component/advanced/access-component/Message.vue @@ -24,7 +24,6 @@ }, methods: { handleClick() { - console.log('lalala') this.$emit('message-clicked', this.message) } } From 5adb9ee243106c82cc2115f5938cc577c62f9e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barth=C3=A9l=C3=A9my=20Ledoux?= Date: Wed, 4 Aug 2021 10:07:11 -0500 Subject: [PATCH 144/204] style: typo in commment Co-authored-by: Lachlan Miller --- cli/lib/cypress.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/lib/cypress.js b/cli/lib/cypress.js index 1d6e560b8aa2..84ecbc7d137f 100644 --- a/cli/lib/cypress.js +++ b/cli/lib/cypress.js @@ -71,8 +71,8 @@ const cypressModuleApi = { /** * This function should not be used in pure JavaScript - * By the type of its argument it allows autocomplettion and - * type chekcing of the configuration given by users. + * By the type of its argument it allows autocompletion and + * type checking of the configuration given by users. * * @see ../types/cypress-npm-api.d.ts * @param {Cypress.ConfigOptions} config From 36e4fef5c62493a66799fd1e347034f784d02460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barth=C3=A9l=C3=A9my=20Ledoux?= Date: Wed, 4 Aug 2021 10:13:30 -0500 Subject: [PATCH 145/204] style: comment typo Co-authored-by: Lachlan Miller --- packages/server/lib/util/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 9e507970ed0d..8cd273b04501 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -159,7 +159,7 @@ module.exports = { const evaluatedFiles = foundConfigFiles.filter((file) => file !== 'cypress.json') // if we found more than one evaluated file (js or ts) - // we can't trust that migration has been doen properly + // we can't trust that migration has been done properly if (evaluatedFiles.length > 1) { return errors.throw('CONFIG_FILES_LANGUAGE_CONFLICT', projectRoot) } From e903baa54ce661e797bb2b8c9f9d859c8faa23fb Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Tue, 3 Aug 2021 14:57:36 -0500 Subject: [PATCH 146/204] remove useless function --- packages/runner-ct/src/app/RunnerCt.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/runner-ct/src/app/RunnerCt.tsx b/packages/runner-ct/src/app/RunnerCt.tsx index b13cf2700b61..1d3bf4c66c68 100644 --- a/packages/runner-ct/src/app/RunnerCt.tsx +++ b/packages/runner-ct/src/app/RunnerCt.tsx @@ -184,15 +184,11 @@ const RunnerCt = namedObserver('RunnerCt', removeWarning(warning)} + onDismissWarning={() => state.removeWarning(warning)} /> )) } - const removeWarning = (warning) => { - state.removeWarning(warning) - } - return ( <> {renderWarnings()} From 9fa76f78d641d2087f9a40745a51fb164cabc1be Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 4 Aug 2021 09:51:43 -0500 Subject: [PATCH 147/204] add console warning for deprecation --- packages/server/lib/project-base.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 581fabcb5024..2a13f4e97268 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -181,6 +181,8 @@ export class ProjectBase extends EE { let cfg = this.getConfig() if (typeof cfg.configFile === 'string' && /\.json$/.test(cfg.configFile)) { + errors.warning('DEPREACTED_CYPRESS_JSON', cfg.configFile) + this.options.onWarning(errors.get('DEPREACTED_CYPRESS_JSON', cfg.configFile)) } From 87c1202ba409dcdae7ad3a0031177dc0eb7622cb Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 4 Aug 2021 10:24:50 -0500 Subject: [PATCH 148/204] fix linting --- packages/server/lib/util/require_async.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/server/lib/util/require_async.ts b/packages/server/lib/util/require_async.ts index 5cf00300c772..b91c50b0b6e3 100644 --- a/packages/server/lib/util/require_async.ts +++ b/packages/server/lib/util/require_async.ts @@ -11,17 +11,17 @@ const debug = Debug('cypress:server:require_async') let requireProcess: cp.ChildProcess | null interface RequireAsyncOptions{ - projectRoot: string - loadErrorCode: string - /** - * members of the object returned that are functions and will need to be wrapped - */ - functionNames: string[] + projectRoot: string + loadErrorCode: string + /** + * members of the object returned that are functions and will need to be wrapped + */ + functionNames: string[] } interface ChildOptions{ - stdio: 'inherit' - execArgv?: string[] + stdio: 'inherit' + execArgv?: string[] } const killChildProcess = () => { From 12a20f9ba3429717118b64ec011655d32ca3a40c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 4 Aug 2021 11:37:25 -0500 Subject: [PATCH 149/204] convert require_async_child to ts --- packages/server/lib/plugins/util.js | 152 ---------------- packages/server/lib/plugins/util.ts | 165 ++++++++++++++++++ ..._async_child.js => require_async_child.ts} | 37 ++-- 3 files changed, 186 insertions(+), 168 deletions(-) delete mode 100644 packages/server/lib/plugins/util.js create mode 100644 packages/server/lib/plugins/util.ts rename packages/server/lib/util/{require_async_child.js => require_async_child.ts} (57%) diff --git a/packages/server/lib/plugins/util.js b/packages/server/lib/plugins/util.js deleted file mode 100644 index 0b84df127ada..000000000000 --- a/packages/server/lib/plugins/util.js +++ /dev/null @@ -1,152 +0,0 @@ -const _ = require('lodash') -const EE = require('events') -const debug = require('debug')('cypress:server:plugins') -const Promise = require('bluebird') - -const UNDEFINED_SERIALIZED = '__cypress_undefined__' -const FUNCTION_SERIALIZED = '__cypress_function__' - -const serializeError = (err) => { - return _.pick(err, 'name', 'message', 'stack', 'code', 'annotated', 'type') -} - -function serializeArgument (arg) { - if (arg === null || arg === undefined) { - return null - } - - if (typeof arg === 'function') { - return FUNCTION_SERIALIZED - } - - if (typeof arg === 'object') { - if (_.isArray(arg)) { - return arg.map(serializeArgument) - } - - const serializedObject = {} - - for (const [key, val] of Object.entries(arg)) { - serializedObject[key] = serializeArgument(val) - } - - return serializedObject - } - - return arg -} - -function deserializeArgument (arg) { - if (arg === null || arg === undefined) { - return null - } - - if (arg === FUNCTION_SERIALIZED) { - return function () { - throw Error('this function is not meant to be used on it\'s own. It is the result of a deserialization and can be used to check the type of a returned object.') - } - } - - if (typeof arg === 'object') { - if (_.isArray(arg)) { - return arg.map((argElement) => deserializeArgument(argElement)) - } - - return Object.keys(arg).reduce(function (acc, key) { - acc[key] = deserializeArgument(arg[key]) - - return acc - }, {}) - } - - return arg -} - -module.exports = { - serializeError, - - wrapIpc (aProcess) { - const emitter = new EE() - - aProcess.on('message', (message) => { - 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, ...rawArgs) { - if (aProcess.killed) { - return - } - - const args = rawArgs.map((arg) => serializeArgument(arg)) - - return aProcess.send({ - event, - args, - }) - }, - - on (event, handler) { - function wrappedHandler (...rawArgs) { - return handler(...rawArgs.map((arg) => deserializeArgument(arg))) - } - handler.__realHandlerFunction__ = wrappedHandler - emitter.on(event, wrappedHandler) - }, - - removeListener (event, handler) { - emitter.removeListener(event, handler.__realHandlerFunction__) - }, - } - }, - - wrapChildPromise (ipc, invoke, ids, args = []) { - return Promise.try(() => { - return invoke(ids.eventId, args) - }) - .then((value) => { - // undefined is coerced into null when sent over ipc, but we need - // to differentiate between them for 'task' event - if (value === undefined) { - value = UNDEFINED_SERIALIZED - } - - return ipc.send(`promise:fulfilled:${ids.invocationId}`, null, value) - }).catch((err) => { - return ipc.send(`promise:fulfilled:${ids.invocationId}`, serializeError(err)) - }) - }, - - wrapParentPromise (ipc, eventId, callback) { - const invocationId = _.uniqueId('inv') - - return new Promise((resolve, reject) => { - const handler = function (err, value) { - ipc.removeListener(`promise:fulfilled:${invocationId}`, handler) - - if (err) { - debug('promise rejected for id %s %o', invocationId, ':', err.stack) - reject(_.extend(new Error(err.message), err)) - - return - } - - if (value === UNDEFINED_SERIALIZED) { - value = undefined - } - - debug(`promise resolved for id '${invocationId}' with value`, value) - - return resolve(value) - } - - ipc.on(`promise:fulfilled:${invocationId}`, handler) - - return callback(invocationId) - }) - }, -} diff --git a/packages/server/lib/plugins/util.ts b/packages/server/lib/plugins/util.ts new file mode 100644 index 000000000000..d439b0ca7e6b --- /dev/null +++ b/packages/server/lib/plugins/util.ts @@ -0,0 +1,165 @@ +import _ from 'lodash' +import EE from 'events' +import Debug from 'debug' +import Promise from 'bluebird' +import type { ChildProcess } from 'child_process' + +const debug = Debug('cypress:server:plugins') + +const UNDEFINED_SERIALIZED = '__cypress_undefined__' +const FUNCTION_SERIALIZED = '__cypress_function__' + +export function serializeError (err) { + return _.pick(err, 'name', 'message', 'stack', 'code', 'annotated', 'type') +} + +function serializeArgument (arg) { + if (arg === null || arg === undefined) { + return null + } + + if (typeof arg === 'function') { + return FUNCTION_SERIALIZED + } + + if (typeof arg === 'object') { + if (_.isArray(arg)) { + return arg.map(serializeArgument) + } + + const serializedObject = {} + + for (const [key, val] of Object.entries(arg)) { + serializedObject[key] = serializeArgument(val) + } + + return serializedObject + } + + return arg +} + +function deserializeArgument (arg) { + if (arg === null || arg === undefined) { + return null + } + + if (arg === FUNCTION_SERIALIZED) { + return function () { + throw Error('this function is not meant to be used on it\'s own. It is the result of a deserialization and can be used to check the type of a returned object.') + } + } + + if (typeof arg === 'object') { + if (_.isArray(arg)) { + return arg.map((argElement) => deserializeArgument(argElement)) + } + + return Object.keys(arg).reduce(function (acc, key) { + acc[key] = deserializeArgument(arg[key]) + + return acc + }, {}) + } + + return arg +} + +type Handler = {(...args: any[]): any, __realHandlerFunction__?: () => void} + +export function wrapIpc (aProcess: ChildProcess | (NodeJS.Process & { + killed: boolean + send: (opts: { + event: string + args: any[] + }) => void +})): { + send: (event: string, ...rawArgs: any[]) => void + on: (event: string, handler: Handler) => void + removeListener: (event: string, handler: Handler) => void +} { + const emitter = new EE() + + aProcess.on('message', (message) => { + 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, ...rawArgs) { + if (aProcess.killed) { + return + } + + const args = rawArgs.map((arg) => serializeArgument(arg)) + + return aProcess.send({ + event, + args, + }) + }, + + on (event, handler) { + function wrappedHandler (...rawArgs) { + return handler(...rawArgs.map((arg) => deserializeArgument(arg))) + } + handler.__realHandlerFunction__ = wrappedHandler + emitter.on(event, wrappedHandler) + }, + + removeListener (event, handler) { + if (handler.__realHandlerFunction__) { + emitter.removeListener(event, handler.__realHandlerFunction__) + } + }, + } +} + +export function wrapChildPromise (ipc, invoke, ids, args = []) { + return Promise.try(() => { + return invoke(ids.eventId, args) + }) + .then((value) => { + // undefined is coerced into null when sent over ipc, but we need + // to differentiate between them for 'task' event + if (value === undefined) { + value = UNDEFINED_SERIALIZED + } + + return ipc.send(`promise:fulfilled:${ids.invocationId}`, null, value) + }).catch((err) => { + return ipc.send(`promise:fulfilled:${ids.invocationId}`, serializeError(err)) + }) +} + +export function wrapParentPromise (ipc, eventId, callback) { + const invocationId = _.uniqueId('inv') + + return new Promise((resolve, reject) => { + const handler = function (err, value) { + ipc.removeListener(`promise:fulfilled:${invocationId}`, handler) + + if (err) { + debug('promise rejected for id %s %o', invocationId, ':', err.stack) + reject(_.extend(new Error(err.message), err)) + + return + } + + if (value === UNDEFINED_SERIALIZED) { + value = undefined + } + + debug(`promise resolved for id '${invocationId}' with value`, value) + + return resolve(value) + } + + ipc.on(`promise:fulfilled:${invocationId}`, handler) + + return callback(invocationId) + }) +} diff --git a/packages/server/lib/util/require_async_child.js b/packages/server/lib/util/require_async_child.ts similarity index 57% rename from packages/server/lib/util/require_async_child.js rename to packages/server/lib/util/require_async_child.ts index c9ad98be0a0c..1739f2d985c3 100644 --- a/packages/server/lib/util/require_async_child.js +++ b/packages/server/lib/util/require_async_child.ts @@ -1,10 +1,15 @@ -require('graceful-fs').gracefulify(require('fs')) -const debug = require('debug')('cypress:server:require_async:child') -const tsNodeUtil = require('./ts_node') -const util = require('../plugins/util') -const ipc = util.wrapIpc(process) +import { gracefulify } from 'graceful-fs' +import * as fs from 'fs' +import Debug from 'debug' +import * as tsNodeUtil from './ts_node' +import { wrapIpc, serializeError } from '../plugins/util' +import { suppress } from './suppress_warnings' -require('./suppress_warnings').suppress() +const ipc = wrapIpc(process as any) +const debug = Debug('cypress:server:require_async:child') + +gracefulify(fs) +suppress() const { file, projectRoot, loadErrorCode } = require('minimist')(process.argv.slice(2)) @@ -13,13 +18,13 @@ let tsRegistered = false run(ipc, file, projectRoot) /** - * runs and returns the passed `requiredFile` file in the ipc `load` event + * Runs and returns the passed `requiredFile` file in the ipc `load` event * @param {*} ipc Inter Process Comunication protocol - * @param {*} requiredFile the file we are trying to load - * @param {*} projectRoot the root of the typescript project (useful mainly for tsnode) + * @param {string} requiredFile the file we are trying to load + * @param {string} projectRoot the root of the typescript project (useful mainly for tsnode) * @returns */ -function run (ipc, requiredFile, projectRoot) { +function run (ipc, requiredFile: string, projectRoot: string) { debug('requiredFile:', requiredFile) debug('projectRoot:', projectRoot) if (!projectRoot) { @@ -35,17 +40,17 @@ function run (ipc, requiredFile, projectRoot) { } process.on('uncaughtException', (err) => { - debug('uncaught exception:', util.serializeError(err)) - ipc.send('error', util.serializeError(err)) + debug('uncaught exception:', serializeError(err)) + ipc.send('error', serializeError(err)) return false }) - process.on('unhandledRejection', (event) => { + process.on('unhandledRejection', (event?: {reason: string}) => { const err = (event && event.reason) || event - debug('unhandled rejection:', util.serializeError(err)) - ipc.send('error', util.serializeError(err)) + debug('unhandled rejection:', serializeError(err)) + ipc.send('error', serializeError(err)) return false }) @@ -53,7 +58,7 @@ function run (ipc, requiredFile, projectRoot) { ipc.on('load', () => { try { debug('try loading', requiredFile) - const exp = require(requiredFile) + const exp = require(requiredFile) as any const result = exp.default || exp From bb00ece0fcdffb31d9003e5d6420b7aec9e65a3c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 4 Aug 2021 11:50:21 -0500 Subject: [PATCH 150/204] make cache_helper typescript --- .../server/test/{cache_helper.js => cache_helper.ts} | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) rename packages/server/test/{cache_helper.js => cache_helper.ts} (55%) diff --git a/packages/server/test/cache_helper.js b/packages/server/test/cache_helper.ts similarity index 55% rename from packages/server/test/cache_helper.js rename to packages/server/test/cache_helper.ts index 1da4ed3e0d2b..1a20675c2dbb 100644 --- a/packages/server/test/cache_helper.js +++ b/packages/server/test/cache_helper.ts @@ -1,19 +1,14 @@ -const supportedConfigFiles = [ - 'cypress.json', - 'cypress.config.js', -] +import { CYPRESS_CONFIG_FILES } from '../lib/configFiles' /** * Since we load the cypress.json via `require`, * we need to clear the `require.cache` before/after some tests * to ensure we are not using a cached configuration file. */ -const clearCypressJsonCache = () => { +export function clearCypressJsonCache () { Object.keys(require.cache).forEach((key) => { - if (supportedConfigFiles.some((file) => key.includes(file))) { + if (CYPRESS_CONFIG_FILES.some((file) => key.includes(file))) { delete require.cache[key] } }) } - -module.exports = { clearCypressJsonCache } From 72b0ee99a2790cb607f0d3cc7e3614f506d2d230 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 4 Aug 2021 11:54:55 -0500 Subject: [PATCH 151/204] cmake desktop-gui more readable --- packages/desktop-gui/src/projects/projects-api.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/desktop-gui/src/projects/projects-api.js b/packages/desktop-gui/src/projects/projects-api.js index 4028d366832d..2a7dae4f5cc6 100644 --- a/packages/desktop-gui/src/projects/projects-api.js +++ b/packages/desktop-gui/src/projects/projects-api.js @@ -193,8 +193,6 @@ const openProject = (project) => { return ipc.openProject(project.path) .then((config = {}) => { - const hasE2Eproperty = !!config.e2e - // In this context we know we are in e2e. // The configuration in e2e has already been merged with the main. // It is not useful to display component/e2e fields explicitely. @@ -207,7 +205,7 @@ const openProject = (project) => { config = _.omit(config, 'e2e', 'component') - updateConfig(config, hasE2Eproperty) + updateConfig(config, !!config.e2e) const projectIdAndPath = { id: config.projectId, path: project.path } specsStore.setFilter(projectIdAndPath, localData.get(specsStore.getSpecsFilterId(projectIdAndPath))) From d5909d74778d9fb22561669a21f61eb2c695f5ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barth=C3=A9l=C3=A9my=20Ledoux?= Date: Wed, 4 Aug 2021 11:56:32 -0500 Subject: [PATCH 152/204] style: replace indexOf with includes Co-authored-by: Lachlan Miller --- packages/desktop-gui/src/lib/config-file-formatted.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop-gui/src/lib/config-file-formatted.jsx b/packages/desktop-gui/src/lib/config-file-formatted.jsx index 43d893dc1e0a..c7774ab50056 100644 --- a/packages/desktop-gui/src/lib/config-file-formatted.jsx +++ b/packages/desktop-gui/src/lib/config-file-formatted.jsx @@ -6,7 +6,7 @@ const configFileFormatted = (configFile) => { return <>cypress.json file (currently disabled by --config-file false) } - if (isUndefined(configFile) || ['cypress.json', 'cypress.config.ts', 'cypress.config.js'].indexOf(configFile) !== -1) { + if (isUndefined(configFile) || ['cypress.json', 'cypress.config.ts', 'cypress.config.js'].includes(configFile)) { return <>{configFile} file } From 5c1cb6db7ca70b3b54279f3bcb4f7d8ae3bd9946 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 4 Aug 2021 11:58:54 -0500 Subject: [PATCH 153/204] remove function for renderWarnings --- packages/runner-ct/src/app/RunnerCt.tsx | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/packages/runner-ct/src/app/RunnerCt.tsx b/packages/runner-ct/src/app/RunnerCt.tsx index 1d3bf4c66c68..a81eb39eaac6 100644 --- a/packages/runner-ct/src/app/RunnerCt.tsx +++ b/packages/runner-ct/src/app/RunnerCt.tsx @@ -177,21 +177,15 @@ const RunnerCt = namedObserver('RunnerCt', state.updateSpecListWidth(width) } - const renderWarnings = () => { - const { warnings } = state - - return warnings.map((warning, i) => ( - state.removeWarning(warning)} - /> - )) - } - return ( <> - {renderWarnings()} + {state.warnings.map((warning, i) => ( + state.removeWarning(warning)} + /> + ))} Date: Wed, 4 Aug 2021 12:00:57 -0500 Subject: [PATCH 154/204] fix indentation in warningMessage --- packages/runner-ct/src/app/WarningMessage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/runner-ct/src/app/WarningMessage.tsx b/packages/runner-ct/src/app/WarningMessage.tsx index 069c16ac9385..61f6c7d9938b 100644 --- a/packages/runner-ct/src/app/WarningMessage.tsx +++ b/packages/runner-ct/src/app/WarningMessage.tsx @@ -3,8 +3,8 @@ import { Warning } from '../lib/state' import styles from './WarningMessage.module.scss' interface WarningMessageProps{ - warning: Warning - onDismissWarning: () => void + warning: Warning + onDismissWarning: () => void } export const WarningMessage: React.FC = From 0022fe94389ca1aa331c8293a6b0bbdcc19fc3c2 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 4 Aug 2021 12:43:10 -0500 Subject: [PATCH 155/204] Revert "convert require_async_child to ts" This reverts commit 12a20f9ba3429717118b64ec011655d32ca3a40c. --- ..._async_child.ts => require_async_child.js} | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) rename packages/server/lib/util/{require_async_child.ts => require_async_child.js} (57%) diff --git a/packages/server/lib/util/require_async_child.ts b/packages/server/lib/util/require_async_child.js similarity index 57% rename from packages/server/lib/util/require_async_child.ts rename to packages/server/lib/util/require_async_child.js index 1739f2d985c3..c9ad98be0a0c 100644 --- a/packages/server/lib/util/require_async_child.ts +++ b/packages/server/lib/util/require_async_child.js @@ -1,15 +1,10 @@ -import { gracefulify } from 'graceful-fs' -import * as fs from 'fs' -import Debug from 'debug' -import * as tsNodeUtil from './ts_node' -import { wrapIpc, serializeError } from '../plugins/util' -import { suppress } from './suppress_warnings' +require('graceful-fs').gracefulify(require('fs')) +const debug = require('debug')('cypress:server:require_async:child') +const tsNodeUtil = require('./ts_node') +const util = require('../plugins/util') +const ipc = util.wrapIpc(process) -const ipc = wrapIpc(process as any) -const debug = Debug('cypress:server:require_async:child') - -gracefulify(fs) -suppress() +require('./suppress_warnings').suppress() const { file, projectRoot, loadErrorCode } = require('minimist')(process.argv.slice(2)) @@ -18,13 +13,13 @@ let tsRegistered = false run(ipc, file, projectRoot) /** - * Runs and returns the passed `requiredFile` file in the ipc `load` event + * runs and returns the passed `requiredFile` file in the ipc `load` event * @param {*} ipc Inter Process Comunication protocol - * @param {string} requiredFile the file we are trying to load - * @param {string} projectRoot the root of the typescript project (useful mainly for tsnode) + * @param {*} requiredFile the file we are trying to load + * @param {*} projectRoot the root of the typescript project (useful mainly for tsnode) * @returns */ -function run (ipc, requiredFile: string, projectRoot: string) { +function run (ipc, requiredFile, projectRoot) { debug('requiredFile:', requiredFile) debug('projectRoot:', projectRoot) if (!projectRoot) { @@ -40,17 +35,17 @@ function run (ipc, requiredFile: string, projectRoot: string) { } process.on('uncaughtException', (err) => { - debug('uncaught exception:', serializeError(err)) - ipc.send('error', serializeError(err)) + debug('uncaught exception:', util.serializeError(err)) + ipc.send('error', util.serializeError(err)) return false }) - process.on('unhandledRejection', (event?: {reason: string}) => { + process.on('unhandledRejection', (event) => { const err = (event && event.reason) || event - debug('unhandled rejection:', serializeError(err)) - ipc.send('error', serializeError(err)) + debug('unhandled rejection:', util.serializeError(err)) + ipc.send('error', util.serializeError(err)) return false }) @@ -58,7 +53,7 @@ function run (ipc, requiredFile: string, projectRoot: string) { ipc.on('load', () => { try { debug('try loading', requiredFile) - const exp = require(requiredFile) as any + const exp = require(requiredFile) const result = exp.default || exp From 2f7bffad2c9e42cde993e8e9fa9194d87cc4b495 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 4 Aug 2021 12:44:11 -0500 Subject: [PATCH 156/204] Revert "convert require_async_child to ts" This reverts commit 12a20f9ba3429717118b64ec011655d32ca3a40c. --- packages/server/lib/plugins/util.js | 152 +++++++++++++++++++++++++ packages/server/lib/plugins/util.ts | 165 ---------------------------- 2 files changed, 152 insertions(+), 165 deletions(-) create mode 100644 packages/server/lib/plugins/util.js delete mode 100644 packages/server/lib/plugins/util.ts diff --git a/packages/server/lib/plugins/util.js b/packages/server/lib/plugins/util.js new file mode 100644 index 000000000000..0b84df127ada --- /dev/null +++ b/packages/server/lib/plugins/util.js @@ -0,0 +1,152 @@ +const _ = require('lodash') +const EE = require('events') +const debug = require('debug')('cypress:server:plugins') +const Promise = require('bluebird') + +const UNDEFINED_SERIALIZED = '__cypress_undefined__' +const FUNCTION_SERIALIZED = '__cypress_function__' + +const serializeError = (err) => { + return _.pick(err, 'name', 'message', 'stack', 'code', 'annotated', 'type') +} + +function serializeArgument (arg) { + if (arg === null || arg === undefined) { + return null + } + + if (typeof arg === 'function') { + return FUNCTION_SERIALIZED + } + + if (typeof arg === 'object') { + if (_.isArray(arg)) { + return arg.map(serializeArgument) + } + + const serializedObject = {} + + for (const [key, val] of Object.entries(arg)) { + serializedObject[key] = serializeArgument(val) + } + + return serializedObject + } + + return arg +} + +function deserializeArgument (arg) { + if (arg === null || arg === undefined) { + return null + } + + if (arg === FUNCTION_SERIALIZED) { + return function () { + throw Error('this function is not meant to be used on it\'s own. It is the result of a deserialization and can be used to check the type of a returned object.') + } + } + + if (typeof arg === 'object') { + if (_.isArray(arg)) { + return arg.map((argElement) => deserializeArgument(argElement)) + } + + return Object.keys(arg).reduce(function (acc, key) { + acc[key] = deserializeArgument(arg[key]) + + return acc + }, {}) + } + + return arg +} + +module.exports = { + serializeError, + + wrapIpc (aProcess) { + const emitter = new EE() + + aProcess.on('message', (message) => { + 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, ...rawArgs) { + if (aProcess.killed) { + return + } + + const args = rawArgs.map((arg) => serializeArgument(arg)) + + return aProcess.send({ + event, + args, + }) + }, + + on (event, handler) { + function wrappedHandler (...rawArgs) { + return handler(...rawArgs.map((arg) => deserializeArgument(arg))) + } + handler.__realHandlerFunction__ = wrappedHandler + emitter.on(event, wrappedHandler) + }, + + removeListener (event, handler) { + emitter.removeListener(event, handler.__realHandlerFunction__) + }, + } + }, + + wrapChildPromise (ipc, invoke, ids, args = []) { + return Promise.try(() => { + return invoke(ids.eventId, args) + }) + .then((value) => { + // undefined is coerced into null when sent over ipc, but we need + // to differentiate between them for 'task' event + if (value === undefined) { + value = UNDEFINED_SERIALIZED + } + + return ipc.send(`promise:fulfilled:${ids.invocationId}`, null, value) + }).catch((err) => { + return ipc.send(`promise:fulfilled:${ids.invocationId}`, serializeError(err)) + }) + }, + + wrapParentPromise (ipc, eventId, callback) { + const invocationId = _.uniqueId('inv') + + return new Promise((resolve, reject) => { + const handler = function (err, value) { + ipc.removeListener(`promise:fulfilled:${invocationId}`, handler) + + if (err) { + debug('promise rejected for id %s %o', invocationId, ':', err.stack) + reject(_.extend(new Error(err.message), err)) + + return + } + + if (value === UNDEFINED_SERIALIZED) { + value = undefined + } + + debug(`promise resolved for id '${invocationId}' with value`, value) + + return resolve(value) + } + + ipc.on(`promise:fulfilled:${invocationId}`, handler) + + return callback(invocationId) + }) + }, +} diff --git a/packages/server/lib/plugins/util.ts b/packages/server/lib/plugins/util.ts deleted file mode 100644 index d439b0ca7e6b..000000000000 --- a/packages/server/lib/plugins/util.ts +++ /dev/null @@ -1,165 +0,0 @@ -import _ from 'lodash' -import EE from 'events' -import Debug from 'debug' -import Promise from 'bluebird' -import type { ChildProcess } from 'child_process' - -const debug = Debug('cypress:server:plugins') - -const UNDEFINED_SERIALIZED = '__cypress_undefined__' -const FUNCTION_SERIALIZED = '__cypress_function__' - -export function serializeError (err) { - return _.pick(err, 'name', 'message', 'stack', 'code', 'annotated', 'type') -} - -function serializeArgument (arg) { - if (arg === null || arg === undefined) { - return null - } - - if (typeof arg === 'function') { - return FUNCTION_SERIALIZED - } - - if (typeof arg === 'object') { - if (_.isArray(arg)) { - return arg.map(serializeArgument) - } - - const serializedObject = {} - - for (const [key, val] of Object.entries(arg)) { - serializedObject[key] = serializeArgument(val) - } - - return serializedObject - } - - return arg -} - -function deserializeArgument (arg) { - if (arg === null || arg === undefined) { - return null - } - - if (arg === FUNCTION_SERIALIZED) { - return function () { - throw Error('this function is not meant to be used on it\'s own. It is the result of a deserialization and can be used to check the type of a returned object.') - } - } - - if (typeof arg === 'object') { - if (_.isArray(arg)) { - return arg.map((argElement) => deserializeArgument(argElement)) - } - - return Object.keys(arg).reduce(function (acc, key) { - acc[key] = deserializeArgument(arg[key]) - - return acc - }, {}) - } - - return arg -} - -type Handler = {(...args: any[]): any, __realHandlerFunction__?: () => void} - -export function wrapIpc (aProcess: ChildProcess | (NodeJS.Process & { - killed: boolean - send: (opts: { - event: string - args: any[] - }) => void -})): { - send: (event: string, ...rawArgs: any[]) => void - on: (event: string, handler: Handler) => void - removeListener: (event: string, handler: Handler) => void -} { - const emitter = new EE() - - aProcess.on('message', (message) => { - 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, ...rawArgs) { - if (aProcess.killed) { - return - } - - const args = rawArgs.map((arg) => serializeArgument(arg)) - - return aProcess.send({ - event, - args, - }) - }, - - on (event, handler) { - function wrappedHandler (...rawArgs) { - return handler(...rawArgs.map((arg) => deserializeArgument(arg))) - } - handler.__realHandlerFunction__ = wrappedHandler - emitter.on(event, wrappedHandler) - }, - - removeListener (event, handler) { - if (handler.__realHandlerFunction__) { - emitter.removeListener(event, handler.__realHandlerFunction__) - } - }, - } -} - -export function wrapChildPromise (ipc, invoke, ids, args = []) { - return Promise.try(() => { - return invoke(ids.eventId, args) - }) - .then((value) => { - // undefined is coerced into null when sent over ipc, but we need - // to differentiate between them for 'task' event - if (value === undefined) { - value = UNDEFINED_SERIALIZED - } - - return ipc.send(`promise:fulfilled:${ids.invocationId}`, null, value) - }).catch((err) => { - return ipc.send(`promise:fulfilled:${ids.invocationId}`, serializeError(err)) - }) -} - -export function wrapParentPromise (ipc, eventId, callback) { - const invocationId = _.uniqueId('inv') - - return new Promise((resolve, reject) => { - const handler = function (err, value) { - ipc.removeListener(`promise:fulfilled:${invocationId}`, handler) - - if (err) { - debug('promise rejected for id %s %o', invocationId, ':', err.stack) - reject(_.extend(new Error(err.message), err)) - - return - } - - if (value === UNDEFINED_SERIALIZED) { - value = undefined - } - - debug(`promise resolved for id '${invocationId}' with value`, value) - - return resolve(value) - } - - ipc.on(`promise:fulfilled:${invocationId}`, handler) - - return callback(invocationId) - }) -} From 28065278b5e21c2d303c3872b504b60ca06cba59 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 4 Aug 2021 14:24:09 -0500 Subject: [PATCH 157/204] avoid trying to close a non open project --- packages/server/lib/project-base.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 2a13f4e97268..2c61d2f0f6ab 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -87,6 +87,7 @@ export class ProjectBase extends EE { public spec: Cypress.Cypress['spec'] | null private generatedProjectIdTimestamp: any projectRoot: string + isOpen: boolean = false constructor ({ projectRoot, @@ -309,6 +310,8 @@ export class ProjectBase extends EE { system: _.pick(sys, 'osName', 'osVersion'), } + this.isOpen = true + return runEvents.execute('before:run', cfg, beforeRunDetails) } @@ -339,6 +342,10 @@ export class ProjectBase extends EE { } async close () { + if (!this.isOpen) { + return Promise.resolve() + } + debug('closing project instance %s', this.projectRoot) this.spec = null @@ -358,6 +365,8 @@ export class ProjectBase extends EE { if (config.isTextTerminal || !config.experimentalInteractiveRunEvents) return + this.isOpen = false + return runEvents.execute('after:run', config) } @@ -513,9 +522,9 @@ export class ProjectBase extends EE { configFile, projectRoot, }: { - projectRoot: string - configFile?: string | boolean onSettingsChanged?: false | (() => void) + configFile?: string | boolean + projectRoot: string }) { // bail if we havent been told to // watch anything (like in run mode) @@ -527,6 +536,8 @@ export class ProjectBase extends EE { const obj = { onChange: () => { + debug('settings files have changed') + // dont fire change events if we generated // a project id less than 1 second ago if (this.generatedProjectIdTimestamp && @@ -534,6 +545,8 @@ export class ProjectBase extends EE { return } + debug('run onSettingsChanged') + // call our callback function // when settings change! onSettingsChanged() From a31dff307f52ccc4aa30a1a615727f83df4edfee Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 4 Aug 2021 22:04:40 -0500 Subject: [PATCH 158/204] highlight the path to the file in error --- packages/server/lib/errors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 075c6eac3f0d..c42dfb13663c 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -563,7 +563,7 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { msg = stripIndent`\ Error when loading the config file at the following location: - ${arg1} + \`${arg1}\` If you renamed the extension of your config file, restart the test runner.`.trim() From f735048d820e4d266c33b3d356c9f1d0702945dc Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 4 Aug 2021 22:05:02 -0500 Subject: [PATCH 159/204] show a better error when TS node fails --- .../server/lib/util/require_async_child.js | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/server/lib/util/require_async_child.js b/packages/server/lib/util/require_async_child.js index c9ad98be0a0c..cc8d69064ffa 100644 --- a/packages/server/lib/util/require_async_child.js +++ b/packages/server/lib/util/require_async_child.js @@ -61,16 +61,25 @@ function run (ipc, requiredFile, projectRoot) { debug('config %o', result) } catch (err) { - debug('failed to load requiredFile:\n%s', err.code, err.stack) - if (err.code === 'ENOENT' || err.code === 'MODULE_NOT_FOUND') { - ipc.send('load:error', err.code, requiredFile, err.stack) - - return + if (err.name === 'TSError') { + const cleanMessage = err.message + // beause of this https://github.com/TypeStrong/ts-node/issues/1418 + // we have to do this https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings/29497680 + // eslint-disable-next-line no-control-regex + .replace(/\u001b\[.*?m/g, '') + // replace the first line with better text (remove potentially misleading word TypeScript for example) + .replace(/^.*\n/g, 'Error compiling file\n') + + ipc.send('load:error', loadErrorCode, requiredFile, cleanMessage) + } else { + debug('failed to load file:%s\n%s: %s', requiredFile, err.code, err.message) + + if (err.code === 'ENOENT' || err.code === 'MODULE_NOT_FOUND') { + ipc.send('load:error', err.code, requiredFile, err) + } + + ipc.send('load:error', loadErrorCode, requiredFile, err.message) } - - ipc.send('load:error', loadErrorCode, requiredFile, err.stack) - - return } }) } From 9083997b46a78ddf3d9ae094d0de1ab8d80ebfea Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 5 Aug 2021 10:21:20 -0500 Subject: [PATCH 160/204] wire runner-ct to receive warnings --- packages/runner-shared/src/event-manager.js | 6 ++- packages/server-ct/index.ts | 51 +++++++++++++++++++++ packages/server/lib/errors.js | 2 +- packages/server/lib/project-base.ts | 10 ++-- packages/server/lib/socket-base.ts | 2 +- 5 files changed, 64 insertions(+), 7 deletions(-) diff --git a/packages/runner-shared/src/event-manager.js b/packages/runner-shared/src/event-manager.js index 823bface6784..8ca66fcd2dd5 100644 --- a/packages/runner-shared/src/event-manager.js +++ b/packages/runner-shared/src/event-manager.js @@ -99,6 +99,10 @@ export const eventManager = { rerun() }) + ws.on('project:warning', (warning) => { + state.addWarning(warning) + }) + ws.on('specs:changed', ({ specs, testingType }) => { // do not emit the event if e2e runner is not displaying an inline spec list. if (testingType === 'e2e' && state.useInlineSpecList === false) { @@ -125,7 +129,7 @@ export const eventManager = { _.each(socketToDriverEvents, (event) => { ws.on(event, (...args) => { - Cypress.emit(event, ...args) + Cypress && Cypress.emit(event, ...args) }) }) diff --git a/packages/server-ct/index.ts b/packages/server-ct/index.ts index 0ef5a2ba2192..edc0b7556ded 100644 --- a/packages/server-ct/index.ts +++ b/packages/server-ct/index.ts @@ -1,6 +1,7 @@ /* eslint-disable no-console */ import browsers from '@packages/server/lib/browsers' import openProject from '@packages/server/lib/open_project' +import * as random from '@packages/server/lib/util/random' import chalk from 'chalk' import human from 'human-interval' import _ from 'lodash' @@ -46,12 +47,62 @@ export const start = async (projectRoot: string, args: Record) => { specType: 'component', } + /** + * store warnings in this var + * before the runner is connected + */ + let warningStack: any[] = [] + let isRunnerConnected = false + /** + * as soon as the runner is connected, + * this variable contains a socket to talk + * to the runner-ct + */ + let cySocket + + /** + * Push project-wide warnings to the runner-ct + * @param warning + */ + const showWarning = (warning) => { + const warnObj = { ...warning } + + warnObj.message = warning.message + cySocket.toRunner('project:warning', warnObj) + } + const options = { browsers: [browser], + onWarning (warning) { + // when the runner is connected + // we can show the warinng without loosing them + if (isRunnerConnected) { + showWarning(warning) + } else { + // until the runner is connected stack + // the warnings + warningStack.push(warning) + } + }, + onConnect (id, socket, _cySocket) { + cySocket = _cySocket + if (warningStack.length) { + warningStack.forEach((warning) => { + showWarning(warning) + }) + + warningStack = [] + } + }, } debug('create project') + // runner-ct will need access to communications before + // we run a spec. assigning a socketId before we create + // when we open, the app already has a connection + args.config.socketId = random.id() + return openProject.create(projectRoot, args, options) .then((project) => { debug('launch project') diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index c42dfb13663c..767bced95630 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -1021,7 +1021,7 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { ` // TODO: update with vetted cypress language - case 'DEPREACTED_CYPRESS_JSON': + case 'DEPRECATED_CYPRESS_JSON': return stripIndent` in 9.0, cypress.json will not be supported anymore. please see the docs to migrate it to cypress.config.js diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 2c61d2f0f6ab..071a20d3e3c0 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -63,6 +63,7 @@ interface Options { onFocusTests?: WebSocketOptionsCallback onSpecChanged?: WebSocketOptionsCallback onSavedStateChanged?: WebSocketOptionsCallback + onConnect?: WebSocketOptionsCallback [key: string]: any } @@ -182,9 +183,9 @@ export class ProjectBase extends EE { let cfg = this.getConfig() if (typeof cfg.configFile === 'string' && /\.json$/.test(cfg.configFile)) { - errors.warning('DEPREACTED_CYPRESS_JSON', cfg.configFile) + errors.warning('DEPRECATED_CYPRESS_JSON', cfg.configFile) - this.options.onWarning(errors.get('DEPREACTED_CYPRESS_JSON', cfg.configFile)) + this.options.onWarning(errors.get('DEPRECATED_CYPRESS_JSON', cfg.configFile)) } process.chdir(this.projectRoot) @@ -268,6 +269,7 @@ export class ProjectBase extends EE { onReloadBrowser: this.options.onReloadBrowser, onFocusTests: this.options.onFocusTests, onSpecChanged: this.options.onSpecChanged, + onConnect: this.options.onConnect, }, { socketIoCookie: cfg.socketIoCookie, namespace: cfg.namespace, @@ -620,8 +622,8 @@ export class ProjectBase extends EE { this.emit('capture:video:frames', data) }, - onConnect: (id: string) => { - debug('socket:connected') + onConnect: (id: string, socket, cySocket) => { + options.onConnect?.(id, socket, cySocket) this.emit('socket:connected', id) }, diff --git a/packages/server/lib/socket-base.ts b/packages/server/lib/socket-base.ts index 77c1e174f77e..c8dcc310ad4a 100644 --- a/packages/server/lib/socket-base.ts +++ b/packages/server/lib/socket-base.ts @@ -288,7 +288,7 @@ export class SocketBase { }) socket.on('app:connect', (socketId) => { - return options.onConnect(socketId, socket) + return options.onConnect(socketId, socket, this) }) socket.on('set:runnables:and:maybe:record:tests', async (runnables, cb) => { From e89e875c7598488d1d6676a36eecf7989ab84a4c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 5 Aug 2021 10:29:17 -0500 Subject: [PATCH 161/204] implement the removeWarning function in state --- packages/runner-ct/src/lib/state.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/runner-ct/src/lib/state.ts b/packages/runner-ct/src/lib/state.ts index 9ff48872689e..461702197088 100644 --- a/packages/runner-ct/src/lib/state.ts +++ b/packages/runner-ct/src/lib/state.ts @@ -203,7 +203,9 @@ export default class State extends BaseStore { } @action removeWarning (warning: Warning) { - this.warnings + const index = this.warnings.findIndex((warn) => warn.message === warning.message) + + this.warnings.splice(index, 1) } @action setScreenshotting (screenshotting: boolean) { From 43f46c97ebe6cd02f036a66cc3e385911651b5f1 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 5 Aug 2021 10:30:29 -0500 Subject: [PATCH 162/204] finnsh the runner-ct message --- .../cypress/component/RunnerCt.spec.tsx | 38 +++++++++++++++++++ .../src/app/WarningMessage.module.scss | 9 +++++ packages/runner-ct/src/app/WarningMessage.tsx | 12 ++++-- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/packages/runner-ct/cypress/component/RunnerCt.spec.tsx b/packages/runner-ct/cypress/component/RunnerCt.spec.tsx index ccbaa8c41545..bb373bac242b 100644 --- a/packages/runner-ct/cypress/component/RunnerCt.spec.tsx +++ b/packages/runner-ct/cypress/component/RunnerCt.spec.tsx @@ -132,4 +132,42 @@ describe('RunnerCt', () => { cy.get(selectors.noSpecSelectedReporter).should('exist') }) }) + + context('show warning', () => { + it('show warning', () => { + const state = makeState({ spec: null }) + + state.addWarning({ message: 'this is a warning' }) + + mount( + , + ) + + cy.contains('this is a warning').should('exist') + }) + + it('dismiss warning', () => { + const state = makeState({ spec: null }) + + state.addWarning({ message: 'this is a warning' }) + + mount( + , + ) + + cy.contains('this is a warning').should('exist') + cy.get('[aria-label="dismiss"]').click() + cy.contains('this is a warning').should('not.exist') + }) + }) }) diff --git a/packages/runner-ct/src/app/WarningMessage.module.scss b/packages/runner-ct/src/app/WarningMessage.module.scss index b74d2c041bc9..6dc325a6ecb3 100644 --- a/packages/runner-ct/src/app/WarningMessage.module.scss +++ b/packages/runner-ct/src/app/WarningMessage.module.scss @@ -4,4 +4,13 @@ background-color:#fcf8e3; border-color:#faebcc; padding:15px 20px; + white-space: pre-wrap; +} + +.close{ + float: right; + background: none; + border: none; + padding: 10px; + cursor: pointer; } \ No newline at end of file diff --git a/packages/runner-ct/src/app/WarningMessage.tsx b/packages/runner-ct/src/app/WarningMessage.tsx index 61f6c7d9938b..fde262f63505 100644 --- a/packages/runner-ct/src/app/WarningMessage.tsx +++ b/packages/runner-ct/src/app/WarningMessage.tsx @@ -8,10 +8,14 @@ interface WarningMessageProps{ } export const WarningMessage: React.FC = - ({ warning }) => { + ({ warning, onDismissWarning }) => { return ( -
      -        {JSON.stringify(warning.message)}
      -      
      +
      + +

      Warning

      + {warning.message} +
      ) } From 5f04acc468bf3f182a413b0a571c5f4a70961376 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 5 Aug 2021 10:30:56 -0500 Subject: [PATCH 163/204] don't wait for runner-ct tu run a spec to connect --- packages/runner-ct/src/app/RunnerCt.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/runner-ct/src/app/RunnerCt.tsx b/packages/runner-ct/src/app/RunnerCt.tsx index a81eb39eaac6..2d37020b5073 100644 --- a/packages/runner-ct/src/app/RunnerCt.tsx +++ b/packages/runner-ct/src/app/RunnerCt.tsx @@ -169,6 +169,8 @@ const RunnerCt = namedObserver('RunnerCt', window.addEventListener('resize', onWindowResize) window.dispatchEvent(new Event('resize')) + eventManager.start(config) + return () => window.removeEventListener('resize', onWindowResize) // eslint-disable-next-line react-hooks/exhaustive-deps }, []) From 81d5b79afad86f4befb044b50e873199f50556a4 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 5 Aug 2021 10:37:12 -0500 Subject: [PATCH 164/204] add link to error --- packages/server/lib/errors.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 767bced95630..76907b8349c4 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -1023,8 +1023,10 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { // TODO: update with vetted cypress language case 'DEPRECATED_CYPRESS_JSON': return stripIndent` - in 9.0, cypress.json will not be supported anymore. - please see the docs to migrate it to cypress.config.js + In cypress 9.0, \`cypress.json\` will not be supported anymore. + please see the docs to migrate it to \`cypress.config.js\` + + Learn more: https://on.cypress.io/migrate-configjs ` case 'UNSUPPORTED_BROWSER_VERSION': From 3059088e92ae2a4ff9c748dbc252eeb9581381bc Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 5 Aug 2021 11:43:00 -0500 Subject: [PATCH 165/204] use markdown-renderer to render warning --- .../desktop-gui/src/lib/markdown-renderer.jsx | 48 +++++-------------- .../src/app/WarningMessage.module.scss | 7 ++- packages/runner-ct/src/app/WarningMessage.tsx | 14 +++++- packages/runner-shared/src/event-manager.js | 4 ++ packages/ui-components/package.json | 1 + packages/ui-components/src/index.tsx | 2 + .../ui-components/src/markdown-renderer.jsx | 34 +++++++++++++ 7 files changed, 71 insertions(+), 39 deletions(-) create mode 100644 packages/ui-components/src/markdown-renderer.jsx diff --git a/packages/desktop-gui/src/lib/markdown-renderer.jsx b/packages/desktop-gui/src/lib/markdown-renderer.jsx index 222ae2d639d3..b95b0c7109bf 100644 --- a/packages/desktop-gui/src/lib/markdown-renderer.jsx +++ b/packages/desktop-gui/src/lib/markdown-renderer.jsx @@ -1,44 +1,20 @@ import React from 'react' -import Markdown from 'markdown-it' +import { MarkdownRenderer as UIMarkdownRenderer } from '@packages/ui-components' import ipc from '../lib/ipc' -const md = new Markdown({ - html: true, - linkify: true, -}) +function clickHandler (e) { + if (e.target.href) { + e.preventDefault() -export default class MarkdownRenderer extends React.PureComponent { - componentDidMount () { - this.node.addEventListener('click', this._clickHandler) + return ipc.externalOpen(e.target.href) } +} - componentWillUnmount () { - this.node.removeEventListener('click', this._clickHandler) - } - - _clickHandler (e) { - if (e.target.href) { - e.preventDefault() - - return ipc.externalOpen(e.target.href) - } - } - - render () { - let renderFn = md.render - - if (this.props.noParagraphWrapper) { - // prevent markdown-it from wrapping the output in a

      tag - renderFn = md.renderInline - } - - return ( - this.node = node} - dangerouslySetInnerHTML={{ - __html: renderFn.call(md, this.props.markdown), - }}> - - ) - } +const MarkdownRenderer = ({ markdown, noParagraphWrapper }) => { + return ( + + ) } + +export default MarkdownRenderer diff --git a/packages/runner-ct/src/app/WarningMessage.module.scss b/packages/runner-ct/src/app/WarningMessage.module.scss index 6dc325a6ecb3..8f3fa1284070 100644 --- a/packages/runner-ct/src/app/WarningMessage.module.scss +++ b/packages/runner-ct/src/app/WarningMessage.module.scss @@ -11,6 +11,11 @@ float: right; background: none; border: none; - padding: 10px; + padding: 5px; cursor: pointer; +} + +.title{ + font-weight:bold; + margin-bottom:10px } \ No newline at end of file diff --git a/packages/runner-ct/src/app/WarningMessage.tsx b/packages/runner-ct/src/app/WarningMessage.tsx index fde262f63505..e9f3bf3f29f4 100644 --- a/packages/runner-ct/src/app/WarningMessage.tsx +++ b/packages/runner-ct/src/app/WarningMessage.tsx @@ -1,12 +1,22 @@ import * as React from 'react' +import { MarkdownRenderer } from '@packages/ui-components' import { Warning } from '../lib/state' import styles from './WarningMessage.module.scss' +import { eventManager } from '@packages/runner-shared' interface WarningMessageProps{ warning: Warning onDismissWarning: () => void } +function clickHandler (e) { + if (e.target.href) { + e.preventDefault() + + return eventManager.externalOpen(e.target.href) + } +} + export const WarningMessage: React.FC = ({ warning, onDismissWarning }) => { return ( @@ -14,8 +24,8 @@ export const WarningMessage: React.FC = -

      Warning

      - {warning.message} +

      Warning

      +
    ) } diff --git a/packages/runner-shared/src/event-manager.js b/packages/runner-shared/src/event-manager.js index 8ca66fcd2dd5..f5e63b339044 100644 --- a/packages/runner-shared/src/event-manager.js +++ b/packages/runner-shared/src/event-manager.js @@ -635,4 +635,8 @@ export const eventManager = { saveState (state) { ws.emit('save:app:state', state) }, + + externalOpen (href) { + ws.emit('external:open', href) + }, } diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 56bb5a0f9149..37c4081c28d3 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -31,6 +31,7 @@ "cypress-multi-reporters": "1.4.0", "file-loader": "4.3.0", "lodash": "4.17.19", + "markdown-it": "11.0.0", "mobx": "5.15.4", "mobx-react": "6.1.7", "prop-types": "15.7.2", diff --git a/packages/ui-components/src/index.tsx b/packages/ui-components/src/index.tsx index 09dc96e528fc..2d70046a50c6 100644 --- a/packages/ui-components/src/index.tsx +++ b/packages/ui-components/src/index.tsx @@ -10,6 +10,8 @@ export { default as EditorPickerModal } from './file-opener/editor-picker-modal' export { default as FileOpener } from './file-opener/file-opener' +export { default as MarkdownRenderer } from './markdown-renderer' + export * from './file-opener/file-model' export * from './select' diff --git a/packages/ui-components/src/markdown-renderer.jsx b/packages/ui-components/src/markdown-renderer.jsx new file mode 100644 index 000000000000..2740fe0c3069 --- /dev/null +++ b/packages/ui-components/src/markdown-renderer.jsx @@ -0,0 +1,34 @@ +import React from 'react' +import Markdown from 'markdown-it' + +const md = new Markdown({ + html: true, + linkify: true, +}) + +export default class MarkdownRenderer extends React.PureComponent { + componentDidMount () { + this.node.addEventListener('click', this.props.clickHandler) + } + + componentWillUnmount () { + this.node.removeEventListener('click', this.props.clickHandler) + } + + render () { + let renderFn = md.render + + if (this.props.noParagraphWrapper) { + // prevent markdown-it from wrapping the output in a

    tag + renderFn = md.renderInline + } + + return ( +

    this.node = node} + dangerouslySetInnerHTML={{ + __html: renderFn.call(md, this.props.markdown), + }}> +
    + ) + } +} From 9dca3fb92e28e7d70ed348fc942d1f2e2ba84072 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 5 Aug 2021 13:21:57 -0500 Subject: [PATCH 166/204] fix lint types --- cli/types/cypress-npm-api.d.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cli/types/cypress-npm-api.d.ts b/cli/types/cypress-npm-api.d.ts index 410a3e4d2cf2..864420fa26d9 100644 --- a/cli/types/cypress-npm-api.d.ts +++ b/cli/types/cypress-npm-api.d.ts @@ -363,27 +363,27 @@ declare module 'cypress' { }) ``` */ - export function run(options?: Partial): Promise + function run(options?: Partial): Promise /** * Opens Cypress GUI. Resolves with void when the * GUI is closed. * @see https://on.cypress.io/module-api#cypress-open */ - export function open(options?: Partial): Promise + function open(options?: Partial): Promise /** * Utility functions for parsing CLI arguments the same way * Cypress does */ - export const cli: CypressCommandLine.CypressCliParser + const cli: CypressCommandLine.CypressCliParser /** * Type helper to make writing `cypress.config.ts` easier */ - export function defineConfig(config: Cypress.ConfigOptions): Cypress.ConfigOptions + function defineConfig(config: Cypress.ConfigOptions): Cypress.ConfigOptions /** * Types for configuring cypress in cypress.config.ts */ - export type ConfigOptions = Cypress.ConfigOptions + type ConfigOptions = Cypress.ConfigOptions } From bd4dae6942ca27b5e41a47c0968894f033db9a2f Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 5 Aug 2021 13:56:33 -0500 Subject: [PATCH 167/204] test: fix unit tests for project-base --- packages/server/test/unit/project_spec.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/server/test/unit/project_spec.js b/packages/server/test/unit/project_spec.js index 7eb42780f913..3880db87cfd1 100644 --- a/packages/server/test/unit/project_spec.js +++ b/packages/server/test/unit/project_spec.js @@ -298,6 +298,7 @@ This option will not have an effect in Some-other-name. Tests that rely on web s onReloadBrowser: undefined, onFocusTests, onSpecChanged: undefined, + onConnect: undefined, }, { socketIoCookie: '__socket.io', namespace: '__cypress', @@ -506,6 +507,8 @@ This option will not have an effect in Some-other-name. Tests that rely on web s beforeEach(function () { this.project = new ProjectBase({ projectRoot: '/_test-output/path/to/project-e2e', testingType: 'e2e' }) + this.project.isOpen = true + this.project._server = { close () {} } sinon.stub(this.project, 'getConfig').returns(this.config) From 3d38d9f16d46847444441ec094ef4465ee297f2a Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 5 Aug 2021 14:31:07 -0500 Subject: [PATCH 168/204] fix files tests --- packages/server/test/unit/gui/files_spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/test/unit/gui/files_spec.ts b/packages/server/test/unit/gui/files_spec.ts index e3930eaff2a3..04955b582238 100644 --- a/packages/server/test/unit/gui/files_spec.ts +++ b/packages/server/test/unit/gui/files_spec.ts @@ -35,6 +35,7 @@ describe('gui/files', () => { this.err = new Error('foo') + sinon.stub(ProjectBase.prototype, 'initializeConfig').resolves() sinon.stub(ProjectBase.prototype, 'open').resolves() sinon.stub(ProjectBase.prototype, 'getConfig').returns(this.config) From fe91bd8b13d1862f68b0854e5002504fa6fe5f7f Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 5 Aug 2021 17:31:11 -0500 Subject: [PATCH 169/204] test: flag projects as open to close them properly --- packages/server/test/unit/project_spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/server/test/unit/project_spec.js b/packages/server/test/unit/project_spec.js index 3880db87cfd1..4767d810f407 100644 --- a/packages/server/test/unit/project_spec.js +++ b/packages/server/test/unit/project_spec.js @@ -71,6 +71,7 @@ describe('lib/project-base', () => { Fixtures.remove() if (this.project) { + this.project.isOpen = true this.project.close() } }) @@ -101,6 +102,7 @@ describe('lib/project-base', () => { expect(projectCt._cfg.viewportWidth).to.eq(500) expect(projectCt._cfg.baseUrl).to.eq('http://localhost:9999') expect(projectCt.startCtDevServer).to.have.beenCalled + projectCt.isOpen = true projectCt.close() }) }) From 58bf2f22b0a482d30287cceb9ad1ac0ace04cd24 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 5 Aug 2021 18:39:14 -0500 Subject: [PATCH 170/204] test: flag project as open so that it closes properly --- packages/server/test/integration/cypress_spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/test/integration/cypress_spec.js b/packages/server/test/integration/cypress_spec.js index 15d73f5fd83c..bf6bb641a5b8 100644 --- a/packages/server/test/integration/cypress_spec.js +++ b/packages/server/test/integration/cypress_spec.js @@ -173,6 +173,7 @@ describe('lib/cypress', () => { try { // make sure every project // we spawn is closed down + openProject.getProject().isOpen = true await openProject.close() } catch (e) { // ... From a3e068a239a99afad883bfa425bc82dccbf4816c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 6 Aug 2021 13:34:55 -0500 Subject: [PATCH 171/204] remove duplicated console message --- packages/server/lib/project-base.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index c1ea6a56f76e..53177f3cea65 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -183,8 +183,6 @@ export class ProjectBase extends EE { let cfg = this.getConfig() if (typeof cfg.configFile === 'string' && /\.json$/.test(cfg.configFile)) { - errors.warning('DEPRECATED_CYPRESS_JSON', cfg.configFile) - this.options.onWarning(errors.get('DEPRECATED_CYPRESS_JSON', cfg.configFile)) } From 6ae9d9d240ebf5284d6775fe7ddf1fc73f8b30a5 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 6 Aug 2021 13:41:07 -0500 Subject: [PATCH 172/204] us stripAnsi instead of regexp to clean up tserror --- cli/types/cypress.d.ts | 2 +- npm/react/cypress/plugins/index.js | 2 +- npm/vue/cypress.config.ts | 9 +++++---- packages/server/lib/util/require_async_child.js | 5 ++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index e67403ce033a..d8bf31a59a32 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2488,7 +2488,7 @@ declare namespace Cypress { cmdKey: boolean } - type PluginsFunction = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptionsMergedWithTestingTypes | undefined) + type PluginsFunction = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptionsMergedWithTestingTypes | undefined | void) type TestingTypeConfig = Omit & { setupNodeEvents?: PluginsFunction } type TestingTypeConfigComponent = TestingTypeConfig & { diff --git a/npm/react/cypress/plugins/index.js b/npm/react/cypress/plugins/index.js index 036b2a068ab5..b1fd5eef2958 100644 --- a/npm/react/cypress/plugins/index.js +++ b/npm/react/cypress/plugins/index.js @@ -62,7 +62,7 @@ const webpackConfig = { */ module.exports = (on, config) => { if (config.testingType !== 'component') { - throw Error(`This is a component testing project. testingType should be 'component'. Received ${config.testingType}`) + throw Error(`This is a component testing project. testingType should be 'component'. Received '${config.testingType}'`) } on('dev-server:start', (options) => { diff --git a/npm/vue/cypress.config.ts b/npm/vue/cypress.config.ts index 2f1c8ba51474..1abb05b92e0a 100644 --- a/npm/vue/cypress.config.ts +++ b/npm/vue/cypress.config.ts @@ -24,16 +24,17 @@ export default defineConfig({ return startDevServer({ options, webpackConfig }) }, setupNodeEvents (on, config) { - if (config.testingType !== 'component') { - throw Error(`This is a component testing project. testingType should be 'component'. Received ${config.testingType}`) - } - require('@cypress/code-coverage/task')(on, config) return config }, }, e2e: { + setupNodeEvents (on, config) { + if (config.testingType !== 'component') { + throw Error(`This is a component testing project. testingType should be 'component'. Received '${config.testingType}'`) + } + }, includeShadowDom: true, }, }) diff --git a/packages/server/lib/util/require_async_child.js b/packages/server/lib/util/require_async_child.js index cc8d69064ffa..2a2a8203ec30 100644 --- a/packages/server/lib/util/require_async_child.js +++ b/packages/server/lib/util/require_async_child.js @@ -1,4 +1,5 @@ require('graceful-fs').gracefulify(require('fs')) +const stripAnsi = require('strip-ansi') const debug = require('debug')('cypress:server:require_async:child') const tsNodeUtil = require('./ts_node') const util = require('../plugins/util') @@ -62,11 +63,9 @@ function run (ipc, requiredFile, projectRoot) { debug('config %o', result) } catch (err) { if (err.name === 'TSError') { - const cleanMessage = err.message // beause of this https://github.com/TypeStrong/ts-node/issues/1418 // we have to do this https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings/29497680 - // eslint-disable-next-line no-control-regex - .replace(/\u001b\[.*?m/g, '') + const cleanMessage = stripAnsi(err.message) // replace the first line with better text (remove potentially misleading word TypeScript for example) .replace(/^.*\n/g, 'Error compiling file\n') From 4b412050222b78df0e033f0d57718975dfd496bb Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 6 Aug 2021 14:15:50 -0500 Subject: [PATCH 173/204] avoid showing deprecation warning in every test --- packages/server/lib/project-base.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 53177f3cea65..047d5bf4c253 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -182,7 +182,7 @@ export class ProjectBase extends EE { let cfg = this.getConfig() - if (typeof cfg.configFile === 'string' && /\.json$/.test(cfg.configFile)) { + if (typeof cfg.configFile === 'string' && /\.json$/.test(cfg.configFile) && process.env.CYPRESS_INTERNAL_ENV !== 'test') { this.options.onWarning(errors.get('DEPRECATED_CYPRESS_JSON', cfg.configFile)) } From 70687b50624c549ef99f6b2f8688cd08939c5fd9 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 6 Aug 2021 14:17:06 -0500 Subject: [PATCH 174/204] add comment about why --- packages/server/lib/project-base.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 047d5bf4c253..078976319040 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -182,6 +182,7 @@ export class ProjectBase extends EE { let cfg = this.getConfig() + // don't show the deprecation warning in e2e tests to avoid polluting snapshots if (typeof cfg.configFile === 'string' && /\.json$/.test(cfg.configFile) && process.env.CYPRESS_INTERNAL_ENV !== 'test') { this.options.onWarning(errors.get('DEPRECATED_CYPRESS_JSON', cfg.configFile)) } From 503a44d109b3236d9ab9a30e1c05eccfb3314408 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 6 Aug 2021 14:18:47 -0500 Subject: [PATCH 175/204] style: better indentation --- packages/server/lib/project-base.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 078976319040..f5f861ac5ad2 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -182,8 +182,10 @@ export class ProjectBase extends EE { let cfg = this.getConfig() - // don't show the deprecation warning in e2e tests to avoid polluting snapshots - if (typeof cfg.configFile === 'string' && /\.json$/.test(cfg.configFile) && process.env.CYPRESS_INTERNAL_ENV !== 'test') { + if (typeof cfg.configFile === 'string' + && /\.json$/.test(cfg.configFile) + // don't show the deprecation warning in e2e tests to avoid polluting snapshots + && process.env.CYPRESS_INTERNAL_ENV !== 'test') { this.options.onWarning(errors.get('DEPRECATED_CYPRESS_JSON', cfg.configFile)) } From 4ad572afccc3a104c778f87d24d184868a1678ef Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 6 Aug 2021 14:39:31 -0500 Subject: [PATCH 176/204] fix runner-ct test --- packages/runner-ct/cypress/component/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/runner-ct/cypress/component/utils.ts b/packages/runner-ct/cypress/component/utils.ts index 3a0165ad9573..ebfaa9a833a3 100644 --- a/packages/runner-ct/cypress/component/utils.ts +++ b/packages/runner-ct/cypress/component/utils.ts @@ -19,4 +19,5 @@ export const getPort = (href: string) => { export class FakeEventManager { on (evt: string) {} + start () {} } From b89d3d7ea5e71679e925024c630b2d30cb2d6c02 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 6 Aug 2021 14:42:47 -0500 Subject: [PATCH 177/204] stop running unimplemented e2e tests --- circle.yml | 4 ---- npm/vue/package.json | 1 - 2 files changed, 5 deletions(-) diff --git a/circle.yml b/circle.yml index 9f41937774dd..563dcbbe2bfd 100644 --- a/circle.yml +++ b/circle.yml @@ -1346,10 +1346,6 @@ jobs: name: Run component tests command: yarn test:ci:ct working_directory: npm/vue - - run: - name: Run e2e tests - command: yarn test:ci:e2e - working_directory: npm/vue - store_test_results: path: npm/vue/test_results - store_artifacts: diff --git a/npm/vue/package.json b/npm/vue/package.json index 6b2bcca67a3b..139d4b6e5301 100644 --- a/npm/vue/package.json +++ b/npm/vue/package.json @@ -11,7 +11,6 @@ "typecheck": "vue-tsc --noEmit", "test": "yarn cy:run", "watch": "yarn build --watch --watch.exclude ./dist/**/*", - "test:ci:e2e": "node ../../scripts/cypress.js run --project ${PWD}", "test:ci:ct": "node ../../scripts/run-ct-examples.js --examplesList=./examples.env" }, "dependencies": { From bdfeb9af3d4c23608700f1b0e475106d755d7d91 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 6 Aug 2021 15:43:35 -0500 Subject: [PATCH 178/204] migrate all npm packages to cypress.config.js --- npm/angular/cypress.config.ts | 29 ++++ npm/angular/cypress.json | 11 -- .../cypress/plugins/cy-ts-preprocessor.ts | 134 ------------------ npm/angular/cypress/plugins/index.ts | 18 --- npm/angular/cypress/plugins/webpack.config.ts | 2 +- .../plugins/index.js => cypress.config.js} | 45 ++++-- npm/react/cypress.json | 15 -- 7 files changed, 60 insertions(+), 194 deletions(-) create mode 100644 npm/angular/cypress.config.ts delete mode 100644 npm/angular/cypress.json delete mode 100644 npm/angular/cypress/plugins/cy-ts-preprocessor.ts delete mode 100644 npm/angular/cypress/plugins/index.ts rename npm/react/{cypress/plugins/index.js => cypress.config.js} (64%) delete mode 100644 npm/react/cypress.json diff --git a/npm/angular/cypress.config.ts b/npm/angular/cypress.config.ts new file mode 100644 index 000000000000..9f1e8512617a --- /dev/null +++ b/npm/angular/cypress.config.ts @@ -0,0 +1,29 @@ +import { defineConfig } from 'cypress' +import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin' +import { startDevServer } from '@cypress/webpack-dev-server' +import webpackConfig from './cypress/plugins/webpack.config' + +export default defineConfig({ + experimentalFetchPolyfill: true, + fixturesFolder: false, + includeShadowDom: true, + fileServerFolder: 'src', + projectId: 'nf7zag', + component: { + componentFolder: '.', + testFiles: 'src/app/**/*cy-spec.ts', + setupDevServer (options) { + return startDevServer({ + options, + webpackConfig, + }) + }, + setupNodeEvents (on, config) { + addMatchImageSnapshotPlugin(on, config) + + require('@cypress/code-coverage/task')(on, config) + + return config + }, + }, +}) diff --git a/npm/angular/cypress.json b/npm/angular/cypress.json deleted file mode 100644 index ebe21a31c348..000000000000 --- a/npm/angular/cypress.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "experimentalFetchPolyfill": true, - "fixturesFolder": false, - "includeShadowDom": true, - "fileServerFolder": "src", - "projectId": "nf7zag", - "component": { - "componentFolder": "src/app", - "testFiles": "**/*cy-spec.ts" - } -} diff --git a/npm/angular/cypress/plugins/cy-ts-preprocessor.ts b/npm/angular/cypress/plugins/cy-ts-preprocessor.ts deleted file mode 100644 index def0d07f8b57..000000000000 --- a/npm/angular/cypress/plugins/cy-ts-preprocessor.ts +++ /dev/null @@ -1,134 +0,0 @@ -const wp = require('@cypress/webpack-preprocessor') -import root from './helpers' -import * as webpack from 'webpack' -import * as path from 'path' - -const webpackOptions = { - mode: 'development', - devtool: 'inline-source-map', - resolve: { - extensions: ['.ts', '.js'], - modules: [root('src'), 'node_modules'], - }, - module: { - rules: [ - { - enforce: 'pre', - test: /\.js$/, - loader: 'source-map-loader', - }, - { - test: /\.ts$/, - // loaders: ['ts-loader', 'angular2-template-loader'], - use: [ - { - loader: 'ts-loader', - options: { - transpileOnly: true, - }, - }, - { - loader: 'angular2-template-loader', - }, - ], - exclude: [/node_modules/, /test.ts/, /polyfills.ts/], - }, - { - test: /\.(js|ts)$/, - loader: 'istanbul-instrumenter-loader', - options: { esModules: true }, - enforce: 'post', - include: path.join(__dirname, '../..', 'src'), - exclude: [ - /\.(e2e|spec)\.ts$/, - /node_modules/, - /(ngfactory|ngstyle)\.js/, - ], - }, - { - // Mark files inside `@angular/core` as using SystemJS style dynamic imports. - // Removing this will cause deprecation warnings to appear. - test: /[\/\\]@angular[\/\\]core[\/\\].+\.js$/, - parser: { system: true }, - }, - { - test: /\.css$/, - loader: 'raw-loader', - }, - { - test: /(\.scss|\.sass)$/, - use: ['raw-loader', 'sass-loader'], - }, - { - test: /\.html$/, - loader: 'raw-loader', - exclude: [root('src/index.html')], - }, - { - enforce: 'post', - test: /\.(js|ts)$/, - loader: 'istanbul-instrumenter-loader', - query: { - esModules: true, - }, - include: root('src'), - exclude: [/\.(e2e|spec|cy-spec)\.ts$/, /node_modules/], - }, - { - test: /\.(jpe?g|png|gif)$/i, - loader: 'file-loader?name=assets/images/[name].[ext]', - }, - { - test: /\.(mp4|webm|ogg)$/i, - loader: 'file-loader?name=assets/videos/[name].[ext]', - }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: - 'file-loader?limit=10000&mimetype=image/svg+xml&name=assets/svgs/[name].[ext]', - }, - { - test: /\.eot(\?v=\d+.\d+.\d+)?$/, - loader: - 'file-loader?prefix=font/&limit=5000&name=assets/fonts/[name].[ext]', - }, - { - test: /\.(woff|woff2)$/, - loader: - 'file-loader?prefix=font/&limit=5000&name=assets/fonts/[name].[ext]', - }, - { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: - 'file-loader?limit=10000&mimetype=application/octet-stream&name=assets/fonts/[name].[ext]', - }, - ], - }, - plugins: [ - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'test'), - }), - new webpack.ContextReplacementPlugin( - /\@angular(\\|\/)core(\\|\/)f?esm5/, - path.join(__dirname, './src'), - ), - ], - performance: { - hints: false, - }, - node: { - global: true, - crypto: 'empty', - process: false, - module: false, - clearImmediate: false, - setImmediate: false, - fs: 'empty', - }, -} - -const options = { - webpackOptions, -} - -module.exports = wp(options) diff --git a/npm/angular/cypress/plugins/index.ts b/npm/angular/cypress/plugins/index.ts deleted file mode 100644 index 19515cec728b..000000000000 --- a/npm/angular/cypress/plugins/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin' -import * as webpackConfig from './webpack.config' - -module.exports = (on, config) => { - addMatchImageSnapshotPlugin(on, config) - const { startDevServer } = require('@cypress/webpack-dev-server') - - on('dev-server:start', (options) => { - return startDevServer({ - options, - webpackConfig, - }) - }) - - require('@cypress/code-coverage/task')(on, config) - - return config -} diff --git a/npm/angular/cypress/plugins/webpack.config.ts b/npm/angular/cypress/plugins/webpack.config.ts index ee6cfb9eb475..2b6b128d765b 100644 --- a/npm/angular/cypress/plugins/webpack.config.ts +++ b/npm/angular/cypress/plugins/webpack.config.ts @@ -1,7 +1,7 @@ import * as webpack from 'webpack' import * as path from 'path' -module.exports = { +export default { mode: 'development', devtool: 'inline-source-map', resolve: { diff --git a/npm/react/cypress/plugins/index.js b/npm/react/cypress.config.js similarity index 64% rename from npm/react/cypress/plugins/index.js rename to npm/react/cypress.config.js index b1fd5eef2958..3ade1cabe40a 100644 --- a/npm/react/cypress/plugins/index.js +++ b/npm/react/cypress.config.js @@ -1,7 +1,10 @@ // @ts-check + +const { defineConfig } = require('cypress') + const { startDevServer } = require('@cypress/webpack-dev-server') const path = require('path') -const babelConfig = require('../../babel.config.js') +const babelConfig = require('./babel.config') /** @type import("webpack").Configuration */ const webpackConfig = { @@ -57,17 +60,29 @@ const webpackConfig = { }, } -/** - * @type Cypress.PluginConfig - */ -module.exports = (on, config) => { - if (config.testingType !== 'component') { - throw Error(`This is a component testing project. testingType should be 'component'. Received '${config.testingType}'`) - } - - on('dev-server:start', (options) => { - return startDevServer({ options, webpackConfig, disableLazyCompilation: false }) - }) - - return config -} +module.exports = defineConfig({ + viewportWidth: 400, + viewportHeight: 400, + video: false, + projectId: 'z9dxah', + ignoreTestFiles: [ + '**/__snapshots__/*', + '**/__image_snapshots__/*', + ], + experimentalFetchPolyfill: true, + component: { + componentFolder: '.', + env: { + reactDevtools: true, + }, + testFiles: '**/*spec.{js,jsx,ts,tsx}', + setupDevServer (options) { + return startDevServer({ options, webpackConfig, disableLazyCompilation: false }) + }, + }, + e2e: { + setupNodeEvents () { + throw Error('This is a component testing project. Please use `cypress open-ct` to run it') + }, + }, +}) diff --git a/npm/react/cypress.json b/npm/react/cypress.json deleted file mode 100644 index a6b5db93e737..000000000000 --- a/npm/react/cypress.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "viewportWidth": 400, - "viewportHeight": 400, - "video": false, - "projectId": "z9dxah", - "testFiles": "**/*spec.{js,jsx,ts,tsx}", - "env": { - "reactDevtools": true - }, - "ignoreTestFiles": [ - "**/__snapshots__/*", - "**/__image_snapshots__/*" - ], - "experimentalFetchPolyfill": true -} \ No newline at end of file From d3c8c6d242678b4aed07e8c85033db02ae053ef7 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 6 Aug 2021 17:44:43 -0500 Subject: [PATCH 179/204] avoid creating pluginsFile when only dev server --- packages/server/lib/plugins/child/run_plugins.js | 10 +++++++--- packages/server/lib/plugins/index.js | 10 ++++++---- packages/server/lib/project-base.ts | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js index 6c946b38920a..536f2d3608fc 100644 --- a/packages/server/lib/plugins/child/run_plugins.js +++ b/packages/server/lib/plugins/child/run_plugins.js @@ -92,7 +92,11 @@ const load = (ipc, config, pluginsFile) => { register('dev-server:start', setupDevServerFunction) } - return plugins(register, config) + if (plugins) { + return plugins(register, config) + } + + return }) .tap(() => { if (!registeredEventsByName['file:preprocessor']) { @@ -211,7 +215,7 @@ const runPlugins = (ipc, pluginsFile, projectRoot, testingType, functionName) => plugins = getPluginsFunction(pluginsObject, testingType, functionName) if (testingType === 'component') { - setupDevServerFunction = pluginsObject[testingType].setupDevServer + setupDevServerFunction = pluginsObject.component.setupDevServer } debug('plugins %o', plugins) @@ -223,7 +227,7 @@ const runPlugins = (ipc, pluginsFile, projectRoot, testingType, functionName) => return } - if (typeof plugins !== 'function') { + if (typeof plugins !== 'function' && !setupDevServerFunction) { debug('not a function') ipc.send('load:error', 'PLUGINS_DIDNT_EXPORT_FUNCTION', pluginsFile, plugins) diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index 8c6fc3cafd62..905918febf8c 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -81,7 +81,9 @@ const init = (config, options) => { registeredEvents = {} - const pluginsFile = config.pluginsFile || path.join(__dirname, 'child', 'default_plugins_file.js') + const pluginsFile = config.pluginsFile === false + ? path.join(__dirname, 'child', 'default_plugins_file.js') + : config.pluginsFile const childIndexFilename = path.join(__dirname, 'child', 'index.js') const childArguments = ['--projectRoot', options.projectRoot] const childOptions = { @@ -94,14 +96,14 @@ const init = (config, options) => { const testingType = options.testingType || 'e2e' - if (typeof config[PLUGINS_FUNCTION_NAME] === 'function') { + if (/\.json$/.test(options.configFile)) { + childArguments.push('--file', pluginsFile) + } else { childArguments.push( '--testingType', testingType, '--functionName', PLUGINS_FUNCTION_NAME, '--file', options.configFile, ) - } else { - childArguments.push('--file', pluginsFile) } if (config.resolvedNodePath) { diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index f5f861ac5ad2..0509efdf978b 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -195,7 +195,7 @@ export class ProjectBase extends EE { // even when headlessly or else it will cause an error when // we try to load it and it's not there. We must do this here // else initialing the plugins will instantly fail. - if (cfg.pluginsFile && !cfg[this.testingType]?.setupNodeEvents) { + if (cfg.pluginsFile && (!cfg.configFile || cfg.configFile && !/\.json$/.test(cfg.configFile))) { debug('scaffolding with plugins file %s', cfg.pluginsFile) await scaffold.plugins(path.dirname(cfg.pluginsFile), cfg) From 8bcb7315b4f66d6783cafe93f79f8ddc21c54777 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 6 Aug 2021 17:59:52 -0500 Subject: [PATCH 180/204] fix: merge defaults / config validation reorder --- packages/server/lib/config.js | 38 +++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/server/lib/config.js b/packages/server/lib/config.js index 33475a599b1f..2bb4a8883148 100644 --- a/packages/server/lib/config.js +++ b/packages/server/lib/config.js @@ -90,7 +90,8 @@ const validateNoBreakingConfig = (cfg) => { * @param {boolean} bypassRootLimitations skip checks related to position when we are working with merged configs * @returns */ -function validate (cfg, onErr, bypassRootLimitations = false) { +function validate (cfg, onErr, + { bypassRootLimitations } = { bypassRootLimitations: false }) { return _.each(cfg, (value, key) => { const validationFn = validationRules[key] @@ -262,13 +263,7 @@ module.exports = { config.projectRoot = projectRoot config.projectName = projectName - const testingType = isComponentTesting(options) ? 'component' : 'e2e' - return this.mergeDefaults(config, options).then((configObject = {}) => { - if (testingType in configObject) { - configObject = { ...configObject, ...configObject[testingType] } - } - debug('merged config is %o', config) return configObject @@ -276,6 +271,7 @@ module.exports = { }, mergeDefaults (config = {}, options = {}) { + // 1 - merge the CLI options const resolved = {} config.rawJson = _.cloneDeep(config) @@ -292,6 +288,7 @@ module.exports = { config[key] = val }).value() + // 2 - cleanup baseUrl & env let url = config.baseUrl if (url) { @@ -301,8 +298,6 @@ module.exports = { config.baseUrl = url.replace(/\/\/+$/, '/') } - _.defaults(config, defaultValues) - // split out our own app wide env from user env variables // and delete envFile config.env = this.parseEnv(config, options.env, resolved) @@ -325,6 +320,22 @@ module.exports = { config.numTestsKeptInMemory = 0 } + // 3 - validate config again here so that we catch + // configuration errors coming from the CLI overrides + // or env var overrides + validate(config, (errMsg) => { + return errors.throw('CONFIG_VALIDATION_ERROR', errMsg) + }) + + const testingType = isComponentTesting(options) ? 'component' : 'e2e' + + if (testingType in config) { + config = { ...config, ...config[testingType] } + } + + // 3 - merge the defaults + _.defaults(config, defaultValues) + config = this.setResolvedConfigValues(config, defaultValues, resolved) if (config.port) { @@ -335,13 +346,6 @@ module.exports = { config = this.setParentTestsPaths(config) - // validate config again here so that we catch - // configuration errors coming from the CLI overrides - // or env var overrides - validate(config, (errMsg) => { - return errors.throw('CONFIG_VALIDATION_ERROR', errMsg) - }) - validateNoBreakingConfig(config) return this.setSupportFileAndFolder(config) @@ -394,7 +398,7 @@ module.exports = { } return errors.throw('CONFIG_VALIDATION_ERROR', errMsg) - }, true) + }, { bypassRootLimitations: true }) let originalResolvedBrowsers = cfg && cfg.resolved && cfg.resolved.browsers && R.clone(cfg.resolved.browsers) From c8a38e33edb5e5edbc902ded2919e0b86d62c7c9 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 6 Aug 2021 18:35:02 -0500 Subject: [PATCH 181/204] fix config refactor --- npm/angular/cypress.config.ts | 4 ++-- npm/react/cypress.config.js | 3 +-- packages/server/lib/config.js | 24 +++++++++++++--------- packages/server/test/unit/config_spec.js | 26 ++++++++++++------------ 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/npm/angular/cypress.config.ts b/npm/angular/cypress.config.ts index 9f1e8512617a..ab2a159809ce 100644 --- a/npm/angular/cypress.config.ts +++ b/npm/angular/cypress.config.ts @@ -10,8 +10,8 @@ export default defineConfig({ fileServerFolder: 'src', projectId: 'nf7zag', component: { - componentFolder: '.', - testFiles: 'src/app/**/*cy-spec.ts', + componentFolder: 'src', + testFiles: '**/*cy-spec.ts', setupDevServer (options) { return startDevServer({ options, diff --git a/npm/react/cypress.config.js b/npm/react/cypress.config.js index 3ade1cabe40a..1de7d79560fc 100644 --- a/npm/react/cypress.config.js +++ b/npm/react/cypress.config.js @@ -71,11 +71,10 @@ module.exports = defineConfig({ ], experimentalFetchPolyfill: true, component: { - componentFolder: '.', + testFiles: '**/*spec.{js,jsx,ts,tsx}', env: { reactDevtools: true, }, - testFiles: '**/*spec.{js,jsx,ts,tsx}', setupDevServer (options) { return startDevServer({ options, webpackConfig, disableLazyCompilation: false }) }, diff --git a/packages/server/lib/config.js b/packages/server/lib/config.js index 2bb4a8883148..ac04896de20f 100644 --- a/packages/server/lib/config.js +++ b/packages/server/lib/config.js @@ -263,15 +263,14 @@ module.exports = { config.projectRoot = projectRoot config.projectName = projectName - return this.mergeDefaults(config, options).then((configObject = {}) => { - debug('merged config is %o', config) + return this.mergeAllConfigs(config, options).then((configObject = {}) => { + debug('merged config is %o', configObject) return configObject }) }, - mergeDefaults (config = {}, options = {}) { - // 1 - merge the CLI options + mergeCLIOptions (config, options) { const resolved = {} config.rawJson = _.cloneDeep(config) @@ -288,7 +287,10 @@ module.exports = { config[key] = val }).value() - // 2 - cleanup baseUrl & env + return resolved + }, + + cleanUpConfig (config, options, resolved) { let url = config.baseUrl if (url) { @@ -319,10 +321,13 @@ module.exports = { // to zero config.numTestsKeptInMemory = 0 } + }, + + mergeAllConfigs (config = {}, options = {}) { + const resolved = this.mergeCLIOptions(config, options) + + this.cleanUpConfig(config, options, resolved) - // 3 - validate config again here so that we catch - // configuration errors coming from the CLI overrides - // or env var overrides validate(config, (errMsg) => { return errors.throw('CONFIG_VALIDATION_ERROR', errMsg) }) @@ -370,7 +375,7 @@ module.exports = { return _.each(obj, (val, key) => { if (_.isObject(val) && !_.isArray(val) && resolvedObj[key]) { // recurse setting overrides - // inside of this nested objected + // inside of this nested object return this.setPluginResolvedOn(resolvedObj[key], val) } @@ -808,5 +813,4 @@ module.exports = { getNameFromRoot (root = '') { return path.basename(root) }, - } diff --git a/packages/server/test/unit/config_spec.js b/packages/server/test/unit/config_spec.js index 2ac5ee496eac..bacb34a84447 100644 --- a/packages/server/test/unit/config_spec.js +++ b/packages/server/test/unit/config_spec.js @@ -31,7 +31,7 @@ describe('lib/config', () => { } const options = {} - config.mergeDefaults(cfg, options) + config.mergeAllConfigs(cfg, options) expect(errors.throw).have.been.calledOnce }) @@ -44,7 +44,7 @@ describe('lib/config', () => { } const options = {} - config.mergeDefaults(cfg, options) + config.mergeAllConfigs(cfg, options) expect(errors.throw).not.to.be.called }) @@ -1153,12 +1153,12 @@ describe('lib/config', () => { }) }) - context('.mergeDefaults', () => { + context('.mergeAllConfigs', () => { beforeEach(function () { this.defaults = (prop, value, cfg = {}, options = {}) => { cfg.projectRoot = '/foo/bar/' - return config.mergeDefaults(cfg, options) + return config.mergeAllConfigs(cfg, options) .then(R.prop(prop)) .then((result) => { expect(result).to.deep.eq(value) @@ -1359,35 +1359,35 @@ describe('lib/config', () => { }) it('resets numTestsKeptInMemory to 0 when runMode', () => { - return config.mergeDefaults({ projectRoot: '/foo/bar/' }, { isTextTerminal: true }) + return config.mergeAllConfigs({ projectRoot: '/foo/bar/' }, { isTextTerminal: true }) .then((cfg) => { expect(cfg.numTestsKeptInMemory).to.eq(0) }) }) it('resets watchForFileChanges to false when runMode', () => { - return config.mergeDefaults({ projectRoot: '/foo/bar/' }, { isTextTerminal: true }) + return config.mergeAllConfigs({ projectRoot: '/foo/bar/' }, { isTextTerminal: true }) .then((cfg) => { expect(cfg.watchForFileChanges).to.be.false }) }) it('can override morgan in options', () => { - return config.mergeDefaults({ projectRoot: '/foo/bar/' }, { morgan: false }) + return config.mergeAllConfigs({ projectRoot: '/foo/bar/' }, { morgan: false }) .then((cfg) => { expect(cfg.morgan).to.be.false }) }) it('can override isTextTerminal in options', () => { - return config.mergeDefaults({ projectRoot: '/foo/bar/' }, { isTextTerminal: true }) + return config.mergeAllConfigs({ projectRoot: '/foo/bar/' }, { isTextTerminal: true }) .then((cfg) => { expect(cfg.isTextTerminal).to.be.true }) }) it('can override socketId in options', () => { - return config.mergeDefaults({ projectRoot: '/foo/bar/' }, { socketId: 1234 }) + return config.mergeAllConfigs({ projectRoot: '/foo/bar/' }, { socketId: 1234 }) .then((cfg) => { expect(cfg.socketId).to.eq(1234) }) @@ -1406,7 +1406,7 @@ describe('lib/config', () => { }, } - return config.mergeDefaults(obj) + return config.mergeAllConfigs(obj) .then((cfg) => { expect(cfg.env).to.deep.eq({ foo: 'bar', @@ -1437,7 +1437,7 @@ describe('lib/config', () => { }, } - return config.mergeDefaults(obj, options) + return config.mergeAllConfigs(obj, options) .then((cfg) => { expect(cfg.env).to.deep.eq({ host: 'localhost', @@ -1511,7 +1511,7 @@ describe('lib/config', () => { port: 1234, } - return config.mergeDefaults(obj, options) + return config.mergeAllConfigs(obj, options) .then((cfg) => { expect(cfg.resolved).to.deep.eq({ animationDistanceThreshold: { value: 5, from: 'default' }, @@ -1596,7 +1596,7 @@ describe('lib/config', () => { }, } - return config.mergeDefaults(obj, options) + return config.mergeAllConfigs(obj, options) .then((cfg) => { expect(cfg.resolved).to.deep.eq({ animationDistanceThreshold: { value: 5, from: 'default' }, From 9e6ca6ea571026ffcc0dd7c67eba5b0d1610446b Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 7 Aug 2021 18:58:28 -0500 Subject: [PATCH 182/204] fix: create a pluginsFile when config is json --- packages/server/__snapshots__/3_new_project_spec.js | 2 +- packages/server/lib/project-base.ts | 2 +- packages/server/test/e2e/3_new_project_spec.js | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/server/__snapshots__/3_new_project_spec.js b/packages/server/__snapshots__/3_new_project_spec.js index 1ea1143e1544..005b892a244d 100644 --- a/packages/server/__snapshots__/3_new_project_spec.js +++ b/packages/server/__snapshots__/3_new_project_spec.js @@ -1,4 +1,4 @@ -exports['e2e new project passes 1'] = ` +exports['e2e new project creates a sample supportFile & a sample pluginsFile 1'] = ` ==================================================================================================== diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 0509efdf978b..6dc051a52fb3 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -195,7 +195,7 @@ export class ProjectBase extends EE { // even when headlessly or else it will cause an error when // we try to load it and it's not there. We must do this here // else initialing the plugins will instantly fail. - if (cfg.pluginsFile && (!cfg.configFile || cfg.configFile && !/\.json$/.test(cfg.configFile))) { + if (cfg.pluginsFile && (!cfg.configFile || /\.json$/.test(cfg.configFile))) { debug('scaffolding with plugins file %s', cfg.pluginsFile) await scaffold.plugins(path.dirname(cfg.pluginsFile), cfg) diff --git a/packages/server/test/e2e/3_new_project_spec.js b/packages/server/test/e2e/3_new_project_spec.js index 4d577f7eab62..bd6be97f6c24 100644 --- a/packages/server/test/e2e/3_new_project_spec.js +++ b/packages/server/test/e2e/3_new_project_spec.js @@ -5,11 +5,12 @@ const { fs } = require('../../lib/util/fs') const noScaffoldingPath = Fixtures.projectPath('no-scaffolding') const supportPath = path.join(noScaffoldingPath, 'cypress', 'support') +const pluginsPath = path.join(noScaffoldingPath, 'cypress', 'plugins') describe('e2e new project', () => { e2e.setup() - it('passes', function () { + it('creates a sample supportFile & a sample pluginsFile', function () { return fs .statAsync(supportPath) .then(() => { @@ -21,7 +22,7 @@ describe('e2e new project', () => { snapshot: true, }) .then(() => { - return fs.statAsync(supportPath) + return Promise.all([fs.statAsync(supportPath), fs.statAsync(pluginsPath)]) }) }) }) From 6805f1ac8238d233c76dc2f948b3293b04e6b714 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 7 Aug 2021 19:05:26 -0500 Subject: [PATCH 183/204] fix: error when using new syntax (no more dev-server event) --- packages/server/lib/errors.js | 11 +++++++++++ packages/server/lib/plugins/dev-server.js | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 76907b8349c4..f329c821a8ca 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -1001,6 +1001,17 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { https://on.cypress.io/component-testing ` // TODO: update with vetted cypress language + case 'CT_NO_DEV_START_FUNCTION': + return stripIndent`\ + To run component-testing, cypress needs the \`setupDevServer\` function to be implemented. + + Add a \`setupDevServer()\` in the component object of the ${arg1} file. + + Learn how to set up component testing: + + https://on.cypress.io/component-testing + ` + // TODO: update with vetted cypress language case 'CONFLICT_PLUGINSFILE_CONFIGJS': return stripIndent` \`pluginsFile\` cannot be set in a \`${arg1}\` file. diff --git a/packages/server/lib/plugins/dev-server.js b/packages/server/lib/plugins/dev-server.js index 385e1b1bb89e..8eac100f48f5 100644 --- a/packages/server/lib/plugins/dev-server.js +++ b/packages/server/lib/plugins/dev-server.js @@ -33,6 +33,10 @@ const API = { start ({ specs, config }) { if (!plugins.has('dev-server:start')) { + if (config.configFile && !/\.json$/.test(config.configFile)) { + return errors.throw('CT_NO_DEV_START_FUNCTION', config.configFile) + } + return errors.throw('CT_NO_DEV_START_EVENT', config.pluginsFile) } From dd555a7d650348f900f0e2b428f9d2397c17870a Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 7 Aug 2021 19:47:52 -0500 Subject: [PATCH 184/204] test: http server tests rely on undefined pluginsFile --- packages/server/lib/plugins/index.js | 6 +++--- packages/server/test/integration/http_requests_spec.js | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index 905918febf8c..87dca069a705 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -81,9 +81,9 @@ const init = (config, options) => { registeredEvents = {} - const pluginsFile = config.pluginsFile === false - ? path.join(__dirname, 'child', 'default_plugins_file.js') - : config.pluginsFile + const pluginsFile = typeof config.pluginsFile === 'string' + ? config.pluginsFile + : path.join(__dirname, 'child', 'default_plugins_file.js') const childIndexFilename = path.join(__dirname, 'child', 'index.js') const childArguments = ['--projectRoot', options.projectRoot] const childOptions = { diff --git a/packages/server/test/integration/http_requests_spec.js b/packages/server/test/integration/http_requests_spec.js index dd253120d333..44293c4ff30e 100644 --- a/packages/server/test/integration/http_requests_spec.js +++ b/packages/server/test/integration/http_requests_spec.js @@ -167,6 +167,7 @@ describe('Routes', () => { pluginsModule.init(cfg, { projectRoot: cfg.projectRoot, + configFile: 'cypress.json', }), ]) } From 4dba7791870b869c97dd26767dd2f36f04042399 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 7 Aug 2021 20:10:23 -0500 Subject: [PATCH 185/204] test: use the component testing defaults for snapshots --- .../snapshots/All Specs/clicked.snap.png | Bin 9035 -> 0 bytes .../cypress/snapshots/All Specs/init.snap.png | Bin 7643 -> 0 bytes .../clicked.snap.png | Bin 9708 -> 9339 bytes .../init.snap.png | Bin 8566 -> 7878 bytes .../clicked.snap.png | Bin 9334 -> 0 bytes .../init.snap.png | Bin 7878 -> 0 bytes 6 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 npm/angular/cypress/snapshots/All Specs/clicked.snap.png delete mode 100644 npm/angular/cypress/snapshots/All Specs/init.snap.png delete mode 100644 npm/angular/cypress/snapshots/image-snapshot/image-snapshot.component.cy-spec.ts/clicked.snap.png delete mode 100644 npm/angular/cypress/snapshots/image-snapshot/image-snapshot.component.cy-spec.ts/init.snap.png diff --git a/npm/angular/cypress/snapshots/All Specs/clicked.snap.png b/npm/angular/cypress/snapshots/All Specs/clicked.snap.png deleted file mode 100644 index a15665c967cf34dc038faf58b950a7cf85a3a157..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9035 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 Y<#w>p=gq?($?U_4$=>}eqW%2e0O$Ek{Qv*} diff --git a/npm/angular/cypress/snapshots/All Specs/init.snap.png b/npm/angular/cypress/snapshots/All Specs/init.snap.png deleted file mode 100644 index 36afaac5a836d3fcd5ccf00c88e91566d6759e69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7643 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 R7cE*uRn$<3KX~fOe*v4)?LYtk literal 9708 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 diff --git a/npm/angular/cypress/snapshots/app/image-snapshot/image-snapshot.component.cy-spec.ts/init.snap.png b/npm/angular/cypress/snapshots/app/image-snapshot/image-snapshot.component.cy-spec.ts/init.snap.png index d981051ae056db26050f6069c35f7e6ae0902303..c88a40c59456ab386c85e0c0540eb32d19be62a8 100644 GIT binary patch literal 7878 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 UC%57Mr*~m5>s-pccsuBS0bNTeI{*Lx literal 8566 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 tdMl~7l6ottKlqR6Z!zeH~Z{{r@N{8s<~ diff --git a/npm/angular/cypress/snapshots/image-snapshot/image-snapshot.component.cy-spec.ts/clicked.snap.png b/npm/angular/cypress/snapshots/image-snapshot/image-snapshot.component.cy-spec.ts/clicked.snap.png deleted file mode 100644 index 645a6fde74696d717de66bac8a3ca245c0138169..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9334 zcmeHNX*`wd-fnI;?^c6C6f2~nP%>8}i;|%di3VdtBJ+^!k}30)F|)`JWu^g{B154J zA!9OUTzId$bAG?`<-8xx`<{>cgZ*n`JZG zr4!`qk5%|i<>z+SB}=GcG^rEkEtmd$W8>Fo+BrSi+xy8+OPbr`u#VqkQbNf^x6O5e zN3Wcr#LniLU6FmXHOEYo?Wo16X7!7=g!HNV^y|+uGHF+;$Q8$2VA2-$=ix6JU${{n zTwhv!eJU%pu;xAcujbOTu{q7EGCnbuE2%W4iDf?2eb!59K3fa!QE9Fk$9<@$cz4r$ zq8PlWwAyv}wZ8x8*S4kU*03lgZnbE=%P`x>By@+Gc-}Qk#A57*UqAq#-K<%g-PDx@ zhc6kroA86Yp@Lzsq*dg&|VSaArsjRH546eyWo3+tDe_TzC(YWB(c2_wXZH-UNB2!($J-fNd z+GJJ!T#I4(BKvvEfyDTrBL>^WtS7#O+YEI$EKWJ-H^10>PVCp#@!lHUs-VO4U#SkU zDybUlnU8s?y}ao?{k46ew9K2`s>?(0Ql9z2ojZ5ZYZmF7EZXe^kKAn7J=z~1ac8(- zitk40`nvl1{>D^RhlTNo#LHPc?;dVoPrvbg<4|_jMh&a(ZLbWQPhwFwO5LB*d^v0* z1luf~M27QyyDI~IeSIazdp_(GF+YGc4>o5wn^p@shZq+IuUxlXK|$eolxc5ui_LJs z`J{^;#dE_&H3^E*mOq|nadC6AadJ8{8RvV480EUi`g4y~iwu8E)s$GZiKWVElXzKO ztXz3bO}xY6Lgvj+uEi-=@^*+?9;%N!djmHNwV&;u_|fP&)S9Qu!%+UXJf1^dUVh_;5JBC+rgSY`UEgD4W$ZL&lj6-XJ_{O=%K#Iu0|#Of z5*`&ZXxhHPLi?K1+2cd+tUY-7RZL{$z5IO9+`B)dabiNl`QzEW;Wj~h+H19i%?Hca zJw@ND>V}o{)kf<#By8?_@2T7O@hR2(=a-V$K#hv&(JuYksFh*DcRK?v8(kawUT#Yt zeNUYnZa-HUaL{VH!&(1h;Lj{zjC~MtXv?EfZoS@r&;a%yxC9tG`+gT6d!KQZCp6R z5vXY|l+|j^bm7_gYab&|Fa61Mc%pA#Quj zQ+$?dW@g5HC;vk!PZqHtF`J{^cYl8I_mvlR5x1L7yWMdT2ofb=z5esdO9B15zxK)n z9Mb!^+j^q!&3KK(xvo-=nb{wyLbH9*LgQ8ZwC~;Bwb>>&Hu{BxRQ^aUs$R`%d%V+) zw=fbL3;3oPwF*;yG^Xl)PF8J5RCWdt+-bAw9c@rPSNr_3;KTJh-;z&YGO%In>2`DG zSU}F~K$6fvLiBW{=E6yH^P}A!%tAh70|EozcL!=#$DIwy{&s)e^gvSTjw3g>?h-cp z_JBcSw}eCl-^-hy*i62ApZ-k>^glo>-8h*&wVC?L7gN;Bx11Aw^RiJx4vb>(Ik^L8 z7=_ue9V_QNpphR$hNyjEGK*?d@ay8u&&lO4v@2yqvC3ri9H00I`zI`7)?Nw9@f+u7 ze??bekuuk=?O~U8ze+G+JvDR|Teef!jEi5l`ukPfj&>c0%rtHfmjXa>mfBxm-8%nV z&0k7Khl2p3zrWuWV5c`a%rltwR9yoD)k)HyXfKtL3k2bUSHD_?vh)pr8 zXxbfluJ*F?IR3+y4rnkQH7tud<;Im^*001Up!b?SGfvAiYJJi^KV*@7IqR`*xYfg? zlw4P-ETh&0aG=xbO*$YBKTuFF&MgjHUSKn&)brGt<$6=vwTYi!1D#o{AD!nesqo)V zbKfNrF!}SVZ&`Wywf?$T!>_MAq8|&pG9J0=RQISnB!l)ve1Fp(dp}$&nnr z^~FmjR*8(r+qWkvoT0au%}ou*$He^g{BqVnXRI3sNNT=u7A-!HVAtHVuKZDcNc zp4BFMeQKzc%9Gp22EL~;n>Sqxy;HHz7Wbg$n)Scc4!k1$hhvSzVkjo<(YE~;y_#}N zIlHfz)t-XDQJ@)-fFusZNeY7$ETB)p-V%Dxy!ABzylWDgtD4i z1vZ)NU-6c6F=u{!eoi}}`r?k|wmjn8>#b>^6q1eq%bX+Z*?p#Lz zSP_EV5VM)o`jV+XG29+8HA>OG6ifs&FE!}h{3Nbp!z(Y(j~`^z*O=I>HK`C6dq z?>#+A93mnie5aXBRuIw!qn|P{IV>b34n-900g?eHBcc+_lNe_HVIt>rf4F+=c->>q*YFwv+*?~UFoIsQZJENL@ zeI*8%ieG%6sbAj*dBfwgk}vp*7My`PJQMY?@BkMdKfgD1mpY@w$?gjO#Doh;7ft$O z1C@&G?ZT}G_B}aztD?qX!L(X@&hV8{t88b}4NvV4PmVC_HKttgOT3V@-36cU4h}AN ze5R6Goo&*^H9S#&j+1Z(M2(OZVH^7NDB0&y52pGU+7^mG_l3?pcUFn;YwsS`rd$z8 zDV)8DlPmQ;%~Fmp8f;a&E&!;drKM0{eU}1~(*kE#g^2`0R=u&ovlIRA3{nfEga2H% zTKT!!5k5Y?X(AOLG1WrAQ(^*uH1>M=rV(cex?@xn+Y>?K>d((F@073?_2oRrQ5$!b ziK42inygXqWckmQ9N*%@@0s7>8DwH&V@)D4?IT#JVycE1c{aGk55S>x6ok@ju*n+? z&{Gxs4w6iag9npfoN9&+^W6qTX1j@68J~?jBgQW*N3T;n#U^)vh2l)QN^M~!wr=14 z0)~}>liA9^kO7vfs0tDE?CiW=Az;|#RSXFztEgbxzuyfHQkP?@Yd`-(Q~Bl13#-=e zczti_@*Rh+E?K^6{Wm{l`D>T~0Bh{cYE~~O6`0f#!a_^Fk{94g2--!zK8~CL{M4~F z`?*^b_@U_hMoot>zIOY$gDY3A6fpSgPE2dQl_~W01mq;A({+E4kSPa*T&wwo_W6dy z`0xr?#C<$GaaGQ&_GRF0y_W35F6X&VU0+<7w++mw5f3SbEXY8aO{#@@z52dp@dgJ6 z3*7qRClxL2!NtR)Gc(rh*PLg;%db4g@ow$nO#NT}UpGqu!{wFWp37BptL^(Au;5py!)d)V2XfPis$=>Ux5 zoo4MoMGcHod0E-jk#BF@q3tu{z4dt(#@l%{&Okaq>C9&Ut$p0wonH)6W8oy=`$k6I zZSp-6 zSRXcmnd4kInD+*GY61d-#+1FV46eCdk43O4agxp(^JjWO$mtUq^7cN_TOIb>M^f#l z^4dr7C?JJ9Yqi9pZK?atcVAu_=r^V9K{8PbTp(~3Bh2SR36N_4YBe? zf>`Coz#FGq6TTk4S_+P4C#=+!T(gcmEgc;`?TW3^9*n1)H|~-l{|ma47w)GL%ESFf zucTymuEl9prCocs*%lYmjX9=GsLu3)zfRE$@k{G}N;dYa%gznWc^-gqxxO$s)BM=+p_l%B5EQXS8 z@2`&^kC&Kt@swBya?0t8atji-6E2$ll*&~P<^p<8BU5ns`umrBH7+^}qnkb5=|*cv zQt>1%j;OlSAC9zAliri{px@FSd0$FZ#Av1UC!>6iL$%@>p zL(z%g8*A8-?L9GJMzmw24oI4$AL2A4Dq!MD5b(;Yt9#-nh^0tW+WR6S!<%|R zopDiT&kCYOZ-{x@pPKK}@y^-w=gY=15=^wp{-iKySngT<4CV_ysyD(P7T?nZ=mF#I zTJYw+@Js>!D6H&oW|c=!n?4MLLO^XJ5S>SrlTC;z}RFa|d;X1%b zu)^odakewmwEBb(gl-_iBSh9TBV+p93)5ctHtQb;;jV;!X4WUDz2r_O7AnGSa_4(i z2hmT@E=c;sI5-O|EQiKY*<8L^5>Lyq3MUg5VdS+giyQnkSP zS07Rtu@*km5~{~qk`ckmF*Z{}%oP`tFTL^Gr<@J`@TTtLa9n$-$X-l0)MS-&E`Rkg zC-_Ttnv&ta9!|jiWQd?~51fQfZPY1ZQ~=ug+`9&ZfhnYo=y?0Uoki@)nk^gzQbY|- z{cK8iVlv8hgkP52Bqn8TE!^#aU%|yqVIqL`2UOak$_jfnT$*@X(b3{94bLwJZa=6- z5RGwqgKx{&PSlBv`G6pTVGYLM1q?->mIpU&$xG1s+ zI|%{OkD1HQ3bq!vSv78r0@r^V?4E&%u$~+&&$k+v z5xCj#$Yl#B;o6OHm$QsSVJVQWoNy~jef+uJXGtlYV2ao?FlB-*x0j2U5ALpoV8h^? z25h{k{R))nHOXo(U5m>(BdQ$bMMtGbqIm-&=|xzKR5!sQ7CgVPw#$NgJ6Jp#4NBIWc5OLt!2~6>aF!cPa@;3I^6WRxG@B_S3Y=h% za}d8SY`xhxB$Bg0I6NsJ?@wydfuriyiO`ZNV`iUTSe zr4%bm5>M5WBkO2B8;|JISICRcXi-@0CN{svV|F9#Jihzm^JtRhysJ|)dNobk(Ul|( zYQsmCS5QItK0|H+mxkjH{_K0NV;KUY2{R+2Yd zD2K;KNfmVlA>IXcOfl)=K}wQJst!VwjE`Bu8u|*{1EH1FFpkStkD+`>yI!(()dt35 zF!zV1^cyrexvo63{<;U?{0zNXwxdQZWg{K$n$vHrM)lE=M+mk$Ok_Nyo&Jzcw}s)A zw<4_Rg1+DdZ286KYNt@FxNSdp@*j)zCISK+@mY;&@#K%3q#%X2I4sVGTJ;3mM&Pbt z$OyN~_Xc{wPVXWW%WBzJB`VnqB>o^GAac9~eq$&9AhXrJxG>dX+2uii91|Z;WN(9D z3yM_dCk?txg8n@^x}$cpQ^PfA5fc+y^DJOF+ZFQ6Z{d2|UV`ij^Qw^L7pAN7TCZlNk}C%BqF7LJ%1` z;(m<%{47w#MT@>KDS#j_8! zUz5{gt@UUL17=RECEJ8qZ=$ajp~CbKG5UbN*>GDJp3$3F@Zv|CG0<)xOwxmq@(9-8 z*hfIQzpX$d!eK#(j0HMTLUAHP*-k1NRu6zok}P0i_3C2QXcGs9bZ3cdcx-MkgLo=0 zu-vwUdsp(z6)#;XTgjk)Tv~e7>Su6!HR5vzh`l=wvx3m0bpE{5b?mMg+P~E-miN%; z5~7Wv`I2_@cQP?9nhzkg6DE)U>gy$-l0b_|!wR2U1*DdJ z_^>zKxLrIYe@vFdIrO5hp_1~Q_<0O!u+8k!hs^pEC?vrlv#;MJ>=(?h%V*eh2tD)6 zH>XXSa1`$?}1HLIghvAlN{ zgcm`@vDUu^Y$IM8H2Rd4l|`O`uSI?wb9Hr<5uIu=Z83Sjt06(rsTdv8$*6M?;>=ZW z`j@hdb{$m9jLZ)WWyW-$hXDb>k(UMe^OIlyDRdQIAvHzQCA=ufI$(Z!=YIDwczhW- zx#+5~?#e{eliEXtb4KL2$fn-PYAyY1%@!HLEyd3+Btc)k9|sk1VjzG%#Wqm;KTs1% z8er5GWO0tf{YP00x6#>uH(@ZKiaIDGLhpW7Jrm6LfR6-hL{;@W<$H+`(?>lWsI_U= zBq=Zt>{xAb@&PocG(@Nwyi_^ciGD-7I#h_PgKypT14JV)MBYy+m{cQ2jb^HXypI|G zD7opdQOhw>aR7>X2r+`KW?L4^wyCI}0Fp^13G@)-)PqFDj%aC+lJju67@$NN*biiW zTJr=+AVKhh2Jks7*peTR*GHe9Gr{kZ*CmMe!WKg$p9PCr@nyAH|28JvtpXzf9iRB? z5+4C~USMKz`iLZld}tZWPIIRI7MQ@!VBPSi5EUwU{}6CM_3g)}lKT!G)W)a_7`KHI zLyAqVgO$TOkV;Hv5CW>rbrwb~h84>gDc*Ed>xHdl5z8Fqs zNAK&agK_}}u7S1*RtDP&LY6LHMc(O9?hZ6`#Q4fzM?oe!@_PB!ty^!;|7y$t;gi=F zNN~jB_{+qp{|#(hkp-i5_rLej@ZX5Ye^-+JzS`f1 z^`F1X{u`G6x4!}Sd*A-vxBto?ll%>&{{TpH4y>O{(mkJ)B;xOhme5YgOQoE=djEd_ Ddvn!y diff --git a/npm/angular/cypress/snapshots/image-snapshot/image-snapshot.component.cy-spec.ts/init.snap.png b/npm/angular/cypress/snapshots/image-snapshot/image-snapshot.component.cy-spec.ts/init.snap.png deleted file mode 100644 index ab144e4659fe98850a16df46b6be2217218a02f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7878 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 U2Dd>be(l&RgF52dp;<$p8QV From 91aae876cb124b2a9f0380470c029b96dd36c28e Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 7 Aug 2021 20:30:41 -0500 Subject: [PATCH 186/204] fix: avoid crash when no plugins function provided --- packages/server/lib/plugins/child/run_plugins.js | 10 ++++------ packages/server/lib/plugins/index.js | 5 ++++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js index 536f2d3608fc..d7d50adf168e 100644 --- a/packages/server/lib/plugins/child/run_plugins.js +++ b/packages/server/lib/plugins/child/run_plugins.js @@ -163,12 +163,10 @@ function interopRequire (pluginsFile) { * @returns the plugins function found */ function getPluginsFunction (resolvedExport, testingType, functionName) { - if (testingType) { - resolvedExport = resolvedExport[testingType] - } - - if (functionName) { - resolvedExport = resolvedExport[functionName] + if (testingType && functionName) { + if (resolvedExport[testingType]) { + return resolvedExport[testingType][functionName] + } } return resolvedExport diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index 87dca069a705..812c850be2be 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -98,12 +98,15 @@ const init = (config, options) => { if (/\.json$/.test(options.configFile)) { childArguments.push('--file', pluginsFile) - } else { + } else if (options[testingType] && options[testingType][PLUGINS_FUNCTION_NAME]) { childArguments.push( '--testingType', testingType, '--functionName', PLUGINS_FUNCTION_NAME, '--file', options.configFile, ) + } else { + // if the file is evealuated but there is no plugins function, fall back on default plugins + childArguments.push('--file', path.join(__dirname, 'child', 'default_plugins_file.js')) } if (config.resolvedNodePath) { From c32305bfd02c674ca9f4ede64099b45580392a7f Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 7 Aug 2021 22:54:14 -0500 Subject: [PATCH 187/204] simplify and fix plugins management --- packages/server/lib/plugins/child/index.js | 4 ++-- packages/server/lib/plugins/child/run_plugins.js | 15 +++++++-------- packages/server/lib/plugins/index.js | 7 ++----- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/server/lib/plugins/child/index.js b/packages/server/lib/plugins/child/index.js index 73e99d1fb1b8..4f9bb5e5356e 100644 --- a/packages/server/lib/plugins/child/index.js +++ b/packages/server/lib/plugins/child/index.js @@ -3,6 +3,6 @@ require('graceful-fs').gracefulify(require('fs')) require('../../util/suppress_warnings').suppress() const ipc = require('../util').wrapIpc(process) -const { file: pluginsFile, projectRoot, testingType, functionName } = require('minimist')(process.argv.slice(2)) +const { file: pluginsFile, projectRoot, testingType } = require('minimist')(process.argv.slice(2)) -require('./run_plugins')(ipc, pluginsFile, projectRoot, testingType, functionName) +require('./run_plugins')(ipc, pluginsFile, projectRoot, testingType) diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js index d7d50adf168e..c1d536903525 100644 --- a/packages/server/lib/plugins/child/run_plugins.js +++ b/packages/server/lib/plugins/child/run_plugins.js @@ -162,10 +162,10 @@ function interopRequire (pluginsFile) { * @param {string} functionName path to the function in the exported object like `"e2e.plugins"` * @returns the plugins function found */ -function getPluginsFunction (resolvedExport, testingType, functionName) { - if (testingType && functionName) { +function getPluginsFunction (resolvedExport, testingType) { + if (testingType) { if (resolvedExport[testingType]) { - return resolvedExport[testingType][functionName] + return resolvedExport[testingType].setupNodeEvents } } @@ -174,10 +174,9 @@ function getPluginsFunction (resolvedExport, testingType, functionName) { let tsRegistered = false -const runPlugins = (ipc, pluginsFile, projectRoot, testingType, functionName) => { +const runPlugins = (ipc, pluginsFile, projectRoot, testingType) => { debug('plugins file:', pluginsFile) debug('testingType:', testingType) - debug('function name:', functionName) debug('project root:', projectRoot) if (!projectRoot) { throw new Error('Unexpected: projectRoot should be a string') @@ -207,12 +206,12 @@ const runPlugins = (ipc, pluginsFile, projectRoot, testingType, functionName) => } try { - debug('require pluginsFile "%s", functionName "%s"', pluginsFile, functionName) + debug('require pluginsFile "%s"', pluginsFile) const pluginsObject = interopRequire(pluginsFile) - plugins = getPluginsFunction(pluginsObject, testingType, functionName) + plugins = getPluginsFunction(pluginsObject, testingType) - if (testingType === 'component') { + if (testingType === 'component' && pluginsObject.component) { setupDevServerFunction = pluginsObject.component.setupDevServer } diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index 812c850be2be..af3d383b2b91 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -13,8 +13,6 @@ let pluginsProcess = null let registeredEvents = {} let handlers = [] -const PLUGINS_FUNCTION_NAME = 'setupNodeEvents' - const register = (event, callback) => { debug(`register event '${event}'`) @@ -98,14 +96,13 @@ const init = (config, options) => { if (/\.json$/.test(options.configFile)) { childArguments.push('--file', pluginsFile) - } else if (options[testingType] && options[testingType][PLUGINS_FUNCTION_NAME]) { + } else if (config[testingType] && (config[testingType].setupNodeEvents || (testingType === 'component' && config.component.setupDevServer))) { childArguments.push( '--testingType', testingType, - '--functionName', PLUGINS_FUNCTION_NAME, '--file', options.configFile, ) } else { - // if the file is evealuated but there is no plugins function, fall back on default plugins + // if the config file is evaluated but there is no plugins function, fall back on default plugins childArguments.push('--file', path.join(__dirname, 'child', 'default_plugins_file.js')) } From f81c67cf26446b1ce4c4c60fc9ad039fdabbaadc Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 7 Aug 2021 23:07:48 -0500 Subject: [PATCH 188/204] fix: runner-ct config needs to follow the full migration --- packages/runner-ct/cypress.config.js | 31 ++++++++++++++++++ packages/runner-ct/cypress/plugins/index.js | 36 --------------------- 2 files changed, 31 insertions(+), 36 deletions(-) delete mode 100644 packages/runner-ct/cypress/plugins/index.js diff --git a/packages/runner-ct/cypress.config.js b/packages/runner-ct/cypress.config.js index cb5207cf84ae..290609eb852f 100644 --- a/packages/runner-ct/cypress.config.js +++ b/packages/runner-ct/cypress.config.js @@ -1,7 +1,38 @@ +/// +const path = require('path') +const { startDevServer } = require('@cypress/webpack-dev-server') + +function injectStylesInlineForPercyInPlace (webpackConfig) { + webpackConfig.module.rules = webpackConfig.module.rules.map((rule) => { + if (rule?.use[0]?.loader.includes('mini-css-extract-plugin')) { + return { + ...rule, + use: [{ + loader: 'style-loader', + }], + } + } + + return rule + }) +} + module.exports = { testFiles: '**/*spec.{ts,tsx}', video: true, env: { reactDevtools: true, }, + component: { + setupDevServer (options) { + const { default: webpackConfig } = require(path.resolve(__dirname, 'webpack.config.ts')) + + injectStylesInlineForPercyInPlace(webpackConfig) + + return startDevServer({ + webpackConfig, + options, + }) + }, + }, } diff --git a/packages/runner-ct/cypress/plugins/index.js b/packages/runner-ct/cypress/plugins/index.js deleted file mode 100644 index fb59d3d5996a..000000000000 --- a/packages/runner-ct/cypress/plugins/index.js +++ /dev/null @@ -1,36 +0,0 @@ -/// -const path = require('path') -const { startDevServer } = require('@cypress/webpack-dev-server') - -function injectStylesInlineForPercyInPlace (webpackConfig) { - webpackConfig.module.rules = webpackConfig.module.rules.map((rule) => { - if (rule?.use[0]?.loader.includes('mini-css-extract-plugin')) { - return { - ...rule, - use: [{ - loader: 'style-loader', - }], - } - } - - return rule - }) -} -/** - * @type {Cypress.PluginConfig} - */ -module.exports = (on, config) => { - on('dev-server:start', (options) => { - /** @type {import('webpack').Configuration} */ - const { default: webpackConfig } = require(path.resolve(__dirname, '..', '..', 'webpack.config.ts')) - - injectStylesInlineForPercyInPlace(webpackConfig) - - return startDevServer({ - webpackConfig, - options, - }) - }) - - return config -} From da1c409dd2abd033144d56e72a8f1a3f3d1292ed Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 7 Aug 2021 23:11:12 -0500 Subject: [PATCH 189/204] test: fix plugins test --- packages/server/test/integration/plugins_spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/test/integration/plugins_spec.js b/packages/server/test/integration/plugins_spec.js index b32418a05823..cb7c1e5284ee 100644 --- a/packages/server/test/integration/plugins_spec.js +++ b/packages/server/test/integration/plugins_spec.js @@ -27,6 +27,7 @@ describe('lib/plugins', () => { const options = { onWarning, testingType: 'e2e', + configFile: 'cypress.json', } return plugins.init(projectConfig, options) From 198c36a54d741b428ae072d5e4ce1594ed6e67ce Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Mon, 9 Aug 2021 09:59:39 -0500 Subject: [PATCH 190/204] fix: when configFile is null, use the pluginsFile --- packages/server/lib/plugins/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index af3d383b2b91..ef812f3401b5 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -94,7 +94,7 @@ const init = (config, options) => { const testingType = options.testingType || 'e2e' - if (/\.json$/.test(options.configFile)) { + if (/\.json$/.test(options.configFile) || options.configFile === false) { childArguments.push('--file', pluginsFile) } else if (config[testingType] && (config[testingType].setupNodeEvents || (testingType === 'component' && config.component.setupDevServer))) { childArguments.push( From c4faae1d286d6a686f3026ad3c8bffc2e899e0d2 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Wed, 11 Aug 2021 14:49:13 -0500 Subject: [PATCH 191/204] fix: conflict when both config.json and js --- packages/server/lib/errors.js | 2 +- packages/server/lib/util/settings.js | 11 +---------- .../server/test/unit/util/settings_spec.js | 19 ++++++++++++++++++- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index f329c821a8ca..b52361205855 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -1024,7 +1024,7 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { // TODO: update with vetted cypress language case 'CONFIG_FILES_LANGUAGE_CONFLICT': return stripIndent` - There is both a \`cypress.config.js\` and a \`cypress.config.ts\` in the location below: + There is both a \`${arg2}\` and a \`${arg3}\` at the location below: ${arg1} diff --git a/packages/server/lib/util/settings.js b/packages/server/lib/util/settings.js index 8cd273b04501..946516de6e74 100644 --- a/packages/server/lib/util/settings.js +++ b/packages/server/lib/util/settings.js @@ -156,16 +156,7 @@ module.exports = { // if we found more than one, it might just be the json that is still migrating if (foundConfigFiles.length > 1) { - const evaluatedFiles = foundConfigFiles.filter((file) => file !== 'cypress.json') - - // if we found more than one evaluated file (js or ts) - // we can't trust that migration has been done properly - if (evaluatedFiles.length > 1) { - return errors.throw('CONFIG_FILES_LANGUAGE_CONFLICT', projectRoot) - } - - // else, just ignore the cypress.json and return the other one - return evaluatedFiles[0] + return errors.throw('CONFIG_FILES_LANGUAGE_CONFLICT', projectRoot, ...foundConfigFiles) } // Default is to create a new `cypress.config.js` file if one does not exist. diff --git a/packages/server/test/unit/util/settings_spec.js b/packages/server/test/unit/util/settings_spec.js index ad776eec7f2b..f883e6f73fa6 100644 --- a/packages/server/test/unit/util/settings_spec.js +++ b/packages/server/test/unit/util/settings_spec.js @@ -2,10 +2,12 @@ require('../../spec_helper') const { fs } = require(`../../../lib/util/fs`) const setting = require(`../../../lib/util/settings`) +let readStub + describe('lib/util/settings', () => { describe('pathToConfigFile', () => { beforeEach(() => { - sinon.stub(fs, 'readdirSync').returns(['cypress.json']) + readStub = sinon.stub(fs, 'readdirSync').returns(['cypress.json']) }) it('supports relative path', () => { @@ -23,5 +25,20 @@ describe('lib/util/settings', () => { expect(path).to.equal('/users/pepper/cypress/e2e/cypress.config.json') }) + + it('errors if there is json & js', () => { + readStub.returns(['cypress.json', 'cypress.config.js']) + expect(() => setting.pathToConfigFile('/cypress')).to.throw('`cypress.config.js` and a `cypress.json`') + }) + + it('errors if there is ts & js', () => { + readStub.returns(['cypress.config.ts', 'cypress.config.js']) + expect(() => setting.pathToConfigFile('/cypress')).to.throw('`cypress.config.js` and a `cypress.config.ts`') + }) + + it('errors if all three are there', () => { + readStub.returns(['cypress.config.ts', 'cypress.json', 'cypress.config.js']) + expect(() => setting.pathToConfigFile('/cypress')).to.throw('`cypress.config.js` and a `cypress.config.ts`') + }) }) }) From 69ebdbafeb21abcd19316f16256374130b51dff0 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 13 Aug 2021 14:20:15 -0500 Subject: [PATCH 192/204] add promises to setup functions returns --- cli/types/cypress.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index d8bf31a59a32..56005d03bcbc 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2488,7 +2488,7 @@ declare namespace Cypress { cmdKey: boolean } - type PluginsFunction = ((on: PluginEvents, config: PluginConfigOptions) => ConfigOptionsMergedWithTestingTypes | undefined | void) + type PluginsFunction = ((on: PluginEvents, config: PluginConfigOptions) => Promise | ConfigOptionsMergedWithTestingTypes | undefined | void) type TestingTypeConfig = Omit & { setupNodeEvents?: PluginsFunction } type TestingTypeConfigComponent = TestingTypeConfig & { @@ -2496,7 +2496,7 @@ declare namespace Cypress { * Return the setup of your server * @param options the dev server options to pass directly to the dev-server */ - setupDevServer(options: DevServerOptions): Promise + setupDevServer(options: DevServerOptions): Promise | ResolvedDevServerConfig } interface ResolvedConfigOptions { From b9982010898ce21be480e3e8a344ea255ed28aba Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Thu, 9 Sep 2021 14:13:21 -0500 Subject: [PATCH 193/204] refactor: rename option setupDevServer --- cli/types/cypress.d.ts | 6 +++--- npm/angular/cypress.config.ts | 2 +- npm/react/cypress.config.js | 2 +- npm/vue/cypress.config.ts | 2 +- packages/runner-ct/cypress.config.js | 2 +- packages/server/lib/config_options.ts | 2 +- packages/server/lib/errors.js | 4 ++-- packages/server/lib/plugins/child/run_plugins.js | 12 ++++++------ packages/server/lib/plugins/index.js | 2 +- packages/server/test/unit/config_spec.js | 16 ++++++++-------- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 98fa2a64433d..b0e2050ddab7 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2496,7 +2496,7 @@ declare namespace Cypress { * Return the setup of your server * @param options the dev server options to pass directly to the dev-server */ - setupDevServer(options: DevServerOptions): Promise | ResolvedDevServerConfig + devServer(options: DevServerOptions): Promise | ResolvedDevServerConfig } interface ResolvedConfigOptions { @@ -2861,8 +2861,8 @@ declare namespace Cypress { * Config model of cypress. To be used in `cypress.config.js` */ type ConfigOptions = Omit - // make setupDevServer required in component - & {component?: {setupDevServer: TestingTypeConfigComponent['setupDevServer'] }} + // make devServer required in component + & {component?: {devServer: TestingTypeConfigComponent['devServer'] }} interface PluginConfigOptions extends ResolvedConfigOptions { /** diff --git a/npm/angular/cypress.config.ts b/npm/angular/cypress.config.ts index ab2a159809ce..76c12d59469c 100644 --- a/npm/angular/cypress.config.ts +++ b/npm/angular/cypress.config.ts @@ -12,7 +12,7 @@ export default defineConfig({ component: { componentFolder: 'src', testFiles: '**/*cy-spec.ts', - setupDevServer (options) { + devServer (options) { return startDevServer({ options, webpackConfig, diff --git a/npm/react/cypress.config.js b/npm/react/cypress.config.js index 1de7d79560fc..5a7625a259d9 100644 --- a/npm/react/cypress.config.js +++ b/npm/react/cypress.config.js @@ -75,7 +75,7 @@ module.exports = defineConfig({ env: { reactDevtools: true, }, - setupDevServer (options) { + devServer (options) { return startDevServer({ options, webpackConfig, disableLazyCompilation: false }) }, }, diff --git a/npm/vue/cypress.config.ts b/npm/vue/cypress.config.ts index 1abb05b92e0a..4724fea58e6b 100644 --- a/npm/vue/cypress.config.ts +++ b/npm/vue/cypress.config.ts @@ -11,7 +11,7 @@ export default defineConfig({ experimentalFetchPolyfill: true, component: { testFiles: '**/*spec.{js,ts,tsx}', - setupDevServer (options) { + devServer (options) { if (!webpackConfig.resolve) { webpackConfig.resolve = {} } diff --git a/packages/runner-ct/cypress.config.js b/packages/runner-ct/cypress.config.js index 2ca7e1710c96..954a84ddc58c 100644 --- a/packages/runner-ct/cypress.config.js +++ b/packages/runner-ct/cypress.config.js @@ -24,7 +24,7 @@ module.exports = { reactDevtools: true, }, component: { - setupDevServer (options) { + devServer (options) { const { default: webpackConfig } = require(path.resolve(__dirname, 'webpack.config.ts')) injectStylesInlineForPercyInPlace(webpackConfig) diff --git a/packages/server/lib/config_options.ts b/packages/server/lib/config_options.ts index 395f74286f79..528340aa59d1 100644 --- a/packages/server/lib/config_options.ts +++ b/packages/server/lib/config_options.ts @@ -226,7 +226,7 @@ export const options = [ validation: v.isFunction, onlyInOverride: true, }, { - name: 'setupDevServer', + name: 'devServer', defaultvalue: null, validation: v.isFunction, onlyInOverride: 'component', diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index b52361205855..6a8834887079 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -1003,9 +1003,9 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { // TODO: update with vetted cypress language case 'CT_NO_DEV_START_FUNCTION': return stripIndent`\ - To run component-testing, cypress needs the \`setupDevServer\` function to be implemented. + To run component-testing, cypress needs the \`devServer\` function to be implemented. - Add a \`setupDevServer()\` in the component object of the ${arg1} file. + Add a \`devServer()\` in the component object of the ${arg1} file. Learn how to set up component testing: diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js index c1d536903525..f62d6a3d6cf4 100644 --- a/packages/server/lib/plugins/child/run_plugins.js +++ b/packages/server/lib/plugins/child/run_plugins.js @@ -36,7 +36,7 @@ const getDefaultPreprocessor = function (config) { } let plugins -let setupDevServerFunction +let devServerFunction const load = (ipc, config, pluginsFile) => { debug('run plugins function') @@ -88,8 +88,8 @@ const load = (ipc, config, pluginsFile) => { .try(() => { debug('run plugins function') - if (setupDevServerFunction) { - register('dev-server:start', setupDevServerFunction) + if (devServerFunction) { + register('dev-server:start', devServerFunction) } if (plugins) { @@ -212,11 +212,11 @@ const runPlugins = (ipc, pluginsFile, projectRoot, testingType) => { plugins = getPluginsFunction(pluginsObject, testingType) if (testingType === 'component' && pluginsObject.component) { - setupDevServerFunction = pluginsObject.component.setupDevServer + devServerFunction = pluginsObject.component.devServer } debug('plugins %o', plugins) - debug('setupDevServerFunction %o', setupDevServerFunction) + debug('devServerFunction %o', devServerFunction) } catch (err) { debug('failed to require pluginsFile:\n%s', err.stack) ipc.send('load:error', 'PLUGINS_FILE_ERROR', pluginsFile, err.stack) @@ -224,7 +224,7 @@ const runPlugins = (ipc, pluginsFile, projectRoot, testingType) => { return } - if (typeof plugins !== 'function' && !setupDevServerFunction) { + if (typeof plugins !== 'function' && !devServerFunction) { debug('not a function') ipc.send('load:error', 'PLUGINS_DIDNT_EXPORT_FUNCTION', pluginsFile, plugins) diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index ef812f3401b5..a041f01a8300 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -96,7 +96,7 @@ const init = (config, options) => { if (/\.json$/.test(options.configFile) || options.configFile === false) { childArguments.push('--file', pluginsFile) - } else if (config[testingType] && (config[testingType].setupNodeEvents || (testingType === 'component' && config.component.setupDevServer))) { + } else if (config[testingType] && (config[testingType].setupNodeEvents || (testingType === 'component' && config.component.devServer))) { childArguments.push( '--testingType', testingType, '--file', options.configFile, diff --git a/packages/server/test/unit/config_spec.js b/packages/server/test/unit/config_spec.js index 81e3bd28b0d9..968643eb1333 100644 --- a/packages/server/test/unit/config_spec.js +++ b/packages/server/test/unit/config_spec.js @@ -272,12 +272,12 @@ describe('lib/config', () => { }) }) - describe('setupDevServer', function () { - it('allows setupDevServer in component', function () { + describe('devServer', function () { + it('allows devServer in component', function () { this.setup({ viewportWidth: 1024, component: { - setupDevServer () { + devServer () { // noop }, }, @@ -285,13 +285,13 @@ describe('lib/config', () => { return config.get(this.projectRoot, { testingType: 'component' }) .then((obj) => { - expect(obj.component.setupDevServer).to.be.a('function') + expect(obj.component.devServer).to.be.a('function') }) }) - it('disallows setupDevServer in root', function () { + it('disallows devServer in root', function () { this.setup({ - setupDevServer () { + devServer () { // noop }, }) @@ -299,10 +299,10 @@ describe('lib/config', () => { return this.expectValidationFails('\`component\`') }) - it('disallows setupDevServer in e2e', function () { + it('disallows devServer in e2e', function () { this.setup({ e2e: { - setupDevServer () { + devServer () { // noop }, }, From 47897319821a1ac5f496e2a36aafaf795b419806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barth=C3=A9l=C3=A9my=20Ledoux?= Date: Thu, 9 Sep 2021 15:20:38 -0500 Subject: [PATCH 194/204] chore: error wording changes Co-authored-by: Jennifer Shehane --- packages/server/lib/errors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 6a8834887079..51deda85a700 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -1016,7 +1016,7 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { return stripIndent` \`pluginsFile\` cannot be set in a \`${arg1}\` file. - \`pluginsFile\` is deprecated and will error in cypress 9.0, prefer using the \`setupNodeEvents\` function instead. + \`pluginsFile\` is deprecated and will error in a future version of Cypress, prefer using the \`setupNodeEvents\` function instead. https://on.cypress.io/setupNodeEvents ` From 7bcafa000ee67f2813e4f75b29447acffe3acb64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barth=C3=A9l=C3=A9my=20Ledoux?= Date: Thu, 9 Sep 2021 15:21:32 -0500 Subject: [PATCH 195/204] chore: update error wording Co-authored-by: Jennifer Shehane --- packages/server/lib/errors.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 51deda85a700..20545d2a7baa 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -1034,8 +1034,9 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { // TODO: update with vetted cypress language case 'DEPRECATED_CYPRESS_JSON': return stripIndent` - In cypress 9.0, \`cypress.json\` will not be supported anymore. - please see the docs to migrate it to \`cypress.config.js\` + \`cypress.json\` will not be supported in a future version of Cypress. + + Please see the docs to migrate it to \`cypress.config.js\` Learn more: https://on.cypress.io/migrate-configjs ` From 07be3c47253286166e94cb1a7f2986181c361031 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 10 Sep 2021 11:06:05 -0500 Subject: [PATCH 196/204] fix: reword unit test title in server settings --- packages/server/test/unit/settings_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/test/unit/settings_spec.js b/packages/server/test/unit/settings_spec.js index 32e7d20873bd..29187221e256 100644 --- a/packages/server/test/unit/settings_spec.js +++ b/packages/server/test/unit/settings_spec.js @@ -113,7 +113,7 @@ describe('lib/settings', () => { return settings.read(projectRoot) }) .then(() => { - throw Error('should throw wehn 2 files are created') + throw Error('should throw when 2 files are created') }) .catch((err) => { expect(err.type).to.eq('CONFIG_FILES_LANGUAGE_CONFLICT') From 49f97ebd66f1aff908ae17375ec376be910ab257 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 10 Sep 2021 12:02:51 -0500 Subject: [PATCH 197/204] fix: build process --- npm/webpack-dev-server/src/startServer.ts | 2 -- packages/server/test/unit/settings_spec.js | 8 +++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/npm/webpack-dev-server/src/startServer.ts b/npm/webpack-dev-server/src/startServer.ts index 512bece56be8..193af92d7c3e 100644 --- a/npm/webpack-dev-server/src/startServer.ts +++ b/npm/webpack-dev-server/src/startServer.ts @@ -63,7 +63,6 @@ export async function start ({ webpackConfig: userWebpackConfig, template, optio noInfo: false, } - // @ts-expect-error ignore webpack-dev-server v3 type errors return new WebpackDevServer(compiler, webpackDevServerConfig) } @@ -79,7 +78,6 @@ export async function start ({ webpackConfig: userWebpackConfig, template, optio hot: false, } - // @ts-expect-error Webpack types are clashing between Webpack and WebpackDevServer return new WebpackDevServer(webpackDevServerConfig, compiler) } diff --git a/packages/server/test/unit/settings_spec.js b/packages/server/test/unit/settings_spec.js index 29187221e256..cf09ed5dc4a7 100644 --- a/packages/server/test/unit/settings_spec.js +++ b/packages/server/test/unit/settings_spec.js @@ -3,6 +3,8 @@ require('../spec_helper') const path = require('path') const { fs } = require(`${root}lib/util/fs`) const settings = require(`${root}lib/util/settings`) +const { CYPRESS_CONFIG_FILES } = require(`${root}/lib/configFiles`) + const { clearCypressJsonCache } = require('../cache_helper') const projectRoot = process.cwd() @@ -20,9 +22,9 @@ describe('lib/settings', () => { }) afterEach(() => { - return fs.removeAsync('cypress.json') - .then(() => fs.removeAsync('cypress.config.js')) - .then(() => fs.removeAsync('cypress.config.ts')) + return CYPRESS_CONFIG_FILES.reduce((previousPromise, currentFile) => { + return previousPromise.then(() => fs.removeAsync(currentFile)) + }, Promise.resolve()) .then(clearCypressJsonCache) }) From 4cdef63ec7051f9cfd4bbc549df2a4dcd6fa1f5a Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 1 Oct 2021 11:05:59 -0500 Subject: [PATCH 198/204] build: make build artifacts on this PR branch --- circle.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/circle.yml b/circle.yml index 54d82cfb6441..fcb21e76ee9f 100644 --- a/circle.yml +++ b/circle.yml @@ -9,6 +9,7 @@ macBuildFilters: &macBuildFilters only: - develop - issue-17944-remove-source-files + - feat/allow-cypress-js defaults: &defaults parallelism: 1 @@ -43,6 +44,7 @@ onlyMainBranches: &onlyMainBranches only: - develop - issue-17944-remove-source-files + - feat/allow-cypress-js requires: - create-build-artifacts @@ -2125,6 +2127,7 @@ linux-workflow: &linux-workflow only: - develop - issue-17944-remove-source-files + - feat/allow-cypress-js requires: - build - test-kitchensink: @@ -2137,6 +2140,7 @@ linux-workflow: &linux-workflow only: - develop - issue-17944-remove-source-files + - feat/allow-cypress-js requires: - build - create-build-artifacts: @@ -2187,6 +2191,7 @@ linux-workflow: &linux-workflow only: - develop - issue-17944-remove-source-files + - feat/allow-cypress-js requires: - create-build-artifacts - test-npm-module-and-verify-binary: @@ -2195,6 +2200,7 @@ linux-workflow: &linux-workflow only: - develop - issue-17944-remove-source-files + - feat/allow-cypress-js requires: - create-build-artifacts - test-binary-against-staging: @@ -2204,6 +2210,7 @@ linux-workflow: &linux-workflow only: - develop - issue-17944-remove-source-files + - feat/allow-cypress-js requires: - create-build-artifacts @@ -2229,6 +2236,7 @@ linux-workflow: &linux-workflow only: - develop - issue-17944-remove-source-files + - feat/allow-cypress-js requires: - create-build-artifacts @@ -2301,6 +2309,7 @@ mac-workflow: &mac-workflow only: - develop - issue-17944-remove-source-files + - feat/allow-cypress-js requires: - darwin-create-build-artifacts @@ -2313,6 +2322,7 @@ mac-workflow: &mac-workflow only: - develop - issue-17944-remove-source-files + - feat/allow-cypress-js requires: - darwin-create-build-artifacts From 38a9a27b4ba27a441fe2af2f5de063dca6c0be1c Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 1 Oct 2021 17:35:14 -0500 Subject: [PATCH 199/204] make build pass --- npm/webpack-dev-server/src/startServer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/npm/webpack-dev-server/src/startServer.ts b/npm/webpack-dev-server/src/startServer.ts index 9b7f5b67d14b..eaf97a700edf 100644 --- a/npm/webpack-dev-server/src/startServer.ts +++ b/npm/webpack-dev-server/src/startServer.ts @@ -79,6 +79,7 @@ export async function start ({ webpackConfig: userWebpackConfig, template, optio hot: false, } + // @ts-ignore ignore webpack-dev-server v3 type errors return new WebpackDevServer(webpackDevServerConfig, compiler) } From 61a2f58c38df81d0e13e28f23798ae8f3726e999 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 1 Oct 2021 18:00:58 -0500 Subject: [PATCH 200/204] fix types missing --- cli/types/cypress.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 91a34a744c33..3b30d3d1a0c6 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2503,7 +2503,7 @@ declare namespace Cypress { * Return the setup of your server * @param options the dev server options to pass directly to the dev-server */ - devServer(options: DevServerOptions): Promise | ResolvedDevServerConfig + devServer(options: CypressDevServerOptions): Promise | ResolvedDevServerConfig } interface PEMCert { /** @@ -5349,7 +5349,7 @@ declare namespace Cypress { tag?: string } - interface DevServerConfig { + interface CypressDevServerOptions { specs: Spec[] config: ResolvedConfigOptions & RuntimeConfigOptions devServerEvents: NodeJS.EventEmitter @@ -5368,7 +5368,7 @@ declare namespace Cypress { (action: 'before:spec', fn: (spec: Spec) => void | Promise): void (action: 'before:browser:launch', fn: (browser: Browser, browserLaunchOptions: BrowserLaunchOptions) => void | BrowserLaunchOptions | Promise): void (action: 'file:preprocessor', fn: (file: FileObject) => string | Promise): void - (action: 'dev-server:start', fn: (file: DevServerConfig) => Promise): void + (action: 'dev-server:start', fn: (file: CypressDevServerOptions) => Promise): void (action: 'task', tasks: Tasks): void } From d44aada898249c01b36ae79c56955d5a17229ce4 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 1 Oct 2021 18:14:34 -0500 Subject: [PATCH 201/204] fix changed types --- npm/webpack-dev-server/src/startServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/webpack-dev-server/src/startServer.ts b/npm/webpack-dev-server/src/startServer.ts index eaf97a700edf..1f59e4d68e98 100644 --- a/npm/webpack-dev-server/src/startServer.ts +++ b/npm/webpack-dev-server/src/startServer.ts @@ -6,7 +6,7 @@ import { webpackDevServerFacts } from './webpackDevServerFacts' export interface StartDevServer extends UserWebpackDevServerOptions { /* this is the Cypress dev server configuration object */ - options: Cypress.DevServerConfig + options: Cypress.CypressDevServerOptions /* support passing a path to the user's webpack config */ webpackConfig?: Record /* base html template to render in AUT */ From f94d4334bcec2dd061ddf0fcfe498e1f23615a32 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Fri, 1 Oct 2021 18:16:01 -0500 Subject: [PATCH 202/204] fix vite too --- npm/vite-dev-server/src/startServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/vite-dev-server/src/startServer.ts b/npm/vite-dev-server/src/startServer.ts index c57c37ffc0ab..8d46357c850e 100644 --- a/npm/vite-dev-server/src/startServer.ts +++ b/npm/vite-dev-server/src/startServer.ts @@ -10,7 +10,7 @@ export interface StartDevServerOptions { /** * the Cypress dev server configuration object */ - options: Cypress.DevServerConfig + options: Cypress.CypressDevServerOptions /** * By default, vite will use your vite.config file to * Start the server. If you need additional plugins or From 241b09665977efce6e2ffc6fa0db08cf08f1b752 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 2 Oct 2021 16:34:48 -0500 Subject: [PATCH 203/204] fix build process on darwin --- packages/server/lib/config.ts | 2 +- packages/server/lib/project-base.ts | 4 ++-- packages/server/lib/project_static.ts | 2 +- packages/server/lib/util/require_async.ts | 4 ---- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/server/lib/config.ts b/packages/server/lib/config.ts index ec153fcad89b..66e8b45b9cdd 100644 --- a/packages/server/lib/config.ts +++ b/packages/server/lib/config.ts @@ -230,7 +230,7 @@ export function allowed (obj = {}) { } export function get (projectRoot, options = {}) { - const configFilename = settings.configFile(projectRoot, options) + const configFilename = settings.configFile(options) return Promise.all([ settings.read(projectRoot, options).then(validateFile(configFilename)), diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 598d76051996..a487593e0bee 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -575,7 +575,7 @@ export class ProjectBase extends EE { } if (configFile !== false) { - this.watchers.watchTree(settings.pathToConfigFile(projectRoot, { configFile }), obj) + this.watchers.watchTree(settings.pathToConfigFile({ configFile }), obj) } return this.watchers.watch(settings.pathToCypressEnvJson(projectRoot), obj) @@ -878,7 +878,7 @@ export class ProjectBase extends EE { return readSettings.projectId } - return errors.throw('NO_PROJECT_ID', settings.configFile(this.projectRoot, this.options), this.projectRoot) + return errors.throw('NO_PROJECT_ID', settings.configFile(this.options), this.projectRoot) } async verifyExistence () { diff --git a/packages/server/lib/project_static.ts b/packages/server/lib/project_static.ts index 65f27cfa2562..b216451b2662 100644 --- a/packages/server/lib/project_static.ts +++ b/packages/server/lib/project_static.ts @@ -137,7 +137,7 @@ export function remove (path) { export async function add (path, options) { // don't cache a project if a non-default configFile is set // https://git.io/JeGyF - if (settings.configFile(path, options) !== 'cypress.json') { + if (settings.configFile(options) !== 'cypress.json') { return Promise.resolve({ path }) } diff --git a/packages/server/lib/util/require_async.ts b/packages/server/lib/util/require_async.ts index e05fa2b978f2..427500f7effa 100644 --- a/packages/server/lib/util/require_async.ts +++ b/packages/server/lib/util/require_async.ts @@ -13,10 +13,6 @@ let requireProcess: cp.ChildProcess | null interface RequireAsyncOptions{ projectRoot: string loadErrorCode: string - /** - * members of the object returned that are functions and will need to be wrapped - */ - functionNames: string[] } interface ChildOptions{ From a120c2c6cf37f4624b15f71d4b10f8098c192432 Mon Sep 17 00:00:00 2001 From: ElevateBart Date: Sat, 2 Oct 2021 16:36:58 -0500 Subject: [PATCH 204/204] fix the build --- packages/server/lib/project-base.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index a487593e0bee..7b7f9a33b9a3 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -544,7 +544,7 @@ export class ProjectBase extends EE { projectRoot, }: { onSettingsChanged?: false | (() => void) - configFile?: string | boolean + configFile?: string | false projectRoot: string }) { // bail if we havent been told to @@ -575,7 +575,7 @@ export class ProjectBase extends EE { } if (configFile !== false) { - this.watchers.watchTree(settings.pathToConfigFile({ configFile }), obj) + this.watchers.watchTree(settings.pathToConfigFile(projectRoot, { configFile }), obj) } return this.watchers.watch(settings.pathToCypressEnvJson(projectRoot), obj)