From 5d52758d82c47033803c69c7858fc786a900faaf Mon Sep 17 00:00:00 2001 From: Alejandro Estrada Date: Fri, 4 Feb 2022 10:48:10 -0500 Subject: [PATCH] feat: validate specPattern root level (#19980) * feat: validate specPattern root level * Fix/add tests * Fix test * Update specExcludePattern * Fix tests * Fix/add tests * Fix/add tests * Fix tests * Rename specExcludePattern to excludeSpecPattern * Fix tests --- cli/schema/cypress.schema.json | 2 +- cli/types/cypress.d.ts | 4 +- .../schematics/ng-add/files/cypress.config.ts | 4 +- npm/design-system/cypress.config.js | 8 +- npm/react/cypress.config.js | 10 +-- .../examples/snapshots/cypress.config.js | 8 +- npm/vue/cypress.config.ts | 2 +- .../examples/react-app/cypress.config.js | 2 +- packages/config/__snapshots__/index_spec.js | 4 +- packages/config/lib/index.js | 8 +- packages/config/lib/options.ts | 31 +++++++- .../src/actions/ProjectActions.ts | 12 +-- .../src/data/ProjectLifecycleManager.ts | 18 +++++ .../src/sources/FileDataSource.ts | 6 +- .../src/sources/ProjectDataSource.ts | 16 ++-- .../unit/sources/ProjectDataSource.spec.ts | 79 ++++++++++++++++++- packages/frontend-shared/cypress.config.ts | 2 +- .../cypress/e2e/support/e2eProjectDirs.ts | 1 + .../cypress/fixtures/config.json | 2 +- packages/reporter/cypress.config.ts | 2 +- packages/runner/cypress.config.js | 2 +- packages/server/lib/controllers/files.js | 4 +- packages/server/lib/errors.js | 20 +++++ packages/server/lib/makeDataContext.ts | 1 + packages/server/lib/open_project.ts | 6 +- .../server/test/integration/cypress_spec.js | 4 +- packages/server/test/unit/config_spec.js | 29 ++++--- .../server/test/unit/open_project_spec.js | 2 +- packages/types/src/config.ts | 2 +- system-tests/__snapshots__/config_spec.js | 35 ++++++++ .../e2e/cypress-performance.config.js | 2 +- .../invalid-baseUrl-config.js | 4 + .../invalid-component-baseUrl-config.js | 5 ++ .../invalid-excludeSpecPattern.config.js | 4 + .../invalid-specPattern.config.js | 4 + .../invalid-supportFile.config.js | 4 + system-tests/test/base_url_spec.js | 8 +- system-tests/test/block_hosts_spec.js | 4 +- system-tests/test/config_spec.js | 61 ++++++++++++++ .../test/network_error_handling_spec.js | 10 ++- system-tests/test/user_agent_spec.js | 4 +- 41 files changed, 361 insertions(+), 75 deletions(-) create mode 100644 system-tests/projects/invalid-root-level-config/invalid-baseUrl-config.js create mode 100644 system-tests/projects/invalid-root-level-config/invalid-component-baseUrl-config.js create mode 100644 system-tests/projects/invalid-root-level-config/invalid-excludeSpecPattern.config.js create mode 100644 system-tests/projects/invalid-root-level-config/invalid-specPattern.config.js create mode 100644 system-tests/projects/invalid-root-level-config/invalid-supportFile.config.js diff --git a/cli/schema/cypress.schema.json b/cli/schema/cypress.schema.json index 23a1ad2502a8..c4bcd97eafda 100644 --- a/cli/schema/cypress.schema.json +++ b/cli/schema/cypress.schema.json @@ -15,7 +15,7 @@ "description": "Any values to be set as environment variables. See https://on.cypress.io/environment-variables", "body": {} }, - "ignoreSpecPattern": { + "excludeSpecPattern": { "type": [ "string", "array" diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 406f2e5e1952..cbe7be81ac97 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2631,14 +2631,14 @@ declare namespace Cypress { /** * A String or Array of glob patterns used to ignore test files that would otherwise be shown in your list of tests. Cypress uses minimatch with the options: {dot: true, matchBase: true}. We suggest using http://globtester.com to test what files would match. * @default "*.hot-update.js" - * @deprecated use `ignoreSpecPattern` instead + * @deprecated use `excludeSpecPattern` instead */ ignoreTestFiles: string | string[] /** * A String or Array of glob patterns used to ignore test files that would otherwise be shown in your list of tests. Cypress uses minimatch with the options: {dot: true, matchBase: true}. We suggest using http://globtester.com to test what files would match. * @default "*.hot-update.js" */ - ignoreSpecPattern: string | string[] + excludeSpecPattern: string | string[] /** * The number of tests for which snapshots and command data are kept in memory. Reduce this number if you are experiencing high memory consumption in your browser during a test run. * @default 50 diff --git a/npm/cypress-schematic/src/schematics/ng-add/files/cypress.config.ts b/npm/cypress-schematic/src/schematics/ng-add/files/cypress.config.ts index ad13ab513517..8541c85b73fb 100644 --- a/npm/cypress-schematic/src/schematics/ng-add/files/cypress.config.ts +++ b/npm/cypress-schematic/src/schematics/ng-add/files/cypress.config.ts @@ -1,5 +1,7 @@ import { defineConfig } from 'cypress' export default defineConfig({ - 'baseUrl': '<%= baseUrl%>', + e2e: { + 'baseUrl': '<%= baseUrl%>', + }, }) diff --git a/npm/design-system/cypress.config.js b/npm/design-system/cypress.config.js index aebf93eadb3e..2bf3b014cdc3 100644 --- a/npm/design-system/cypress.config.js +++ b/npm/design-system/cypress.config.js @@ -6,12 +6,12 @@ module.exports = { env: { reactDevtools: true, }, - ignoreSpecPattern: [ - '**/__snapshots__/*', - '**/__image_snapshots__/*', - ], fixturesFolder: false, component: { + excludeSpecPattern: [ + '**/__snapshots__/*', + '**/__image_snapshots__/*', + ], devServer (cypressConfig) { const { startDevServer } = require('@cypress/vite-dev-server') diff --git a/npm/react/cypress.config.js b/npm/react/cypress.config.js index c084c153d11c..2aeba49f8575 100644 --- a/npm/react/cypress.config.js +++ b/npm/react/cypress.config.js @@ -8,13 +8,13 @@ module.exports = { 'env': { 'reactDevtools': true, }, - 'ignoreSpecPattern': [ - '**/__snapshots__/*', - '**/__image_snapshots__/*', - ], 'experimentalFetchPolyfill': true, 'component': { - ignoreSpecPattern: 'examples/**/*', + 'excludeSpecPattern': [ + '**/__snapshots__/*', + '**/__image_snapshots__/*', + 'examples/**/*', + ], devServer (cypressConfig, devServerConfig) { const { startDevServer } = require('@cypress/webpack-dev-server') const path = require('path') diff --git a/npm/react/examples/snapshots/cypress.config.js b/npm/react/examples/snapshots/cypress.config.js index 372924884dfd..5cac5fb09b70 100644 --- a/npm/react/examples/snapshots/cypress.config.js +++ b/npm/react/examples/snapshots/cypress.config.js @@ -3,16 +3,16 @@ module.exports = { 'fixturesFolder': false, 'viewportWidth': 500, 'viewportHeight': 500, - 'ignoreSpecPattern': [ - '**/__snapshots__/*', - '**/__image_snapshots__/*', - ], 'env': { 'cypress-plugin-snapshots': { 'prettier': true, }, }, 'component': { + 'excludeSpecPattern': [ + '**/__snapshots__/*', + '**/__image_snapshots__/*', + ], setupNodeEvents (on, config) { // load file devServer that comes with this plugin // https://github.com/bahmutov/cypress-react-unit-test#install diff --git a/npm/vue/cypress.config.ts b/npm/vue/cypress.config.ts index 0753fd96cf8e..0f3851bffd27 100644 --- a/npm/vue/cypress.config.ts +++ b/npm/vue/cypress.config.ts @@ -11,7 +11,7 @@ export default defineConfig({ 'supportFile': false, }, 'component': { - ignoreSpecPattern: 'examples/**/*', + excludeSpecPattern: 'examples/**/*', devServer (cypressConfig) { const { startDevServer } = require('@cypress/webpack-dev-server') const webpackConfig = require('./webpack.config') diff --git a/npm/webpack-preprocessor/examples/react-app/cypress.config.js b/npm/webpack-preprocessor/examples/react-app/cypress.config.js index b97dacd3103c..61922473961b 100644 --- a/npm/webpack-preprocessor/examples/react-app/cypress.config.js +++ b/npm/webpack-preprocessor/examples/react-app/cypress.config.js @@ -1,8 +1,8 @@ module.exports = { - 'baseUrl': 'http://localhost:3000', 'fixturesFolder': false, 'viewportWidth': 600, 'e2e': { + 'baseUrl': 'http://localhost:3000', 'supportFile': false, }, } diff --git a/packages/config/__snapshots__/index_spec.js b/packages/config/__snapshots__/index_spec.js index 1c418858f677..fc524d0822bf 100644 --- a/packages/config/__snapshots__/index_spec.js +++ b/packages/config/__snapshots__/index_spec.js @@ -34,7 +34,7 @@ exports['src/index .getDefaultValues returns list of public config keys 1'] = { "experimentalStudio": false, "fileServerFolder": "", "fixturesFolder": "cypress/fixtures", - "ignoreSpecPattern": "*.hot-update.js", + "excludeSpecPattern": "*.hot-update.js", "includeShadowDom": false, "keystrokeDelay": 0, "modifyObstructiveCode": true, @@ -109,7 +109,7 @@ exports['src/index .getPublicConfigKeys returns list of public config keys 1'] = "experimentalStudio", "fileServerFolder", "fixturesFolder", - "ignoreSpecPattern", + "excludeSpecPattern", "includeShadowDom", "keystrokeDelay", "modifyObstructiveCode", diff --git a/packages/config/lib/index.js b/packages/config/lib/index.js index 7e2c4e510624..3ab8086a695d 100644 --- a/packages/config/lib/index.js +++ b/packages/config/lib/index.js @@ -1,7 +1,7 @@ const _ = require('lodash') const debug = require('debug')('cypress:config:validator') -const { options, breakingOptions, breakingRootOptions } = require('./options') +const { options, breakingOptions, breakingRootOptions, testingTypeBreakingOptions } = require('./options') const dashesOrUnderscoresRe = /^(_-)+/ @@ -120,6 +120,12 @@ module.exports = { return validateNoBreakingOptions(breakingOptions, cfg, onWarning, onErr) }, + validateNoBreakingTestingTypeConfig: (cfg, testingType, onWarning, onErr) => { + const options = testingTypeBreakingOptions[testingType] + + return validateNoBreakingOptions(options, cfg, onWarning, onErr) + }, + validateNoReadOnlyConfig: (config, onErr) => { let errProperty diff --git a/packages/config/lib/options.ts b/packages/config/lib/options.ts index c3a69adc91ba..2147f0974306 100644 --- a/packages/config/lib/options.ts +++ b/packages/config/lib/options.ts @@ -13,6 +13,7 @@ interface ResolvedConfigOption { * Can be mutated with Cypress.config() or test-specific configuration overrides */ canUpdateDuringTestTime?: boolean + specificTestingType?: 'e2e' | 'component' } interface RuntimeConfigOption { @@ -194,8 +195,8 @@ const resolvedOptions: Array = [ isFolder: true, canUpdateDuringTestTime: false, }, { - name: 'ignoreSpecPattern', - defaultValue: '*.hot-update.js', + name: 'excludeSpecPattern', + defaultValue: (options: Record = {}) => options.testingType === 'component' ? ['**/__snapshots__/*', '**/__image_snapshots__/*'] : '*.hot-update.js', validation: validate.isStringOrArrayOfStrings, canUpdateDuringTestTime: true, }, { @@ -540,4 +541,30 @@ export const breakingRootOptions: Array = [ errorKey: 'SUPPORT_FILE_ROOT_NOT_SUPPORTED', isWarning: false, }, + { + name: 'specPattern', + errorKey: 'SPEC_PATTERN_ROOT_NOT_SUPPORTED', + isWarning: false, + }, + { + name: 'excludeSpecPattern', + errorKey: 'SPEC_EXCLUDE_PATTERN_ROOT_NOT_SUPPORTED', + isWarning: false, + }, + { + name: 'baseUrl', + errorKey: 'BASE_URL_ROOT_NOT_SUPPORTED', + isWarning: false, + }, ] + +export const testingTypeBreakingOptions: { e2e: Array, component: Array } = { + e2e: [], + component: [ + { + name: 'baseUrl', + errorKey: 'BASE_URL_CT_NOT_SUPPORTED', + isWarning: false, + }, + ], +} diff --git a/packages/data-context/src/actions/ProjectActions.ts b/packages/data-context/src/actions/ProjectActions.ts index 865254bc8d40..365b9937a93d 100644 --- a/packages/data-context/src/actions/ProjectActions.ts +++ b/packages/data-context/src/actions/ProjectActions.ts @@ -40,7 +40,7 @@ type SetSpecsFoundBySpecPattern = { path: string testingType: Cypress.TestingType specPattern?: Cypress.Config['specPattern'] - ignoreSpecPattern?: Cypress.Config['ignoreSpecPattern'] + excludeSpecPattern?: Cypress.Config['excludeSpecPattern'] additionalIgnorePattern?: string | string[] } @@ -422,7 +422,7 @@ export class ProjectActions { path: this.ctx.currentProject, testingType, specPattern: cfg[testingType]?.specPattern, - ignoreSpecPattern: cfg[testingType]?.ignoreSpecPattern, + excludeSpecPattern: cfg[testingType]?.excludeSpecPattern, additionalIgnorePattern: testingType === 'component' ? cfg?.e2e?.specPattern : undefined, }) @@ -440,12 +440,12 @@ export class ProjectActions { } } - async setSpecsFoundBySpecPattern ({ path, testingType, specPattern, ignoreSpecPattern, additionalIgnorePattern }: SetSpecsFoundBySpecPattern) { + async setSpecsFoundBySpecPattern ({ path, testingType, specPattern, excludeSpecPattern, additionalIgnorePattern }: SetSpecsFoundBySpecPattern) { const toArray = (val?: string | string[]) => val ? typeof val === 'string' ? [val] : val : undefined specPattern = toArray(specPattern) - ignoreSpecPattern = toArray(ignoreSpecPattern) || [] + excludeSpecPattern = toArray(excludeSpecPattern) || [] // exclude all specs matching e2e if in component testing additionalIgnorePattern = toArray(additionalIgnorePattern) || [] @@ -458,13 +458,13 @@ export class ProjectActions { path, testingType, specPattern, - ignoreSpecPattern, + excludeSpecPattern, additionalIgnorePattern, ) this.ctx.actions.project.setSpecs(specs) - return { specs, specPattern, ignoreSpecPattern, additionalIgnorePattern } + return { specs, specPattern, excludeSpecPattern, additionalIgnorePattern } } async reconfigureProject () { diff --git a/packages/data-context/src/data/ProjectLifecycleManager.ts b/packages/data-context/src/data/ProjectLifecycleManager.ts index b306cda65d56..dbf70db05339 100644 --- a/packages/data-context/src/data/ProjectLifecycleManager.ts +++ b/packages/data-context/src/data/ProjectLifecycleManager.ts @@ -50,6 +50,7 @@ export interface InjectedConfigApi { updateWithPluginValues(config: FullConfig, modifiedConfig: Partial): FullConfig setupFullConfigWithDefaults(config: SetupFullConfigOptions): Promise validateRootConfigBreakingChanges(config: Partial, onWarning: (warningMsg: string) => void, onErr: (errMsg: string) => never): T + validateTestingTypeConfigBreakingChanges(config: Partial, testingType: Cypress.TestingType, onWarning: (warningMsg: string) => void, onErr: (errMsg: string) => never): T } type State = V extends undefined ? {state: S, value?: V } : {state: S, value: V} @@ -442,6 +443,8 @@ export class ProjectLifecycleManager { if (this._currentTestingType) { const testingTypeOverrides = configFileContents[this._currentTestingType] ?? {} + this.validateTestingTypeConfig(testingTypeOverrides) + // TODO: pass in options.config overrides separately, so they are reflected in the UI configFileContents = { ...configFileContents, ...testingTypeOverrides } } @@ -563,6 +566,21 @@ export class ProjectLifecycleManager { return promise.then((v) => v.initialConfig) } + private validateTestingTypeConfig (config: Cypress.ConfigOptions) { + assert(this._currentTestingType) + + return this.ctx._apis.configApi.validateTestingTypeConfigBreakingChanges( + config, + this._currentTestingType, + (warning, ...args) => { + return this.ctx.warning(warning, ...args) + }, + (err, ...args) => { + throw this.ctx.error(err, ...args) + }, + ) + } + private validateConfigRoot (config: Cypress.ConfigOptions) { return this.ctx._apis.configApi.validateRootConfigBreakingChanges( config, diff --git a/packages/data-context/src/sources/FileDataSource.ts b/packages/data-context/src/sources/FileDataSource.ts index 1a43fed3a1ac..d84e6663e37d 100644 --- a/packages/data-context/src/sources/FileDataSource.ts +++ b/packages/data-context/src/sources/FileDataSource.ts @@ -31,7 +31,9 @@ export class FileDataSource { } async getFilesByGlob (cwd: string, glob: string | string[], globOptions?: GlobbyOptions) { - const globs = (Array.isArray(glob) ? glob : [glob]).concat('!**/node_modules/**') + const globs = Array.isArray(glob) ? glob : [glob] + + const ignoreGlob = globOptions && Array.isArray(globOptions?.ignore) ? globOptions.ignore.concat('**/node_modules/**') : ['**/node_modules/**'] if (process.platform === 'win32') { // globby can't work with backwards slashes @@ -46,7 +48,7 @@ export class FileDataSource { } try { - const files = await globby(globs, { onlyFiles: true, absolute: true, cwd, ...globOptions }) + const files = await globby(globs, { onlyFiles: true, absolute: true, cwd, ...globOptions, ignore: ignoreGlob }) return files } catch (e) { diff --git a/packages/data-context/src/sources/ProjectDataSource.ts b/packages/data-context/src/sources/ProjectDataSource.ts index 2d2bd4426a76..12fea86cbeb1 100644 --- a/packages/data-context/src/sources/ProjectDataSource.ts +++ b/packages/data-context/src/sources/ProjectDataSource.ts @@ -140,7 +140,7 @@ export class ProjectDataSource { async specPatternsForTestingType (projectRoot: string, testingType: Cypress.TestingType): Promise<{ specPattern?: string[] - ignoreSpecPattern?: string[] + excludeSpecPattern?: string[] }> { const toArray = (val?: string | string[]) => val ? typeof val === 'string' ? [val] : val : undefined @@ -152,7 +152,7 @@ export class ProjectDataSource { return { specPattern: toArray(config[testingType]?.specPattern), - ignoreSpecPattern: toArray(config[testingType]?.ignoreSpecPattern), + excludeSpecPattern: toArray(config[testingType]?.excludeSpecPattern), } } @@ -160,14 +160,14 @@ export class ProjectDataSource { projectRoot: string, testingType: Cypress.TestingType, specPattern: string[], - ignoreSpecPattern: string[], + excludeSpecPattern: string[], globToRemove: string[], ): Promise { const specAbsolutePaths = await this.ctx.file.getFilesByGlob( projectRoot, specPattern, { absolute: true, - ignore: [...ignoreSpecPattern, ...globToRemove], + ignore: [...excludeSpecPattern, ...globToRemove], }, ) @@ -185,7 +185,7 @@ export class ProjectDataSource { projectRoot: string, testingType: Cypress.TestingType, specPattern: string[], - ignoreSpecPattern: string[], + excludeSpecPattern: string[], additionalIgnore: string[], ) { this.stopSpecWatcher() @@ -197,7 +197,7 @@ export class ProjectDataSource { } const onSpecsChanged = debounce(async () => { - const specs = await this.findSpecs(projectRoot, testingType, specPattern, ignoreSpecPattern, additionalIgnore) + const specs = await this.findSpecs(projectRoot, testingType, specPattern, excludeSpecPattern, additionalIgnore) this.setSpecs(specs) @@ -220,9 +220,9 @@ export class ProjectDataSource { const MINIMATCH_OPTIONS = { dot: true, matchBase: true } - const { specPattern = [], ignoreSpecPattern = [] } = await this.ctx.project.specPatternsForTestingType(this.ctx.currentProject, this.ctx.coreData.currentTestingType) + const { specPattern = [], excludeSpecPattern = [] } = await this.ctx.project.specPatternsForTestingType(this.ctx.currentProject, this.ctx.coreData.currentTestingType) - for (const pattern of ignoreSpecPattern) { + for (const pattern of excludeSpecPattern) { if (minimatch(specFile, pattern, MINIMATCH_OPTIONS)) { return false } diff --git a/packages/data-context/test/unit/sources/ProjectDataSource.spec.ts b/packages/data-context/test/unit/sources/ProjectDataSource.spec.ts index 53956e4765e7..83392bceb77b 100644 --- a/packages/data-context/test/unit/sources/ProjectDataSource.spec.ts +++ b/packages/data-context/test/unit/sources/ProjectDataSource.spec.ts @@ -1,5 +1,11 @@ import { expect } from 'chai' -import { matchedSpecs, transformSpec, SpecWithRelativeRoot } from '../../../src/sources' +import { matchedSpecs, transformSpec, SpecWithRelativeRoot, BrowserApiShape } from '../../../src/sources' +import path from 'path' +import { DataContext } from '../../../src' +import { graphqlSchema } from '@packages/graphql/src/schema' +import { AppApiShape, AuthApiShape, ElectronApiShape, LocalSettingsApiShape, ProjectApiShape } from '../../../src/actions' +import { ErrorApiShape } from '../../../src/DataContext' +import { InjectedConfigApi } from '../../../src/data' describe('matchedSpecs', () => { context('got a single spec pattern from --spec via cli', () => { @@ -114,3 +120,74 @@ describe('transformSpec', () => { expect(result).to.eql(actual) }) }) + +describe('findSpecs', () => { + const temporary = 'tmp' + + const fixture = [ + 'node_modules/test/App.spec.js', + 'packages/node_modules/folder/App.spec.js', + 'component/App.spec.ts', + 'component/App.cy.ts', + 'component/App.cy.js', + 'e2e/onboarding.spec.ts', + 'e2e/onboarding.cy.ts', + 'e2e/onboarding.cy.js', + ] + + let ctx: DataContext + + beforeEach(async () => { + ctx = new DataContext({ + schema: graphqlSchema, + mode: 'run', + modeOptions: {}, + appApi: {} as AppApiShape, + localSettingsApi: {} as LocalSettingsApiShape, + authApi: {} as AuthApiShape, + errorApi: {} as ErrorApiShape, + configApi: { + getServerPluginHandlers: () => [], + } as InjectedConfigApi, + projectApi: {} as ProjectApiShape, + electronApi: {} as ElectronApiShape, + browserApi: {} as BrowserApiShape, + }) + + await Promise.all(fixture.map((element) => ctx.fs.outputFile(path.join(temporary, element), ''))) + }) + + afterEach(async () => { + await ctx.fs.remove(temporary) + }) + + it('find all the *.cy.{ts,js} excluding the e2e', async () => { + const specs = await ctx.project.findSpecs(temporary, 'component', ['**/*.cy.{ts,js}'], ['e2e/*.{spec,cy}.{ts,js}'], []) + + expect(specs).to.have.length(2) + }) + + it('find all the *.{cy,spec}.{ts,js} excluding the e2e', async () => { + const specs = await ctx.project.findSpecs(temporary, 'component', ['**/*.{cy,spec}.{ts,js}'], ['e2e/*.{spec,cy}.{ts,js}'], []) + + expect(specs).to.have.length(3) + }) + + it('find all the e2e specs', async () => { + const specs = await ctx.project.findSpecs(temporary, 'e2e', ['e2e/*.{cy,spec}.{ts,js}'], [], []) + + expect(specs).to.have.length(3) + }) + + it('ignores node_modules if excludeSpecPattern is empty array', async () => { + const specs = await ctx.project.findSpecs(temporary, 'component', ['**/*.{cy,spec}.{ts,js}'], [], []) + + expect(specs).to.have.length(6) + }) + + it('ignores e2e tests if globToRemove is set', async () => { + const specs = await ctx.project.findSpecs(temporary, 'component', ['**/*.{cy,spec}.{ts,js}'], [], ['e2e/*.{spec,cy}.{ts,js}']) + + expect(specs).to.have.length(3) + }) +}) diff --git a/packages/frontend-shared/cypress.config.ts b/packages/frontend-shared/cypress.config.ts index 3fd034bcd5e9..e15c16e00926 100644 --- a/packages/frontend-shared/cypress.config.ts +++ b/packages/frontend-shared/cypress.config.ts @@ -5,7 +5,6 @@ const CYPRESS_INTERNAL_CLOUD_ENV = getenv('CYPRESS_INTERNAL_CLOUD_ENV', process. export default defineConfig({ projectId: CYPRESS_INTERNAL_CLOUD_ENV === 'staging' ? 'ypt4pf' : 'sehy69', - baseUrl: 'http://localhost:5555', viewportWidth: 800, viewportHeight: 850, retries: { @@ -36,6 +35,7 @@ export default defineConfig({ }, }, 'e2e': { + baseUrl: 'http://localhost:5555', 'supportFile': 'cypress/e2e/support/e2eSupport.ts', }, }) diff --git a/packages/frontend-shared/cypress/e2e/support/e2eProjectDirs.ts b/packages/frontend-shared/cypress/e2e/support/e2eProjectDirs.ts index 008a3aab9c1f..dd19d8cf88f5 100644 --- a/packages/frontend-shared/cypress/e2e/support/e2eProjectDirs.ts +++ b/packages/frontend-shared/cypress/e2e/support/e2eProjectDirs.ts @@ -25,6 +25,7 @@ export const e2eProjectDirs = [ 'hooks-after-rerun', 'ids', 'integration-outside-project-root', + 'invalid-root-level-config', 'issue-8111-iframe-input', 'kill-child-process', 'launchpad', diff --git a/packages/frontend-shared/cypress/fixtures/config.json b/packages/frontend-shared/cypress/fixtures/config.json index 1bf029dd869e..fc6f8e86e7d5 100644 --- a/packages/frontend-shared/cypress/fixtures/config.json +++ b/packages/frontend-shared/cypress/fixtures/config.json @@ -131,7 +131,7 @@ { "value": "*.hot-update.js", "from": "default", - "field": "ignoreSpecPattern" + "field": "excludeSpecPattern" }, { "value": false, diff --git a/packages/reporter/cypress.config.ts b/packages/reporter/cypress.config.ts index 98a7a2e291d0..0cf318c7c85c 100644 --- a/packages/reporter/cypress.config.ts +++ b/packages/reporter/cypress.config.ts @@ -2,7 +2,6 @@ import { defineConfig } from 'cypress' export default defineConfig({ 'projectId': 'ypt4pf', - 'baseUrl': 'http://localhost:5006', 'viewportWidth': 400, 'viewportHeight': 450, 'reporter': '../../node_modules/cypress-multi-reporters/index.js', @@ -14,6 +13,7 @@ export default defineConfig({ 'openMode': 0, }, 'e2e': { + 'baseUrl': 'http://localhost:5006', setupNodeEvents (on, config) { const express = require('express') diff --git a/packages/runner/cypress.config.js b/packages/runner/cypress.config.js index 9d33db563e1b..3ce5f92c3f23 100644 --- a/packages/runner/cypress.config.js +++ b/packages/runner/cypress.config.js @@ -1,6 +1,5 @@ module.exports = { 'projectId': 'ypt4pf', - 'baseUrl': 'http://localhost:3500', 'retries': { 'runMode': 2, 'openMode': 0, @@ -10,6 +9,7 @@ module.exports = { 'configFile': '../../mocha-reporter-config.json', }, 'e2e': { + 'baseUrl': 'http://localhost:3500', 'setupNodeEvents': require('./cypress/plugins'), }, } diff --git a/packages/server/lib/controllers/files.js b/packages/server/lib/controllers/files.js index 6a03d064f035..2b4e25159864 100644 --- a/packages/server/lib/controllers/files.js +++ b/packages/server/lib/controllers/files.js @@ -79,8 +79,8 @@ module.exports = { // component.specPattern: src/components/**/*.cy.ts // in this case, we want to remove anything that matches // - the component.specPattern - // - the e2e.ignoreSpecPattern - return ctx.project.findSpecs(config.projectRoot, 'e2e', e2ePatterns.specPattern, e2ePatterns.ignoreSpecPattern, componentPatterns.specPattern) + // - the e2e.excludeSpecPattern + return ctx.project.findSpecs(config.projectRoot, 'e2e', e2ePatterns.specPattern, e2ePatterns.excludeSpecPattern, componentPatterns.specPattern) .then((specs) => { debug('found __all specs %o', specs) diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 7345011ef667..0b5a30c934fa 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -1063,6 +1063,26 @@ const getMsgByType = function (type, ...args) { return stripIndent`\ The ${chalk.yellow(`\`supportFile\``)} configuration option was removed from the root in Cypress version \`10.0.0\`. Please update this option under each testing type property. + https://on.cypress.io/migration-guide` + case 'SPEC_PATTERN_ROOT_NOT_SUPPORTED': + return stripIndent`\ + The ${chalk.yellow(`\`specPattern\``)} configuration option was removed from the root in Cypress version \`10.0.0\`. Please update this option under each testing type property. + + https://on.cypress.io/migration-guide` + case 'SPEC_EXCLUDE_PATTERN_ROOT_NOT_SUPPORTED': + return stripIndent`\ + The ${chalk.yellow(`\`excludeSpecPattern\``)} configuration option was removed from the root in Cypress version \`10.0.0\`. Please update this option under each testing type property. + + https://on.cypress.io/migration-guide` + case 'BASE_URL_ROOT_NOT_SUPPORTED': + return stripIndent`\ + The ${chalk.yellow(`\`baseUrl\``)} configuration option was removed from the root in Cypress version \`10.0.0\`. Please update this option under e2e testing type property. + + https://on.cypress.io/migration-guide` + case 'BASE_URL_CT_NOT_SUPPORTED': + return stripIndent`\ + The ${chalk.yellow(`\`baseUrl\``)} configuration option is not valid in Component testing. Please update this option under e2e testing type property. + https://on.cypress.io/migration-guide` default: } diff --git a/packages/server/lib/makeDataContext.ts b/packages/server/lib/makeDataContext.ts index a626d2234d5e..d94849089512 100644 --- a/packages/server/lib/makeDataContext.ts +++ b/packages/server/lib/makeDataContext.ts @@ -68,6 +68,7 @@ export function makeDataContext (options: MakeDataContextOptions): DataContext { updateWithPluginValues: config.updateWithPluginValues, setupFullConfigWithDefaults: config.setupFullConfigWithDefaults, validateRootConfigBreakingChanges: configUtils.validateNoBreakingConfigRoot, + validateTestingTypeConfigBreakingChanges: configUtils.validateNoBreakingTestingTypeConfig, }, appApi: { appData, diff --git a/packages/server/lib/open_project.ts b/packages/server/lib/open_project.ts index 512f60f23f48..550e9ac6481a 100644 --- a/packages/server/lib/open_project.ts +++ b/packages/server/lib/open_project.ts @@ -263,15 +263,15 @@ export class OpenProject { try { const cfg = await this.projectBase.initializeConfig() - const { specPattern, ignoreSpecPattern, additionalIgnorePattern } = await this._ctx.actions.project.setSpecsFoundBySpecPattern({ + const { specPattern, excludeSpecPattern, additionalIgnorePattern } = await this._ctx.actions.project.setSpecsFoundBySpecPattern({ path, testingType, specPattern: options.spec || cfg[testingType].specPattern, - ignoreSpecPattern: cfg[testingType].ignoreSpecPattern, + excludeSpecPattern: cfg[testingType].excludeSpecPattern, additionalIgnorePattern: testingType === 'component' ? cfg?.e2e?.specPattern : undefined, }) - this._ctx.project.startSpecWatcher(path, testingType, specPattern, ignoreSpecPattern, additionalIgnorePattern) + this._ctx.project.startSpecWatcher(path, testingType, specPattern, excludeSpecPattern, additionalIgnorePattern) await this.projectBase.open() } catch (err: any) { diff --git a/packages/server/test/integration/cypress_spec.js b/packages/server/test/integration/cypress_spec.js index fd18969f785b..7c121eb3f22e 100644 --- a/packages/server/test/integration/cypress_spec.js +++ b/packages/server/test/integration/cypress_spec.js @@ -1655,9 +1655,9 @@ describe('lib/cypress', () => { // this should be overriden by the env argument json.baseUrl = 'http://localhost:8080' - const { supportFile, ...rest } = json + const { supportFile, specPattern, excludeSpecPattern, baseUrl, ...rest } = json - return settings.writeForTesting(this.todosPath, rest) + return settings.writeForTesting(this.todosPath, { ...rest, e2e: { baseUrl, supportFile, specPattern, excludeSpecPattern } }) }).then(() => { return cypress.start([ '--port=2121', diff --git a/packages/server/test/unit/config_spec.js b/packages/server/test/unit/config_spec.js index 8c03b9d54d03..25136fe2efb4 100644 --- a/packages/server/test/unit/config_spec.js +++ b/packages/server/test/unit/config_spec.js @@ -188,28 +188,34 @@ describe('lib/config', () => { context('baseUrl', () => { it('passes if begins with http://', function () { - this.setup({ baseUrl: 'http://example.com' }) + this.setup({ e2e: { baseUrl: 'http://example.com', supportFile: false } }) return this.expectValidationPasses() }) it('passes if begins with https://', function () { - this.setup({ baseUrl: 'https://example.com' }) + this.setup({ e2e: { baseUrl: 'https://example.com', supportFile: false } }) return this.expectValidationPasses() }) it('fails if not a string', function () { - this.setup({ baseUrl: false }) + this.setup({ e2e: { baseUrl: false } }) return this.expectValidationFails('be a fully qualified URL') }) it('fails if not a fully qualified url', function () { - this.setup({ baseUrl: 'localhost' }) + this.setup({ e2e: { baseUrl: 'localhost' } }) return this.expectValidationFails('be a fully qualified URL') }) + + it('fails if it is set on root level', function () { + this.setup({ baseUrl: 'localhost' }) + + return this.expectValidationFails('Please update this option under e2e testing type property') + }) }) context('chromeWebSecurity', () => { @@ -246,7 +252,6 @@ describe('lib/config', () => { it('passes if an object with valid properties', function () { this.setup({ component: { - baseUrl: 'https://cypress.com', execTimeout: 10000, }, }) @@ -393,27 +398,27 @@ describe('lib/config', () => { }) }) - context('ignoreSpecPattern', () => { + context('excludeSpecPattern', () => { it('passes if a string', function () { - this.setup({ ignoreSpecPattern: '*.jsx' }) + this.setup({ e2e: { excludeSpecPattern: '*.jsx', supportFile: false } }) return this.expectValidationPasses() }) it('passes if an array of strings', function () { - this.setup({ ignoreSpecPattern: ['*.jsx'] }) + this.setup({ e2e: { excludeSpecPattern: ['*.jsx'], supportFile: false } }) return this.expectValidationPasses() }) it('fails if not a string or array', function () { - this.setup({ ignoreSpecPattern: 5 }) + this.setup({ e2e: { excludeSpecPattern: 5 } }) return this.expectValidationFails('be a string or an array of strings') }) it('fails if not an array of strings', function () { - this.setup({ ignoreSpecPattern: [5] }) + this.setup({ e2e: { excludeSpecPattern: [5] } }) this.expectValidationFails('be a string or an array of strings') return this.expectValidationFails('the value was: `[5]`') @@ -1439,7 +1444,7 @@ describe('lib/config', () => { fileServerFolder: { value: '', from: 'default' }, fixturesFolder: { value: 'cypress/fixtures', from: 'default' }, hosts: { value: null, from: 'default' }, - ignoreSpecPattern: { value: '*.hot-update.js', from: 'default' }, + excludeSpecPattern: { value: '*.hot-update.js', from: 'default' }, includeShadowDom: { value: false, from: 'default' }, isInteractive: { value: true, from: 'default' }, keystrokeDelay: { value: 0, from: 'default' }, @@ -1550,7 +1555,7 @@ describe('lib/config', () => { fileServerFolder: { value: '', from: 'default' }, fixturesFolder: { value: 'cypress/fixtures', from: 'default' }, hosts: { value: null, from: 'default' }, - ignoreSpecPattern: { value: '*.hot-update.js', from: 'default' }, + excludeSpecPattern: { value: '*.hot-update.js', from: 'default' }, includeShadowDom: { value: false, from: 'default' }, isInteractive: { value: true, from: 'default' }, keystrokeDelay: { value: 0, from: 'default' }, diff --git a/packages/server/test/unit/open_project_spec.js b/packages/server/test/unit/open_project_spec.js index 278e38dafe25..de522d685425 100644 --- a/packages/server/test/unit/open_project_spec.js +++ b/packages/server/test/unit/open_project_spec.js @@ -19,7 +19,7 @@ describe('lib/open_project', () => { } this.config = { - ignoreSpecPattern: '**/*.nope', + excludeSpecPattern: '**/*.nope', projectRoot: todosPath, } diff --git a/packages/types/src/config.ts b/packages/types/src/config.ts index f684cea962db..bef12ee623ff 100644 --- a/packages/types/src/config.ts +++ b/packages/types/src/config.ts @@ -29,7 +29,7 @@ export interface FullConfig extends Partial - & Pick // TODO: Figure out how to type this better. + & Pick // TODO: Figure out how to type this better. export interface SampleConfigFile{ status: 'changes' | 'valid' | 'skipped' | 'error' diff --git a/system-tests/__snapshots__/config_spec.js b/system-tests/__snapshots__/config_spec.js index 39ab961dd30b..ad9e31d92894 100644 --- a/system-tests/__snapshots__/config_spec.js +++ b/system-tests/__snapshots__/config_spec.js @@ -181,4 +181,39 @@ There is both a \`cypress.config.js\` and a cypress.json file at the location be Cypress no longer supports 'cypress.json' config, please remove it from your project. +` + +exports['e2e config throws an error if supportFile is set on the root level 1'] = ` +The \`supportFile\` configuration option was removed from the root in Cypress version \`10.0.0\`. Please update this option under each testing type property. + +https://on.cypress.io/migration-guide + +` + +exports['e2e config throws an error if specPattern is set on the root level 1'] = ` +The \`specPattern\` configuration option was removed from the root in Cypress version \`10.0.0\`. Please update this option under each testing type property. + +https://on.cypress.io/migration-guide + +` + +exports['e2e config throws an error if excludeSpecPattern is set on the root level 1'] = ` +The \`excludeSpecPattern\` configuration option was removed from the root in Cypress version \`10.0.0\`. Please update this option under each testing type property. + +https://on.cypress.io/migration-guide + +` + +exports['e2e config throws an error if baseUrl is set on the root level 1'] = ` +The \`baseUrl\` configuration option was removed from the root in Cypress version \`10.0.0\`. Please update this option under e2e testing type property. + +https://on.cypress.io/migration-guide + +` + +exports['e2e config throws an error if baseUrl is set on the component level 1'] = ` +The \`baseUrl\` configuration option is not valid in Component testing. Please update this option under e2e testing type property. + +https://on.cypress.io/migration-guide + ` diff --git a/system-tests/projects/e2e/cypress-performance.config.js b/system-tests/projects/e2e/cypress-performance.config.js index 8b9907ba8b96..60cd60e846c8 100644 --- a/system-tests/projects/e2e/cypress-performance.config.js +++ b/system-tests/projects/e2e/cypress-performance.config.js @@ -1,7 +1,7 @@ module.exports = { - baseUrl: 'http://localhost:3434', video: false, 'e2e': { + baseUrl: 'http://localhost:3434', setupNodeEvents: require('./cypress/plugins'), }, } diff --git a/system-tests/projects/invalid-root-level-config/invalid-baseUrl-config.js b/system-tests/projects/invalid-root-level-config/invalid-baseUrl-config.js new file mode 100644 index 000000000000..b6ff57558986 --- /dev/null +++ b/system-tests/projects/invalid-root-level-config/invalid-baseUrl-config.js @@ -0,0 +1,4 @@ +module.exports = { + baseUrl: 'https://foo.com', + e2e: {}, +} diff --git a/system-tests/projects/invalid-root-level-config/invalid-component-baseUrl-config.js b/system-tests/projects/invalid-root-level-config/invalid-component-baseUrl-config.js new file mode 100644 index 000000000000..6c1052833a10 --- /dev/null +++ b/system-tests/projects/invalid-root-level-config/invalid-component-baseUrl-config.js @@ -0,0 +1,5 @@ +module.exports = { + component: { + baseUrl: 'https://foo.com', + }, +} diff --git a/system-tests/projects/invalid-root-level-config/invalid-excludeSpecPattern.config.js b/system-tests/projects/invalid-root-level-config/invalid-excludeSpecPattern.config.js new file mode 100644 index 000000000000..7938eed00f2e --- /dev/null +++ b/system-tests/projects/invalid-root-level-config/invalid-excludeSpecPattern.config.js @@ -0,0 +1,4 @@ +module.exports = { + excludeSpecPattern: 'src/**/*.cy.js', + e2e: {}, +} diff --git a/system-tests/projects/invalid-root-level-config/invalid-specPattern.config.js b/system-tests/projects/invalid-root-level-config/invalid-specPattern.config.js new file mode 100644 index 000000000000..940b36507de9 --- /dev/null +++ b/system-tests/projects/invalid-root-level-config/invalid-specPattern.config.js @@ -0,0 +1,4 @@ +module.exports = { + specPattern: 'src/**/*.cy.js', + e2e: {}, +} diff --git a/system-tests/projects/invalid-root-level-config/invalid-supportFile.config.js b/system-tests/projects/invalid-root-level-config/invalid-supportFile.config.js new file mode 100644 index 000000000000..ddabeea46a95 --- /dev/null +++ b/system-tests/projects/invalid-root-level-config/invalid-supportFile.config.js @@ -0,0 +1,4 @@ +module.exports = { + supportFile: 'cypress/support/e2e.js', + e2e: {}, +} diff --git a/system-tests/test/base_url_spec.js b/system-tests/test/base_url_spec.js index 3820e8b511e6..556522e1245e 100644 --- a/system-tests/test/base_url_spec.js +++ b/system-tests/test/base_url_spec.js @@ -10,7 +10,9 @@ describe('e2e baseUrl', () => { context('https', () => { systemTests.setup({ settings: { - baseUrl: 'https://httpbin.org', + e2e: { + baseUrl: 'https://httpbin.org', + }, }, }) @@ -27,7 +29,9 @@ describe('e2e baseUrl', () => { onServer, }, settings: { - baseUrl: 'http://localhost:9999/app', + e2e: { + baseUrl: 'http://localhost:9999/app', + }, }, }) diff --git a/system-tests/test/block_hosts_spec.js b/system-tests/test/block_hosts_spec.js index 7697c028aa93..cb49d8694535 100644 --- a/system-tests/test/block_hosts_spec.js +++ b/system-tests/test/block_hosts_spec.js @@ -24,9 +24,11 @@ describe('e2e blockHosts', () => { onServer, }], settings: { - baseUrl: 'http://localhost:3232', blockHosts: 'localhost:3131', video: false, + e2e: { + baseUrl: 'http://localhost:3232', + }, }, }) diff --git a/system-tests/test/config_spec.js b/system-tests/test/config_spec.js index 3fe84249a35a..728c813e9e05 100644 --- a/system-tests/test/config_spec.js +++ b/system-tests/test/config_spec.js @@ -112,4 +112,65 @@ describe('e2e config', () => { snapshot: true, }) }) + + it('throws an error if supportFile is set on the root level', function () { + Fixtures.scaffoldProject('invalid-root-level-config') + Fixtures.projectPath('invalid-root-level-config') + + return systemTests.exec(this, { + project: 'invalid-root-level-config', + configFile: 'invalid-supportFile.config.js', + expectedExitCode: 1, + snapshot: true, + }) + }) + + it('throws an error if specPattern is set on the root level', function () { + Fixtures.scaffoldProject('invalid-root-level-config') + Fixtures.projectPath('invalid-root-level-config') + + return systemTests.exec(this, { + project: 'invalid-root-level-config', + configFile: 'invalid-specPattern.config.js', + expectedExitCode: 1, + snapshot: true, + }) + }) + + it('throws an error if excludeSpecPattern is set on the root level', function () { + Fixtures.scaffoldProject('invalid-root-level-config') + Fixtures.projectPath('invalid-root-level-config') + + return systemTests.exec(this, { + project: 'invalid-root-level-config', + configFile: 'invalid-excludeSpecPattern.config.js', + expectedExitCode: 1, + snapshot: true, + }) + }) + + it('throws an error if baseUrl is set on the root level', function () { + Fixtures.scaffoldProject('invalid-root-level-config') + Fixtures.projectPath('invalid-root-level-config') + + return systemTests.exec(this, { + project: 'invalid-root-level-config', + configFile: 'invalid-baseUrl-config.js', + expectedExitCode: 1, + snapshot: true, + }) + }) + + it('throws an error if baseUrl is set on the component level', function () { + Fixtures.scaffoldProject('invalid-root-level-config') + Fixtures.projectPath('invalid-root-level-config') + + return systemTests.exec(this, { + project: 'invalid-root-level-config', + configFile: 'invalid-component-baseUrl-config.js', + testingType: 'component', + expectedExitCode: 1, + snapshot: true, + }) + }) }) diff --git a/system-tests/test/network_error_handling_spec.js b/system-tests/test/network_error_handling_spec.js index 7c0eaffc840c..a80b5a761924 100644 --- a/system-tests/test/network_error_handling_spec.js +++ b/system-tests/test/network_error_handling_spec.js @@ -242,7 +242,9 @@ describe('e2e network error handling', function () { }, ], settings: { - baseUrl: `http://localhost:${PORT}/`, + e2e: { + baseUrl: `http://localhost:${PORT}/`, + }, }, }) @@ -457,8 +459,8 @@ describe('e2e network error handling', function () { spec: 'network_error_304_handling.cy.js', video: false, config: { - baseUrl: `http://localhost:${PORT}`, pageLoadTimeout: 4000, + baseUrl: `http://localhost:${PORT}`, }, snapshot: true, }) @@ -477,8 +479,8 @@ describe('e2e network error handling', function () { spec: 'network_error_304_handling.cy.js', video: false, config: { - baseUrl: `http://localhost:${PORT}`, pageLoadTimeout: 4000, + baseUrl: `http://localhost:${PORT}`, }, snapshot: true, }) @@ -509,8 +511,8 @@ describe('e2e network error handling', function () { spec: 'network_error_304_handling.cy.js', video: false, config: { - baseUrl: `http://localhost:${PORT}`, pageLoadTimeout: 4000, + baseUrl: `http://localhost:${PORT}`, }, snapshot: true, }) diff --git a/system-tests/test/user_agent_spec.js b/system-tests/test/user_agent_spec.js index 6bfe0dd44fa2..fcf36431b10d 100644 --- a/system-tests/test/user_agent_spec.js +++ b/system-tests/test/user_agent_spec.js @@ -22,7 +22,9 @@ describe('e2e user agent', () => { }, settings: { userAgent: 'foo bar baz agent', - baseUrl: 'http://localhost:4545', + e2e: { + baseUrl: 'http://localhost:4545', + }, }, })