mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-25 16:39:04 -06:00
fix: allow migration of pluginsFile using env properties (#20770)
* fix: allow migration of code-coverage plugin * fix: use 9.X options to default migration * fix: remove internal values from options * test: add unit tests to the new model * test: e2e test all error prone defaultValues * fix: avoid removing values in pluginsFile * test: fix tests * test: fix test * fix unit test
This commit is contained in:
committed by
GitHub
parent
1033e04bd8
commit
771f7654be
@@ -3,6 +3,7 @@ import path from 'path'
|
||||
import { fork } from 'child_process'
|
||||
import type { ForkOptions } from 'child_process'
|
||||
import assert from 'assert'
|
||||
import _ from 'lodash'
|
||||
import type { DataContext } from '..'
|
||||
import {
|
||||
cleanUpIntegrationFolder,
|
||||
@@ -23,10 +24,45 @@ import {
|
||||
getComponentFolder,
|
||||
getIntegrationTestFilesGlobs,
|
||||
getSpecPattern,
|
||||
legacyOptions,
|
||||
} from '../sources/migration'
|
||||
import { makeCoreData } from '../data'
|
||||
import { LegacyPluginsIpc } from '../data/LegacyPluginsIpc'
|
||||
|
||||
export function getConfigWithDefaults (legacyConfig: any) {
|
||||
const newConfig = _.cloneDeep(legacyConfig)
|
||||
|
||||
legacyOptions.forEach(({ defaultValue, name }) => {
|
||||
if (defaultValue !== undefined && legacyConfig[name] === undefined) {
|
||||
newConfig[name] = typeof defaultValue === 'function' ? defaultValue() : defaultValue
|
||||
}
|
||||
})
|
||||
|
||||
return newConfig
|
||||
}
|
||||
|
||||
export function getDiff (oldConfig: any, newConfig: any) {
|
||||
// get all the values updated
|
||||
const result: any = _.reduce(oldConfig, (acc: any, value, key) => {
|
||||
// ignore values that have been removed
|
||||
if (newConfig[key] && !_.isEqual(value, newConfig[key])) {
|
||||
acc[key] = newConfig[key]
|
||||
}
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
// get all the values added
|
||||
return _.reduce(newConfig, (acc: any, value, key) => {
|
||||
// their key is in the new config but not in the old config
|
||||
if (!oldConfig.hasOwnProperty(key)) {
|
||||
acc[key] = value
|
||||
}
|
||||
|
||||
return acc
|
||||
}, result)
|
||||
}
|
||||
|
||||
export async function processConfigViaLegacyPlugins (projectRoot: string, legacyConfig: LegacyCypressConfigJson): Promise<LegacyCypressConfigJson> {
|
||||
const pluginFile = legacyConfig.pluginsFile ?? await tryGetDefaultLegacyPluginsFile(projectRoot)
|
||||
|
||||
@@ -49,12 +85,23 @@ export async function processConfigViaLegacyPlugins (projectRoot: string, legacy
|
||||
const CHILD_PROCESS_FILE_PATH = require.resolve('@packages/server/lib/plugins/child/require_async_child')
|
||||
const ipc = new LegacyPluginsIpc(fork(CHILD_PROCESS_FILE_PATH, configProcessArgs, childOptions))
|
||||
|
||||
const legacyConfigWithDefaults = getConfigWithDefaults(legacyConfig)
|
||||
|
||||
ipc.on('ready', () => {
|
||||
ipc.send('loadLegacyPlugins', legacyConfig)
|
||||
ipc.send('loadLegacyPlugins', legacyConfigWithDefaults)
|
||||
})
|
||||
|
||||
ipc.on('loadLegacyPlugins:reply', (modifiedLegacyConfig) => {
|
||||
resolve(modifiedLegacyConfig)
|
||||
const diff = getDiff(legacyConfigWithDefaults, modifiedLegacyConfig)
|
||||
|
||||
// if env is updated by plugins, avoid adding it to the config file
|
||||
if (diff.env) {
|
||||
delete diff.env
|
||||
}
|
||||
|
||||
const legacyConfigWithChanges = _.merge(legacyConfig, diff)
|
||||
|
||||
resolve(legacyConfigWithChanges)
|
||||
ipc.childProcess.kill()
|
||||
})
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ export type LegacyCypressConfigJson = Partial<{
|
||||
integrationFolder: string
|
||||
testFiles: string | string[]
|
||||
ignoreTestFiles: string | string[]
|
||||
env: { [key: string]: any }
|
||||
[index: string]: any
|
||||
}>
|
||||
|
||||
export interface MigrationFile {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
export * from './autoRename'
|
||||
export * from './codegen'
|
||||
export * from './format'
|
||||
export * from './legacyOptions'
|
||||
export * from './parserUtils'
|
||||
export * from './regexps'
|
||||
export * from './shouldShowSteps'
|
||||
|
||||
305
packages/data-context/src/sources/migration/legacyOptions.ts
Normal file
305
packages/data-context/src/sources/migration/legacyOptions.ts
Normal file
@@ -0,0 +1,305 @@
|
||||
interface ResolvedConfigOption {
|
||||
name: string
|
||||
defaultValue?: any
|
||||
isFolder?: boolean
|
||||
isExperimental?: boolean
|
||||
/**
|
||||
* Can be mutated with Cypress.config() or test-specific configuration overrides
|
||||
*/
|
||||
canUpdateDuringTestTime?: boolean
|
||||
}
|
||||
|
||||
interface RuntimeConfigOption {
|
||||
name: string
|
||||
defaultValue: any
|
||||
isInternal?: boolean
|
||||
/**
|
||||
* Can be mutated with Cypress.config() or test-specific configuration overrides
|
||||
*/
|
||||
canUpdateDuringTestTime?: boolean
|
||||
}
|
||||
|
||||
// NOTE:
|
||||
// If you add/remove/change a config value, make sure to update the following
|
||||
// - cli/types/index.d.ts (including allowed config options on TestOptions)
|
||||
// - cypress.schema.json
|
||||
//
|
||||
// Add options in alphabetical order for better readability
|
||||
|
||||
// TODO - add boolean attribute to indicate read-only / static vs mutable options
|
||||
// that can be updated during test executions
|
||||
const resolvedOptions: Array<ResolvedConfigOption> = [
|
||||
{
|
||||
name: 'animationDistanceThreshold',
|
||||
defaultValue: 5,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'baseUrl',
|
||||
defaultValue: null,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'blockHosts',
|
||||
defaultValue: null,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'chromeWebSecurity',
|
||||
defaultValue: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'clientCertificates',
|
||||
defaultValue: [],
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'component',
|
||||
// runner-ct overrides
|
||||
defaultValue: {},
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'componentFolder',
|
||||
defaultValue: 'cypress/component',
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'defaultCommandTimeout',
|
||||
defaultValue: 4000,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'downloadsFolder',
|
||||
defaultValue: 'cypress/downloads',
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'e2e',
|
||||
// e2e runner overrides
|
||||
defaultValue: {},
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'env',
|
||||
defaultValue: {},
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'execTimeout',
|
||||
defaultValue: 60000,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'exit',
|
||||
defaultValue: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'experimentalFetchPolyfill',
|
||||
defaultValue: false,
|
||||
isExperimental: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'experimentalInteractiveRunEvents',
|
||||
defaultValue: false,
|
||||
isExperimental: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'experimentalSessionSupport',
|
||||
defaultValue: false,
|
||||
isExperimental: true,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'experimentalSourceRewriting',
|
||||
defaultValue: false,
|
||||
isExperimental: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'experimentalStudio',
|
||||
defaultValue: false,
|
||||
isExperimental: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'fileServerFolder',
|
||||
defaultValue: '',
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'fixturesFolder',
|
||||
defaultValue: 'cypress/fixtures',
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'ignoreTestFiles',
|
||||
defaultValue: '*.hot-update.js',
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'includeShadowDom',
|
||||
defaultValue: false,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'integrationFolder',
|
||||
defaultValue: 'cypress/integration',
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'keystrokeDelay',
|
||||
defaultValue: 0,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'modifyObstructiveCode',
|
||||
defaultValue: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'nodeVersion',
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'numTestsKeptInMemory',
|
||||
defaultValue: 50,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'pageLoadTimeout',
|
||||
defaultValue: 60000,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'pluginsFile',
|
||||
defaultValue: 'cypress/plugins',
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'port',
|
||||
defaultValue: null,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'projectId',
|
||||
defaultValue: null,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'redirectionLimit',
|
||||
defaultValue: 20,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'reporter',
|
||||
defaultValue: 'spec',
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'reporterOptions',
|
||||
defaultValue: null,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'requestTimeout',
|
||||
defaultValue: 5000,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'resolvedNodePath',
|
||||
defaultValue: null,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'resolvedNodeVersion',
|
||||
defaultValue: null,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'responseTimeout',
|
||||
defaultValue: 30000,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'retries',
|
||||
defaultValue: {
|
||||
runMode: 0,
|
||||
openMode: 0,
|
||||
},
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'screenshotOnRunFailure',
|
||||
defaultValue: true,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'screenshotsFolder',
|
||||
defaultValue: 'cypress/screenshots',
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'slowTestThreshold',
|
||||
defaultValue: (options: Record<string, any> = {}) => options.testingType === 'component' ? 250 : 10000,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'scrollBehavior',
|
||||
defaultValue: 'top',
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'supportFile',
|
||||
defaultValue: 'cypress/support',
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'supportFolder',
|
||||
defaultValue: false,
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'taskTimeout',
|
||||
defaultValue: 60000,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'testFiles',
|
||||
defaultValue: '**/*.*',
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'trashAssetsBeforeRuns',
|
||||
defaultValue: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'userAgent',
|
||||
defaultValue: null,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'video',
|
||||
defaultValue: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'videoCompression',
|
||||
defaultValue: 32,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'videosFolder',
|
||||
defaultValue: 'cypress/videos',
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'videoUploadOnPasses',
|
||||
defaultValue: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'viewportHeight',
|
||||
defaultValue: 660,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'viewportWidth',
|
||||
defaultValue: 1000,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'waitForAnimations',
|
||||
defaultValue: true,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'watchForFileChanges',
|
||||
defaultValue: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
},
|
||||
]
|
||||
|
||||
const runtimeOptions: Array<RuntimeConfigOption> = [
|
||||
{
|
||||
name: 'browsers',
|
||||
defaultValue: [],
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'hosts',
|
||||
defaultValue: null,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'isInteractive',
|
||||
defaultValue: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'modifyObstructiveCode',
|
||||
defaultValue: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
},
|
||||
]
|
||||
|
||||
export const legacyOptions: Array<ResolvedConfigOption|RuntimeConfigOption> = [
|
||||
...resolvedOptions,
|
||||
...runtimeOptions,
|
||||
]
|
||||
@@ -0,0 +1,40 @@
|
||||
import { expect } from 'chai'
|
||||
import { getConfigWithDefaults, getDiff } from '../../../src/actions/MigrationActions'
|
||||
|
||||
describe('MigrationActions', () => {
|
||||
context('getConfigWithDefaults', () => {
|
||||
it('returns a config with defaults without touching the original', () => {
|
||||
const config = {
|
||||
foo: 'bar',
|
||||
}
|
||||
|
||||
expect(getConfigWithDefaults(config)).to.have.property('env')
|
||||
expect(getConfigWithDefaults(config)).to.have.property('browsers')
|
||||
expect(config).not.to.have.property('env')
|
||||
expect(config).not.to.have.property('browsers')
|
||||
})
|
||||
})
|
||||
|
||||
context('getDiff', () => {
|
||||
it('returns all the updated values', () => {
|
||||
const oldConfig = {
|
||||
foo: 'bar',
|
||||
other: 'config',
|
||||
removed: 'value',
|
||||
updated: 'oldValue',
|
||||
}
|
||||
|
||||
const newConfig = {
|
||||
foo: 'hello',
|
||||
other: 'config',
|
||||
updated: 'newValue',
|
||||
}
|
||||
|
||||
const diff = getDiff(oldConfig, newConfig)
|
||||
|
||||
expect(diff).to.have.property('foo', 'hello')
|
||||
expect(diff).to.have.property('updated', 'newValue')
|
||||
expect(diff).not.to.have.property('removed')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -10,8 +10,18 @@ describe('processConfigViaLegacyPlugins', () => {
|
||||
const result = await processConfigViaLegacyPlugins(projectRoot, {})
|
||||
|
||||
expect(result).to.eql({
|
||||
integrationFolder: 'tests/e2e',
|
||||
testFiles: '**/*.spec.js',
|
||||
'component': {
|
||||
'testFiles': '**/*.spec.ts',
|
||||
},
|
||||
'e2e': {
|
||||
'testFiles': '**/*.js',
|
||||
},
|
||||
'integrationFolder': 'tests/e2e',
|
||||
'retries': {
|
||||
'openMode': 0,
|
||||
'runMode': 1,
|
||||
},
|
||||
'testFiles': '**/*.spec.js',
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
module.exports = (on, config) => {
|
||||
config.integrationFolder = 'tests/e2e'
|
||||
config.testFiles = '**/*.spec.js'
|
||||
// In the plugin @cypress/code-coverage this pattern is used
|
||||
config.env.codeCoverageRegistered = 'true'
|
||||
|
||||
// test that browsers is an array
|
||||
config.browsers.length
|
||||
|
||||
// test that retries is an object
|
||||
config.retries.runMode = 1
|
||||
|
||||
// test component is an object
|
||||
config.component.testFiles = '**/*.spec.ts'
|
||||
|
||||
// test e2e is an object
|
||||
config.e2e.testFiles = '**/*.js'
|
||||
|
||||
// test clientCertificates is an array
|
||||
config.clientCertificates.length
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
const { defineConfig } = require('cypress')
|
||||
|
||||
module.exports = defineConfig({
|
||||
retries: {
|
||||
runMode: 1,
|
||||
openMode: 0,
|
||||
},
|
||||
e2e: {
|
||||
// We've imported your old cypress plugins here.
|
||||
// You may want to clean this up later by importing these.
|
||||
setupNodeEvents (on, config) {
|
||||
return require('./cypress/plugins/index.js')(on, config)
|
||||
},
|
||||
specPattern: 'tests/e2e/**/*.spec.js',
|
||||
specPattern: 'tests/e2e/**/*.js',
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user