diff --git a/.eslintignore b/.eslintignore index 63bb87eaf826..ef89afbf8c34 100644 --- a/.eslintignore +++ b/.eslintignore @@ -87,6 +87,7 @@ system-tests/projects/create-react-app-custom-index-html system-tests/projects/vueclivue2-unconfigured/**/* system-tests/projects/vueclivue2-configured/**/* +system-tests/projects/outdated-deps-vuecli3/**/* system-tests/projects/vueclivue3-unconfigured/**/* system-tests/projects/vueclivue3-configured/**/* diff --git a/packages/data-context/src/data/ProjectConfigManager.ts b/packages/data-context/src/data/ProjectConfigManager.ts index 8e2e021c052b..4c75c7238007 100644 --- a/packages/data-context/src/data/ProjectConfigManager.ts +++ b/packages/data-context/src/data/ProjectConfigManager.ts @@ -11,6 +11,7 @@ import { CypressEnv } from './CypressEnv' import { autoBindDebug } from '../util/autoBindDebug' import type { EventRegistrar } from './EventRegistrar' import type { DataContext } from '../DataContext' +import { DependencyToInstall, inPkgJson, WIZARD_BUNDLERS, WIZARD_DEPENDENCIES, WIZARD_FRAMEWORKS } from '@packages/scaffold-config' const debug = debugLib(`cypress:lifecycle:ProjectConfigManager`) @@ -153,8 +154,84 @@ export class ProjectConfigManager { if (this._registeredEventsTarget && this._testingType !== this._registeredEventsTarget) { this.options.refreshLifecycle().catch(this.onLoadError) } else if (this._eventsIpc && !this._registeredEventsTarget && this._cachedLoadConfig) { - this.setupNodeEvents(this._cachedLoadConfig).catch(this.onLoadError) + this.setupNodeEvents(this._cachedLoadConfig) + .then(() => { + if (this._testingType === 'component') { + this.checkDependenciesForComponentTesting() + } + }) + .catch(this.onLoadError) + } + } + + checkDependenciesForComponentTesting () { + // if it's a function, for example, the user is created their own dev server, + // and not using one of our presets. Assume they know what they are doing and + // what dependencies they require. + if (typeof this._cachedLoadConfig?.initialConfig?.component?.devServer !== 'object') { + return + } + + const devServerOptions = this._cachedLoadConfig.initialConfig.component.devServer + + const bundler = WIZARD_BUNDLERS.find((x) => x.type === devServerOptions.bundler) + + // Use a map since sometimes the same dependency can appear in `bundler` and `framework`, + // for example webpack appears in both `bundler: 'webpack', framework: 'react-scripts'` + const unsupportedDeps = new Map() + + if (!bundler) { + return + } + + const result = inPkgJson(bundler, this.options.projectRoot) + + if (!result.satisfied) { + unsupportedDeps.set(result.dependency.type, result) + } + + const isFrameworkSatisfied = (bundler: typeof WIZARD_BUNDLERS[number], framework: typeof WIZARD_FRAMEWORKS[number]) => { + for (const dep of framework.dependencies(bundler.type, this.options.projectRoot)) { + const res = inPkgJson(dep.dependency, this.options.projectRoot) + + if (!res.satisfied) { + return false + } + } + + return true + } + + const frameworks = WIZARD_FRAMEWORKS.filter((x) => x.configFramework === devServerOptions.framework) + + const mismatchedFrameworkDeps = new Map() + + let isSatisfied = false + + for (const framework of frameworks) { + if (isFrameworkSatisfied(bundler, framework)) { + isSatisfied = true + break + } else { + for (const dep of framework.dependencies(bundler.type, this.options.projectRoot)) { + mismatchedFrameworkDeps.set(dep.dependency.type, dep) + } + } + } + + if (!isSatisfied) { + for (const dep of Array.from(mismatchedFrameworkDeps.values())) { + if (!dep.satisfied) { + unsupportedDeps.set(dep.dependency.type, dep) + } + } } + + if (unsupportedDeps.size === 0) { + return + } + + this.options.ctx.onWarning(getError('COMPONENT_TESTING_MISMATCHED_DEPENDENCIES', Array.from(unsupportedDeps.values()))) } private async setupNodeEvents (loadConfigReply: LoadConfigReply): Promise { diff --git a/packages/errors/__snapshot-html__/COMPONENT_TESTING_MISMATCHED_DEPENDENCIES.html b/packages/errors/__snapshot-html__/COMPONENT_TESTING_MISMATCHED_DEPENDENCIES.html new file mode 100644 index 000000000000..a7efe2148941 --- /dev/null +++ b/packages/errors/__snapshot-html__/COMPONENT_TESTING_MISMATCHED_DEPENDENCIES.html @@ -0,0 +1,43 @@ + + + + + + + + + + + +
We detected that you have versions of dependencies that are not officially supported:
+
+ - `vite`. Expected >=2.0.0, found 1.0.0.
+
+If you're experiencing problems, downgrade dependencies and restart Cypress.
+
+
\ No newline at end of file diff --git a/packages/errors/src/errors.ts b/packages/errors/src/errors.ts index f6ae2307c394..7505811027ed 100644 --- a/packages/errors/src/errors.ts +++ b/packages/errors/src/errors.ts @@ -9,6 +9,7 @@ import type { BreakingErrResult } from '@packages/config' import { humanTime, logError, parseResolvedPattern, pluralize } from './errorUtils' import { errPartial, errTemplate, fmt, theme, PartialErr } from './errTemplate' import { stackWithoutMessage } from './stackUtils' +import type { DependencyToInstall } from '@packages/scaffold-config' import type { ClonedError, ConfigValidationFailureInfo, CypressError, ErrTemplateResult, ErrorLike } from './errorTypes' const ansi_up = new AU() @@ -1561,6 +1562,24 @@ export const AllCypressErrors = { https://on.cypress.io/configuration ` }, + + COMPONENT_TESTING_MISMATCHED_DEPENDENCIES: (dependencies: DependencyToInstall[]) => { + const deps = dependencies.map((dep) => { + if (dep.detectedVersion) { + return `\`${dep.dependency.installer}\`. Expected ${dep.dependency.minVersion}, found ${dep.detectedVersion}.` + } + + return `\`${dep.dependency.installer}\`. Expected ${dep.dependency.minVersion} but dependency was not found.` + }) + + return errTemplate` + We detected that you have versions of dependencies that are not officially supported: + + ${fmt.listItems(deps, { prefix: ' - ' })} + + If you're experiencing problems, downgrade dependencies and restart Cypress. + ` + }, } as const // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/packages/errors/test/unit/visualSnapshotErrors_spec.ts b/packages/errors/test/unit/visualSnapshotErrors_spec.ts index 78501f0c8b2e..9bb86b7ace63 100644 --- a/packages/errors/test/unit/visualSnapshotErrors_spec.ts +++ b/packages/errors/test/unit/visualSnapshotErrors_spec.ts @@ -1172,5 +1172,27 @@ describe('visual error templates', () => { default: ['component'], } }, + + COMPONENT_TESTING_MISMATCHED_DEPENDENCIES: () => { + return { + default: [ + [ + { + dependency: { + type: 'vite', + name: 'Vite', + package: 'vite', + installer: 'vite', + description: 'Vite is dev server that serves your source files over native ES modules', + minVersion: '>=2.0.0', + }, + satisfied: false, + detectedVersion: '1.0.0', + loc: null, + }, + ], + ], + } + }, }) }) diff --git a/packages/frontend-shared/src/warning/Warning.vue b/packages/frontend-shared/src/warning/Warning.vue index 40b828e41fc6..109735b704d8 100644 --- a/packages/frontend-shared/src/warning/Warning.vue +++ b/packages/frontend-shared/src/warning/Warning.vue @@ -10,6 +10,7 @@ >