Files
cypress/packages/errors/src/errors.ts

1630 lines
60 KiB
TypeScript

import AU from 'ansi_up'
/* eslint-disable no-console */
import chalk from 'chalk'
import _ from 'lodash'
import path from 'path'
import stripAnsi from 'strip-ansi'
import type { TestingType } from '@packages/types'
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 { ClonedError, ConfigValidationFailureInfo, CypressError, ErrTemplateResult, ErrorLike } from './errorTypes'
const ansi_up = new AU()
ansi_up.use_classes = true
const displayRetriesRemaining = function (tries: number) {
const times = tries === 1 ? 'time' : 'times'
const lastTryNewLine = tries === 1 ? '\n' : ''
return fmt.meta(
`We will try connecting to it ${tries} more ${times}...${lastTryNewLine}`,
)
}
const getUsedTestsMessage = (limit: number, usedTestsMessage: string) => {
return _.isFinite(limit)
? fmt.off(`The limit is ${chalk.yellow(`${limit}`)} ${usedTestsMessage} results.`)
: fmt.off('')
}
export const warnIfExplicitCiBuildId = function (ciBuildId?: string | null) {
if (!ciBuildId) {
return null
}
return errPartial`\
It also looks like you also passed in an explicit ${fmt.flag('--ci-build-id')} flag.
This is only necessary if you are NOT running in one of our supported CI providers.
This flag must be unique for each new run, but must also be identical for each machine you are trying to --group or run in --parallel.\
`
}
/**
* All Cypress Errors should be defined here:
*
* The errors must return an "errTemplate", this is processed by the
*/
export const AllCypressErrors = {
CANNOT_TRASH_ASSETS: (arg1: Error) => {
return errTemplate`\
Warning: We failed to trash the existing run results.
This error will not alter the exit code.
${fmt.stackTrace(arg1)}`
},
CANNOT_REMOVE_OLD_BROWSER_PROFILES: (arg1: Error) => {
return errTemplate`\
Warning: We failed to remove old browser profiles from previous runs.
This error will not alter the exit code.
${fmt.stackTrace(arg1)}`
},
VIDEO_RECORDING_FAILED: (arg1: Error) => {
return errTemplate`\
Warning: We failed to record the video.
This error will not alter the exit code.
${fmt.stackTrace(arg1)}`
},
VIDEO_POST_PROCESSING_FAILED: (arg1: Error) => {
return errTemplate`\
Warning: We failed processing this video.
This error will not alter the exit code.
${fmt.stackTrace(arg1)}`
},
CHROME_WEB_SECURITY_NOT_SUPPORTED: (browser: string) => {
return errTemplate`\
Your project has set the configuration option: ${fmt.highlight(`chromeWebSecurity`)} to ${fmt.highlightTertiary(`false`)}
This option will not have an effect in ${fmt.off(_.capitalize(browser))}. Tests that rely on web security being disabled will not run as expected.`
},
BROWSER_NOT_FOUND_BY_NAME: (browser: string, foundBrowsersStr: string[]) => {
let canarySuffix: PartialErr | null = null
if (browser === 'canary') {
canarySuffix = errPartial`\
${fmt.off('\n\n')}
Note: In ${fmt.cypressVersion(`4.0.0`)}, Canary must be launched as ${fmt.highlightSecondary(`chrome:canary`)}, not ${fmt.highlightSecondary(`canary`)}.
See https://on.cypress.io/migration-guide for more information on breaking changes in 4.0.0.`
}
return errTemplate`\
Can't run because you've entered an invalid browser name.
Browser: ${fmt.highlight(browser)} was not found on your system or is not supported by Cypress.
Cypress supports the following browsers:
${fmt.listItems(['electron', 'chrome', 'chromium', 'chrome:canary', 'edge', 'firefox'])}
You can also use a custom browser: https://on.cypress.io/customize-browsers
Available browsers found on your system are:
${fmt.listItems(foundBrowsersStr)}${canarySuffix}`
},
BROWSER_NOT_FOUND_BY_PATH: (arg1: string, arg2: string) => {
return errTemplate`\
We could not identify a known browser at the path you provided: ${fmt.path(arg1)}
Read more about how to troubleshoot launching browsers: https://on.cypress.io/troubleshooting-launching-browsers
The output from the command we ran was:
${fmt.highlightSecondary(arg2)}`
},
NOT_LOGGED_IN: () => {
return errTemplate`\
You're not logged in.
Run ${fmt.highlight(`cypress open`)} to open the Desktop App and log in.`
},
TESTS_DID_NOT_START_RETRYING: (arg1: string) => {
return errTemplate`Timed out waiting for the browser to connect. ${fmt.off(arg1)}`
},
TESTS_DID_NOT_START_FAILED: () => {
return errTemplate`The browser never connected. Something is wrong. The tests cannot run. Aborting...`
},
DASHBOARD_CANCEL_SKIPPED_SPEC: () => {
return errTemplate`${fmt.off(`\n `)}This spec and its tests were skipped because the run has been canceled.`
},
DASHBOARD_API_RESPONSE_FAILED_RETRYING: (arg1: {tries: number, delay: number, response: Error}) => {
const time = pluralize('time', arg1.tries)
const delay = humanTime.long(arg1.delay, false)
return errTemplate`\
We encountered an unexpected error talking to our servers.
We will retry ${fmt.off(arg1.tries)} more ${fmt.off(time)} in ${fmt.off(delay)}...
The server's response was:
${fmt.highlightSecondary(arg1.response)}`
/* Because of fmt.listFlags() and fmt.listItems() */
/* eslint-disable indent */
},
DASHBOARD_CANNOT_PROCEED_IN_PARALLEL: (arg1: {flags: any, response: Error}) => {
return errTemplate`\
We encountered an unexpected error talking to our servers.
Because you passed the ${fmt.flag(`--parallel`)} flag, this run cannot proceed because it requires a valid response from our servers.
${fmt.listFlags(arg1.flags, {
group: '--group',
ciBuildId: '--ciBuildId',
})}
The server's response was:
${fmt.highlightSecondary(arg1.response)}`
},
DASHBOARD_CANNOT_PROCEED_IN_SERIAL: (arg1: {flags: any, response: Error}) => {
return errTemplate`\
We encountered an unexpected error talking to our servers.
${fmt.listFlags(arg1.flags, {
group: '--group',
ciBuildId: '--ciBuildId',
})}
The server's response was:
${fmt.highlightSecondary(arg1.response)}`
},
DASHBOARD_UNKNOWN_INVALID_REQUEST: (arg1: {flags: any, response: Error}) => {
return errTemplate`\
We encountered an unexpected error talking to our servers.
There is likely something wrong with the request.
${fmt.listFlags(arg1.flags, {
tags: '--tag',
group: '--group',
parallel: '--parallel',
ciBuildId: '--ciBuildId',
})}
The server's response was:
${fmt.highlightSecondary(arg1.response)}`
},
DASHBOARD_UNKNOWN_CREATE_RUN_WARNING: (arg1: {props: any, message: string}) => {
return errTemplate`\
Warning from Cypress Dashboard: ${fmt.highlight(arg1.message)}
Details:
${fmt.meta(arg1.props)}`
},
DASHBOARD_STALE_RUN: (arg1: {runUrl: string, [key: string]: any}) => {
return errTemplate`\
You are attempting to pass the ${fmt.flag(`--parallel`)} flag to a run that was completed over 24 hours ago.
The existing run is: ${fmt.url(arg1.runUrl)}
You cannot parallelize a run that has been complete for that long.
${fmt.listFlags(arg1, {
tags: '--tag',
group: '--group',
parallel: '--parallel',
ciBuildId: '--ciBuildId',
})}
https://on.cypress.io/stale-run`
},
DASHBOARD_ALREADY_COMPLETE: (props: {runUrl: string}) => {
return errTemplate`\
The run you are attempting to access is already complete and will not accept new groups.
The existing run is: ${fmt.url(props.runUrl)}
When a run finishes all of its groups, it waits for a configurable set of time before finally completing. You must add more groups during that time period.
${fmt.listFlags(props, {
tags: '--tag',
group: '--group',
parallel: '--parallel',
ciBuildId: '--ciBuildId',
})}
https://on.cypress.io/already-complete`
},
DASHBOARD_PARALLEL_REQUIRED: (arg1: {runUrl: string}) => {
return errTemplate`\
You did not pass the ${fmt.flag(`--parallel`)} flag, but this run's group was originally created with the --parallel flag.
The existing run is: ${fmt.url(arg1.runUrl)}
${fmt.listFlags(arg1, {
tags: '--tag',
group: '--group',
parallel: '--parallel',
ciBuildId: '--ciBuildId',
})}
You must use the --parallel flag with this group.
https://on.cypress.io/parallel-required`
},
DASHBOARD_PARALLEL_DISALLOWED: (arg1: {runUrl: string}) => {
return errTemplate`\
You passed the ${fmt.flag(`--parallel`)} flag, but this run group was originally created without the --parallel flag.
The existing run is: ${fmt.url(arg1.runUrl)}
${fmt.listFlags(arg1, {
group: '--group',
parallel: '--parallel',
ciBuildId: '--ciBuildId',
})}
You can not use the --parallel flag with this group.
https://on.cypress.io/parallel-disallowed`
},
DASHBOARD_PARALLEL_GROUP_PARAMS_MISMATCH: (arg1: {runUrl: string, parameters: any}) => {
return errTemplate`\
You passed the ${fmt.flag(`--parallel`)} flag, but we do not parallelize tests across different environments.
This machine is sending different environment parameters than the first machine that started this parallel run.
The existing run is: ${fmt.url(arg1.runUrl)}
In order to run in parallel mode each machine must send identical environment parameters such as:
${fmt.listItems([
'specs',
'osName',
'osVersion',
'browserName',
'browserVersion (major)',
])}
This machine sent the following parameters:
${fmt.meta(arg1.parameters)}
https://on.cypress.io/parallel-group-params-mismatch`
},
DASHBOARD_RUN_GROUP_NAME_NOT_UNIQUE: (arg1: {runUrl: string, ciBuildId?: string | null}) => {
return errTemplate`\
You passed the ${fmt.flag(`--group`)} flag, but this group name has already been used for this run.
The existing run is: ${fmt.url(arg1.runUrl)}
${fmt.listFlags(arg1, {
group: '--group',
parallel: '--parallel',
ciBuildId: '--ciBuildId',
})}
If you are trying to parallelize this run, then also pass the ${fmt.flag(`--parallel`)} flag, else pass a different group name.
${warnIfExplicitCiBuildId(arg1.ciBuildId)}
https://on.cypress.io/run-group-name-not-unique`
},
DEPRECATED_BEFORE_BROWSER_LAUNCH_ARGS: () => {
return errTemplate`\
Deprecation Warning: The ${fmt.highlight(`before:browser:launch`)} plugin event changed its signature in ${fmt.cypressVersion(`4.0.0`)}
The event switched from yielding the second argument as an ${fmt.highlightSecondary(`array`)} of browser arguments to an options ${fmt.highlightSecondary(`object`)} with an ${fmt.highlightSecondary(`args`)} property.
We've detected that your code is still using the previous, deprecated interface signature.
This code will not work in a future version of Cypress. Please see the upgrade guide: https://on.cypress.io/deprecated-before-browser-launch-args`
},
DUPLICATE_TASK_KEY: (arg1: string[]) => {
return errTemplate`\
Warning: Multiple attempts to register the following task(s):
${fmt.listItems(arg1, { color: 'yellow' })}
Only the last attempt will be registered.`
},
INDETERMINATE_CI_BUILD_ID: (arg1: Record<string, string>, arg2: string[]) => {
return errTemplate`\
You passed the ${fmt.flag(`--group`)} or ${fmt.flag(`--parallel`)} flag but we could not automatically determine or generate a ciBuildId.
${fmt.listFlags(arg1, {
group: '--group',
parallel: '--parallel',
})}
In order to use either of these features a ciBuildId must be determined.
The ciBuildId is automatically detected if you are running Cypress in any of the these CI providers:
${fmt.listItems(arg2)}
Because the ciBuildId could not be auto-detected you must pass the --ci-build-id flag manually.
https://on.cypress.io/indeterminate-ci-build-id`
},
RECORD_PARAMS_WITHOUT_RECORDING: (arg1: Record<string, string>) => {
return errTemplate`\
You passed the ${fmt.flag(`--ci-build-id`)}, ${fmt.flag(`--group`)}, ${fmt.flag(`--tag`)}, or ${fmt.flag(`--parallel`)} flag without also passing the ${fmt.flag(`--record`)} flag.
${fmt.listFlags(arg1, {
ciBuildId: '--ci-build-id',
tags: '--tag',
group: '--group',
parallel: '--parallel',
})}
These flags can only be used when recording to the Cypress Dashboard service.
https://on.cypress.io/record-params-without-recording`
},
INCORRECT_CI_BUILD_ID_USAGE: (arg1: Record<string, string>) => {
return errTemplate`\
You passed the ${fmt.flag(`--ci-build-id`)} flag but did not provide either a ${fmt.flag(`--group`)} or ${fmt.flag(`--parallel`)} flag.
${fmt.listFlags(arg1, {
ciBuildId: '--ci-build-id',
})}
The --ci-build-id flag is used to either group or parallelize multiple runs together.
https://on.cypress.io/incorrect-ci-build-id-usage`
/* eslint-enable indent */
},
RECORD_KEY_MISSING: () => {
return errTemplate`\
You passed the ${fmt.flag(`--record`)} flag but did not provide us your Record Key.
You can pass us your Record Key like this:
${fmt.terminal(`cypress run --record --key <record_key>`)}
You can also set the key as an environment variable with the name: ${fmt.highlightSecondary(`CYPRESS_RECORD_KEY`)}
https://on.cypress.io/how-do-i-record-runs`
},
// TODO: make this relative path, not absolute
CANNOT_RECORD_NO_PROJECT_ID: (configFilePath: string) => {
return errTemplate`\
You passed the ${fmt.flag(`--record`)} flag but this project has not been setup to record.
This project is missing the ${fmt.highlight(`projectId`)} inside of: ${fmt.path(configFilePath)}
We cannot uniquely identify this project without this id.
You need to setup this project to record. This will generate a unique projectId.
Alternatively if you omit the ${fmt.flag(`--record`)} flag this project will run without recording.
https://on.cypress.io/recording-project-runs`
},
PROJECT_ID_AND_KEY_BUT_MISSING_RECORD_OPTION: (arg1: string) => {
return errTemplate`\
This project has been configured to record runs on our Dashboard.
It currently has the projectId: ${fmt.highlight(arg1)}
You also provided your Record Key, but you did not pass the ${fmt.flag(`--record`)} flag.
This run will not be recorded.
If you meant to have this run recorded please additionally pass this flag:
${fmt.terminal('cypress run --record')}
If you don't want to record these runs, you can silence this warning:
${fmt.terminal('cypress run --record false')}
https://on.cypress.io/recording-project-runs`
},
DASHBOARD_INVALID_RUN_REQUEST: (arg1: {message: string, errors: string[], object: object}) => {
return errTemplate`\
Recording this run failed because the request was invalid.
${fmt.highlight(arg1.message)}
Errors:
${fmt.meta(arg1.errors)}
Request Sent:
${fmt.meta(arg1.object)}`
},
RECORDING_FROM_FORK_PR: () => {
return errTemplate`\
Warning: It looks like you are trying to record this run from a forked PR.
The ${fmt.highlight(`Record Key`)} is missing. Your CI provider is likely not passing private environment variables to builds from forks.
These results will not be recorded.
This error will not alter the exit code.`
},
DASHBOARD_CANNOT_UPLOAD_RESULTS: (apiErr: Error) => {
return errTemplate`\
Warning: We encountered an error while uploading results from your run.
These results will not be recorded.
This error will not alter the exit code.
${fmt.highlightSecondary(apiErr)}`
},
DASHBOARD_CANNOT_CREATE_RUN_OR_INSTANCE: (apiErr: Error) => {
return errTemplate`\
Warning: We encountered an error talking to our servers.
This run will not be recorded.
This error will not alter the exit code.
${fmt.highlightSecondary(apiErr)}`
},
DASHBOARD_RECORD_KEY_NOT_VALID: (recordKey: string, projectId: string) => {
return errTemplate`\
Your Record Key ${fmt.highlight(recordKey)} is not valid with this projectId: ${fmt.highlightSecondary(projectId)}
It may have been recently revoked by you or another user.
Please log into the Dashboard to see the valid record keys.
https://on.cypress.io/dashboard/projects/${fmt.off(projectId)}`
},
DASHBOARD_PROJECT_NOT_FOUND: (projectId: string, configFileBaseName: string) => {
return errTemplate`\
We could not find a Dashboard project with the projectId: ${fmt.highlight(projectId)}
This ${fmt.highlightSecondary(`projectId`)} came from your ${fmt.path(configFileBaseName)} file or an environment variable.
Please log into the Dashboard and find your project.
We will list the correct projectId in the 'Settings' tab.
Alternatively, you can create a new project using the Desktop Application.
https://on.cypress.io/dashboard`
},
// TODO: make this relative path, not absolute
NO_PROJECT_ID: (configFilePath: string) => {
return errTemplate`Can't find ${fmt.highlight(`projectId`)} in the config file: ${fmt.path(configFilePath || '')}`
},
NO_PROJECT_FOUND_AT_PROJECT_ROOT: (projectRoot: string) => {
return errTemplate`Can't find a project at the path: ${fmt.path(projectRoot)}`
},
CANNOT_FETCH_PROJECT_TOKEN: () => {
return errTemplate`Can't find project's secret key.`
},
CANNOT_CREATE_PROJECT_TOKEN: () => {
return errTemplate`Can't create project's secret key.`
},
PORT_IN_USE_SHORT: (arg1: string | number) => {
return errTemplate`Port ${fmt.highlight(arg1)} is already in use.`
},
PORT_IN_USE_LONG: (arg1: string | number) => {
return errTemplate`\
Can't run project because port is currently in use: ${fmt.highlight(arg1)}
Assign a different port with the ${fmt.flag(`--port <port>`)} argument or shut down the other running process.`
},
ERROR_READING_FILE: (filePath: string, err: Error) => {
return errTemplate`\
Error reading from: ${fmt.path(filePath)}
${fmt.stackTrace(err)}`
},
ERROR_WRITING_FILE: (filePath: string, err: Error) => {
return errTemplate`\
Error writing to: ${fmt.path(filePath)}
${fmt.stackTrace(err)}`
},
NO_SPECS_FOUND: (folderPath: string, globPattern?: string[] | string | null) => {
// no glob provided, searched all specs
if (!globPattern) {
return errTemplate`\
Can't run because ${fmt.highlightSecondary(`no spec files`)} were found.
We searched for specs inside of this folder:
${fmt.listItem(folderPath)}`
}
const globPaths = _.castArray(globPattern).map((pattern) => {
const [resolvedBasePath, resolvedPattern] = parseResolvedPattern(folderPath, pattern)
return path.join(resolvedBasePath!, theme.yellow(resolvedPattern!))
})
const phrase = globPaths.length > 1 ? 'these glob patterns' : 'this glob pattern'
return errTemplate`\
Can't run because ${fmt.highlightSecondary(`no spec files`)} were found.
We searched for specs matching ${fmt.off(phrase)}:
${fmt.listItems(globPaths, { color: 'blue', prefix: ' > ' })}`
},
RENDERER_CRASHED: () => {
return errTemplate`\
We detected that the Chromium Renderer process just crashed.
This is the equivalent to seeing the 'sad face' when Chrome dies.
This can happen for a number of different reasons:
- You wrote an endless loop and you must fix your own code
- There is a memory leak in Cypress (unlikely but possible)
- You are running Docker (there is an easy fix for this: see link below)
- You are running lots of tests on a memory intense application
- You are running in a memory starved VM environment
- There are problems with your GPU / GPU drivers
- There are browser bugs in Chromium
You can learn more including how to fix Docker here:
https://on.cypress.io/renderer-process-crashed`
},
AUTOMATION_SERVER_DISCONNECTED: () => {
return errTemplate`The automation client disconnected. Cannot continue running tests.`
},
SUPPORT_FILE_NOT_FOUND: (supportFilePath: string) => {
return errTemplate`\
Your ${fmt.highlight(`supportFile`)} is missing or invalid: ${fmt.path(supportFilePath)}
The supportFile must be a .js, .ts, .coffee file or be supported by your preprocessor plugin (if configured).
Fix your support file, or set supportFile to ${fmt.highlightSecondary(`false`)} if a support file is not necessary for your project.
If you have just renamed the extension of your supportFile, restart Cypress.
https://on.cypress.io/support-file-missing-or-invalid`
},
DEFAULT_SUPPORT_FILE_NOT_FOUND: (supportFilePath: string) => {
return errTemplate`\
Your project does not contain a default ${fmt.highlight(`supportFile`)}. We expect a file matching ${fmt.path(supportFilePath)} to exist.
If a support file is not necessary for your project, set ${fmt.highlight(`supportFile`)} to ${fmt.highlightSecondary(`false`)}.
https://on.cypress.io/support-file-missing-or-invalid`
},
// TODO: make this relative path, not absolute
CONFIG_FILE_REQUIRE_ERROR: (configFilePath: string, err: Error) => {
return errTemplate`\
Your ${fmt.highlight(`configFile`)} is invalid: ${fmt.path(configFilePath)}
It threw an error when required, check the stack trace below:
${fmt.stackTrace(err)}
`
},
// TODO: make this relative path, not absolute
SETUP_NODE_EVENTS_IS_NOT_FUNCTION: (configFilePath: string, testingType: string, exported: any) => {
const code = errPartial`
{
${fmt.off(testingType)}: {
setupNodeEvents(on, config) {
${fmt.comment(`// configure tasks and plugins here`)}
}
}
}`
return errTemplate`\
Your ${fmt.highlight(`configFile`)} is invalid: ${fmt.path(configFilePath)}
The ${fmt.highlightSecondary(`${testingType}.setupNodeEvents()`)} function must be defined with the following signature:
${fmt.code(code)}
Instead we saw:
${fmt.stringify(exported)}
https://on.cypress.io/plugins-api
`
},
CONFIG_FILE_SETUP_NODE_EVENTS_ERROR: (configFilePath: string, testingType: TestingType, err: ErrorLike) => {
return errTemplate`
Your ${fmt.highlightSecondary(`configFile`)} threw an error from: ${fmt.path(configFilePath)}
The error was thrown while executing your ${fmt.highlight(`${testingType}.setupNodeEvents()`)} function:
${fmt.stackTrace(err)}
`
},
CONFIG_FILE_UNEXPECTED_ERROR: (configFilePath: string, err: ErrorLike) => {
return errTemplate`
Your ${fmt.highlight(`configFile`)} threw an error from: ${fmt.path(configFilePath)}
We stopped running your tests because your config file crashed.
${fmt.stackTrace(err)}
`
},
// TODO: make this relative path, not absolute
SETUP_NODE_EVENTS_INVALID_EVENT_NAME_ERROR: (configFilePath: string, invalidEventName: string, validEventNames: string[], err: Error) => {
return errTemplate`
Your ${fmt.highlightSecondary(`configFile`)} threw a validation error from: ${fmt.path(configFilePath)}
You must pass a valid event name when registering a plugin.
You passed: ${fmt.highlight(invalidEventName)}
The following are valid events:
${fmt.listItems(validEventNames)}
Learn more at https://docs.cypress.io/api/plugins/writing-a-plugin#config
${fmt.stackTrace(err)}
`
},
BUNDLE_ERROR: (filePath: string, arg2: string) => {
// IF YOU MODIFY THIS MAKE SURE TO UPDATE
// THE ERROR MESSAGE IN THE RUNNER TOO
return errTemplate`\
Oops...we found an error preparing this test file:
${fmt.listItem(filePath)}
The error was:
${fmt.highlight(arg2)}
This occurred while Cypress was compiling and bundling your test code. This is usually caused by:
- A missing file or dependency
- A syntax error in the file or one of its dependencies
Fix the error in your code and re-run your tests.`
},
// happens when there is an error in configuration file like "cypress.json"
// TODO: make this relative path, not absolute
CONFIG_VALIDATION_MSG_ERROR: (fileType: 'configFile' | null, fileName: string | null, validationMsg: string) => {
if (!fileType) {
return errTemplate`
An invalid configuration value was set:
${fmt.highlight(validationMsg)}`
}
return errTemplate`
Your ${fmt.highlight(fileType)} as ${fmt.path(fileName)} set an invalid value:
${fmt.highlight(validationMsg)}`
},
// TODO: make this relative path, not absolute
CONFIG_VALIDATION_ERROR: (fileType: 'configFile' | null, filePath: string | null, validationResult: ConfigValidationFailureInfo) => {
const { key, type, value, list } = validationResult
if (!fileType) {
return errTemplate`\
An invalid configuration value was set.
Expected ${fmt.highlight(key)} to be ${fmt.off(type)}.
Instead the value was: ${fmt.stringify(value)}`
}
if (list) {
return errTemplate`\
Your ${fmt.highlight(fileType)} at ${fmt.path(filePath)} set an invalid value:
The error occurred while validating the ${fmt.highlightSecondary(list)} list.
Expected ${fmt.highlight(key)} to be ${fmt.off(type)}.
Instead the value was: ${fmt.stringify(value)}`
}
return errTemplate`\
Your ${fmt.highlight(fileType)} at ${fmt.path(filePath)} set an invalid value:
Expected ${fmt.highlight(key)} to be ${fmt.off(type)}.
Instead the value was: ${fmt.stringify(value)}`
},
RENAMED_CONFIG_OPTION: (arg1: {name: string, newName: string}) => {
return errTemplate`\
The ${fmt.highlight(arg1.name)} configuration option you have supplied has been renamed.
Please rename ${fmt.highlight(arg1.name)} to ${fmt.highlightSecondary(arg1.newName)}`
},
CANNOT_CONNECT_BASE_URL: () => {
return errTemplate`\
Cypress failed to verify that your server is running.
Please start this server and then run Cypress again.`
},
CANNOT_CONNECT_BASE_URL_WARNING: (arg1: string) => {
return errTemplate`\
Cypress could not verify that this server is running:
${fmt.listItem(arg1)}
This server has been configured as your ${fmt.highlight(`baseUrl`)}, and tests will likely fail if it is not running.`
},
// TODO: test this
CANNOT_CONNECT_BASE_URL_RETRYING: (arg1: {attempt: number, baseUrl: string, remaining: number, delay: number}) => {
switch (arg1.attempt) {
case 1:
return errTemplate`\
Cypress could not verify that this server is running:
${fmt.listItem(arg1.baseUrl)}
We are verifying this server because it has been configured as your ${fmt.highlight(`baseUrl`)}.
Cypress automatically waits until your server is accessible before running tests.
${displayRetriesRemaining(arg1.remaining)}`
default:
return errTemplate`${displayRetriesRemaining(arg1.remaining)}`
}
},
// TODO: test this
INVALID_REPORTER_NAME: (arg1: {name: string, paths: string[], error: Error}) => {
return errTemplate`\
Error loading the reporter: ${fmt.highlight(arg1.name)}
We searched for the reporter in these paths:
${fmt.listItems(arg1.paths)}
Learn more at https://on.cypress.io/reporters
${fmt.stackTrace(arg1.error)}
`
},
// TODO: manually test this
NO_DEFAULT_CONFIG_FILE_FOUND: (arg1: string) => {
return errTemplate`\
Could not find a Cypress configuration file in this folder: ${fmt.path(arg1)}`
},
CONFIG_FILES_LANGUAGE_CONFLICT: (projectRoot: string, filesFound: string[]) => {
return errTemplate`
Could not load a Cypress configuration file because there are multiple matches.
We've found ${fmt.highlight(filesFound.length)} Cypress configuration files named
${fmt.highlight(filesFound.join(', '))} at the location below:
${fmt.listItem(projectRoot)}
Please delete the conflicting configuration files.
`
},
CONFIG_FILE_NOT_FOUND: (configFileBaseName: string, projectRoot: string) => {
return errTemplate`\
Could not find a Cypress configuration file.
We looked but did not find a ${fmt.highlight(configFileBaseName)} file in this folder: ${fmt.path(projectRoot)}`
},
INVOKED_BINARY_OUTSIDE_NPM_MODULE: () => {
return errTemplate`\
It looks like you are running the Cypress binary directly.
This is not the recommended approach, and Cypress may not work correctly.
Please install the ${fmt.highlight(`cypress`)} NPM package and follow the instructions here:
https://on.cypress.io/installing-cypress`
},
FREE_PLAN_EXCEEDS_MONTHLY_PRIVATE_TESTS: (arg1: {link: string, usedTestsMessage: string, limit: number}) => {
return errTemplate`\
You've exceeded the limit of private test results under your free plan this month. ${getUsedTestsMessage(arg1.limit, arg1.usedTestsMessage)}
To continue recording tests this month you must upgrade your account. Please visit your billing to upgrade to another billing plan.
${fmt.off(arg1.link)}`
},
FREE_PLAN_IN_GRACE_PERIOD_EXCEEDS_MONTHLY_PRIVATE_TESTS: (arg1: {link: string, usedTestsMessage: string, gracePeriodMessage: string, limit: number}) => {
return errTemplate`\
You've exceeded the limit of private test results under your free plan this month. ${getUsedTestsMessage(arg1.limit, arg1.usedTestsMessage)}
Your plan is now in a grace period, which means your tests will still be recorded until ${fmt.off(arg1.gracePeriodMessage)}. Please upgrade your plan to continue recording tests on the Cypress Dashboard in the future.
${fmt.off(arg1.link)}`
},
PAID_PLAN_EXCEEDS_MONTHLY_PRIVATE_TESTS: (arg1: {link: string, usedTestsMessage: string, limit: number}) => {
return errTemplate`\
You've exceeded the limit of private test results under your current billing plan this month. ${getUsedTestsMessage(arg1.limit, arg1.usedTestsMessage)}
To upgrade your account, please visit your billing to upgrade to another billing plan.
${fmt.off(arg1.link)}`
},
FREE_PLAN_EXCEEDS_MONTHLY_TESTS: (arg1: {link: string, usedTestsMessage: string, limit: number}) => {
return errTemplate`\
You've exceeded the limit of test results under your free plan this month. ${getUsedTestsMessage(arg1.limit, arg1.usedTestsMessage)}
To continue recording tests this month you must upgrade your account. Please visit your billing to upgrade to another billing plan.
${fmt.off(arg1.link)}`
},
FREE_PLAN_IN_GRACE_PERIOD_EXCEEDS_MONTHLY_TESTS: (arg1: {link: string, usedTestsMessage: string, gracePeriodMessage: string, limit: number}) => {
return errTemplate`\
You've exceeded the limit of test results under your free plan this month. ${getUsedTestsMessage(arg1.limit, arg1.usedTestsMessage)}
Your plan is now in a grace period, which means you will have the full benefits of your current plan until ${fmt.highlight(arg1.gracePeriodMessage)}.
Please visit your billing to upgrade your plan.
${fmt.off(arg1.link)}`
},
PLAN_EXCEEDS_MONTHLY_TESTS: (arg1: {link: string, planType: string, usedTestsMessage: string, limit: number}) => {
return errTemplate`\
You've exceeded the limit of test results under your ${fmt.highlight(arg1.planType)} billing plan this month. ${getUsedTestsMessage(arg1.limit, arg1.usedTestsMessage)}
To continue getting the full benefits of your current plan, please visit your billing to upgrade.
${fmt.off(arg1.link)}`
},
FREE_PLAN_IN_GRACE_PERIOD_PARALLEL_FEATURE: (arg1: {link: string, gracePeriodMessage: string}) => {
return errTemplate`\
${fmt.highlightSecondary(`Parallelization`)} is not included under your free plan.
Your plan is now in a grace period, which means your tests will still run in parallel until ${fmt.highlight(arg1.gracePeriodMessage)}. Please upgrade your plan to continue running your tests in parallel in the future.
${fmt.off(arg1.link)}`
},
PARALLEL_FEATURE_NOT_AVAILABLE_IN_PLAN: (arg1: {link: string}) => {
return errTemplate`\
${fmt.highlightSecondary(`Parallelization`)} is not included under your current billing plan.
To run your tests in parallel, please visit your billing and upgrade to another plan with parallelization.
${fmt.off(arg1.link)}`
},
PLAN_IN_GRACE_PERIOD_RUN_GROUPING_FEATURE_USED: (arg1: {link: string, gracePeriodMessage: string}) => {
return errTemplate`\
${fmt.highlightSecondary(`Grouping`)} is not included under your free plan.
Your plan is now in a grace period, which means your tests will still run with groups until ${fmt.highlight(arg1.gracePeriodMessage)}. Please upgrade your plan to continue running your tests with groups in the future.
${fmt.off(arg1.link)}`
},
RUN_GROUPING_FEATURE_NOT_AVAILABLE_IN_PLAN: (arg1: {link: string}) => {
return errTemplate`\
${fmt.highlightSecondary(`Grouping`)} is not included under your current billing plan.
To run your tests with groups, please visit your billing and upgrade to another plan with grouping.
${fmt.off(arg1.link)}`
},
// TODO: fix
FIXTURE_NOT_FOUND: (arg1: string, arg2: string[]) => {
return errTemplate`\
A fixture file could not be found at any of the following paths:
${fmt.listItem(arg1)}
${fmt.listItem(arg1)}.[ext]
Cypress looked for these file extensions at the provided path:
${fmt.listItem(arg2.join(', '))}
Provide a path to an existing fixture file.`
},
BAD_POLICY_WARNING: (policyKeys: string[]) => {
return errTemplate`\
Cypress detected policy settings on your computer that may cause issues.
The following policies were detected that may prevent Cypress from automating Chrome:
${fmt.listItems(policyKeys)}
For more information, see https://on.cypress.io/bad-browser-policy`
},
BAD_POLICY_WARNING_TOOLTIP: () => {
return errTemplate`Cypress detected policy settings on your computer that may cause issues with using this browser. For more information, see https://on.cypress.io/bad-browser-policy`
},
EXTENSION_NOT_LOADED: (browserName: string, extensionPath: string) => {
return errTemplate`\
${fmt.off(browserName)} could not install the extension at path: ${fmt.path(extensionPath)}
Please verify that this is the path to a valid, unpacked WebExtension.`
},
COULD_NOT_FIND_SYSTEM_NODE: (nodeVersion: string) => {
return errTemplate`\
${fmt.highlight(`nodeVersion`)} is set to ${fmt.highlightTertiary(`system`)} but Cypress could not find a usable Node executable on your ${fmt.highlightSecondary(`PATH`)}.
Make sure that your Node executable exists and can be run by the current user.
Cypress will use the built-in Node version ${fmt.highlightSecondary(nodeVersion)} instead.`
},
INVALID_CYPRESS_INTERNAL_ENV: (val: string) => {
return errTemplate`\
We have detected an unknown or unsupported ${fmt.highlightSecondary(`CYPRESS_INTERNAL_ENV`)} value: ${fmt.highlight(val)}
CYPRESS_INTERNAL_ENV is reserved for internal use and cannot be modified.`
},
CDP_VERSION_TOO_OLD: (minimumVersion: string, currentVersion: {major: number, minor: string | number}) => {
const phrase = currentVersion.major !== 0
? fmt.highlight(`${currentVersion.major}.${currentVersion.minor}`)
: fmt.off('an older version')
return errTemplate`A minimum CDP version of ${fmt.highlight(minimumVersion)} is required, but the current browser has ${phrase}.`
},
CDP_COULD_NOT_CONNECT: (browserName: string, port: number, err: Error) => {
// we include a stack trace here because it may contain useful information
// to debug since this is an "uncontrolled" error even though it doesn't
// come from a user
return errTemplate`\
Cypress failed to make a connection to the Chrome DevTools Protocol after retrying for 50 seconds.
This usually indicates there was a problem opening the ${fmt.off(_.capitalize(browserName))} browser.
The CDP port requested was ${fmt.highlight(port)}.
${fmt.stackTrace(err)}`
},
FIREFOX_COULD_NOT_CONNECT: (arg1: Error) => {
// we include a stack trace here because it may contain useful information
// to debug since this is an "uncontrolled" error even though it doesn't
// come from a user
return errTemplate`\
Cypress failed to make a connection to Firefox.
This usually indicates there was a problem opening the Firefox browser.
${fmt.stackTrace(arg1)}`
},
CDP_COULD_NOT_RECONNECT: (arg1: Error) => {
return errTemplate`\
There was an error reconnecting to the Chrome DevTools protocol. Please restart the browser.
${fmt.stackTrace(arg1)}`
},
CDP_RETRYING_CONNECTION: (attempt: string | number, browserName: string) => {
return errTemplate`Still waiting to connect to ${fmt.off(_.capitalize(browserName))}, retrying in 1 second ${fmt.meta(`(attempt ${attempt}/62)`)}`
},
UNEXPECTED_BEFORE_BROWSER_LAUNCH_PROPERTIES: (arg1: string[], arg2: string[]) => {
return errTemplate`\
The ${fmt.highlight('launchOptions')} object returned by your plugin's ${fmt.highlightSecondary(`before:browser:launch`)} handler contained unexpected properties:
${fmt.listItems(arg1)}
launchOptions may only contain the properties:
${fmt.listItems(arg2)}
https://on.cypress.io/browser-launch-api`
},
// TODO: test this
COULD_NOT_PARSE_ARGUMENTS: (argName: string, argValue: string, errMsg: string) => {
return errTemplate`\
Cypress encountered an error while parsing the argument: ${fmt.highlight(`--${argName}`)}
You passed: ${fmt.highlightTertiary(argValue)}
The error was: ${fmt.highlightSecondary(errMsg)}`
},
FIREFOX_MARIONETTE_FAILURE: (origin: string, err: Error) => {
return errTemplate`\
Cypress could not connect to Firefox.
An unexpected error was received from Marionette: ${fmt.highlightSecondary(origin)}
To avoid this error, ensure that there are no other instances of Firefox launched by Cypress running.
${fmt.stackTrace(err)}`
},
FOLDER_NOT_WRITABLE: (arg1: string) => {
return errTemplate`\
This folder is not writable: ${fmt.path(arg1)}
Writing to this directory is required by Cypress in order to store screenshots and videos.
Enable write permissions to this directory to ensure screenshots and videos are stored.
If you don't require screenshots or videos to be stored you can safely ignore this warning.`
},
EXPERIMENTAL_SAMESITE_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`experimentalGetCookiesSameSite`)} configuration option was removed in ${fmt.cypressVersion(`5.0.0`)}.
Returning the ${fmt.highlightSecondary(`sameSite`)} property is now the default behavior of the ${fmt.highlightSecondary(`cy.cookie`)} commands.
You can safely remove this option from your config.`
},
// TODO: verify configFile is absolute path
// TODO: make this relative path, not absolute
EXPERIMENTAL_COMPONENT_TESTING_REMOVED: (arg1: {configFile: string}) => {
return errTemplate`\
The ${fmt.highlight('experimentalComponentTesting')} configuration option was removed in ${fmt.cypressVersion(`7.0.0`)}.
Please remove this flag from: ${fmt.path(arg1.configFile)}
Component Testing is now a standalone command. You can now run your component tests with:
${fmt.terminal(`cypress open-ct`)}
https://on.cypress.io/migration-guide`
},
EXPERIMENTAL_SESSION_SUPPORT_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`experimentalSessionSupport`)} configuration option was removed in ${fmt.cypressVersion(`9.6.0`)} and replaced with ${fmt.highlight(`experimentalSessionAndOrigin`)}. Please update your config to use ${fmt.highlight(`experimentalSessionAndOrigin`)} instead.
https://on.cypress.io/session`
},
EXPERIMENTAL_SHADOW_DOM_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`experimentalShadowDomSupport`)} configuration option was removed in ${fmt.cypressVersion(`5.2.0`)}. It is no longer necessary when utilizing the ${fmt.highlightSecondary(`includeShadowDom`)} option.
You can safely remove this option from your config.`
},
EXPERIMENTAL_NETWORK_STUBBING_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`experimentalNetworkStubbing`)} configuration option was removed in ${fmt.cypressVersion(`6.0.0`)}.
It is no longer necessary for using ${fmt.highlightSecondary(`cy.intercept()`)}. You can safely remove this option from your config.`
},
EXPERIMENTAL_RUN_EVENTS_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`experimentalRunEvents`)} configuration option was removed in ${fmt.cypressVersion(`6.7.0`)}. It is no longer necessary when listening to run events in the plugins file.
You can safely remove this option from your config.`
},
EXPERIMENTAL_STUDIO_REMOVED: () => {
return errTemplate`\
We're ending the experimental phase of Cypress Studio in ${fmt.cypressVersion(`10.0.0`)} and have learned a lot. Stay tuned for updates on Studio's official release in the future. You can leave feedback here: http://on.cypress.io/studio-beta.
You can safely remove the ${fmt.highlight(`experimentalStudio`)} configuration option from your config.`
},
FIREFOX_GC_INTERVAL_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`firefoxGcInterval`)} configuration option was removed in ${fmt.cypressVersion(`8.0.0`)}. It was introduced to work around a bug in Firefox 79 and below.
Since Cypress no longer supports Firefox 85 and below in Cypress ${fmt.cypressVersion(`8.0.0`)}, this option was removed.
You can safely remove this option from your config.`
},
// TODO: make this relative path, not absolute
INCOMPATIBLE_PLUGIN_RETRIES: (arg1: string) => {
return errTemplate`\
We've detected that the incompatible plugin ${fmt.highlight(`cypress-plugin-retries`)} is installed at: ${fmt.path(arg1)}
Test retries is now natively supported in ${fmt.cypressVersion(`5.0.0`)}.
Remove the plugin from your dependencies to silence this warning.
https://on.cypress.io/test-retries
`
},
// TODO: test this
INVALID_CONFIG_OPTION: (arg1: string[]) => {
const phrase = arg1.length > 1 ? 'options are' : 'option is'
return errTemplate`\
The following configuration ${fmt.off(phrase)} invalid:
${fmt.listItems(arg1, { color: 'yellow' })}
https://on.cypress.io/configuration
`
},
PLUGINS_RUN_EVENT_ERROR: (arg1: string, arg2: Error) => {
return errTemplate`\
An error was thrown in your plugins file while executing the handler for the ${fmt.highlight(arg1)} event.
The error we received was:
${fmt.stackTrace(arg2)}`
},
CONFIG_FILE_INVALID_DEV_START_EVENT: (pluginsFilePath: string) => {
const code = errPartial`
module.exports = (on, config) => {
on('dev-server:start', () => {
${fmt.comment('// start dev server here')}
return startDevServer(...)
}
}`
return errTemplate`\
To run component tests, Cypress needs you to configure the ${fmt.highlight(`dev-server:start`)} event.
Please update this file: ${fmt.path(pluginsFilePath)}
${fmt.code(code)}
https://on.cypress.io/component-testing`
},
UNSUPPORTED_BROWSER_VERSION: (errorMsg: string) => {
return errTemplate`${fmt.off(errorMsg)}`
},
NODE_VERSION_DEPRECATION_SYSTEM: (arg1: {name: string, value: any, configFile: string}) => {
return errTemplate`\
Deprecation Warning: ${fmt.highlight(arg1.name)} is currently set to ${fmt.highlightSecondary(arg1.value)} in the ${fmt.highlightTertiary(arg1.configFile)} configuration file.
As of ${fmt.cypressVersion(`9.0.0`)} the default behavior of ${fmt.highlight(arg1.name)} has changed to always use the version of Node used to start cypress via the cli.
Please remove the ${fmt.highlight(arg1.name)} configuration option from ${fmt.highlightTertiary(arg1.configFile)}.
`
},
// TODO: does this need to change since its a warning?
NODE_VERSION_DEPRECATION_BUNDLED: (arg1: {name: string, value: any, configFile: string}) => {
return errTemplate`\
Deprecation Warning: ${fmt.highlight(arg1.name)} is currently set to ${fmt.highlightSecondary(arg1.value)} in the ${fmt.highlightTertiary(arg1.configFile)} configuration file.
As of ${fmt.cypressVersion(`9.0.0`)} the default behavior of ${fmt.highlight(arg1.name)} has changed to always use the version of Node used to start cypress via the cli.
When ${fmt.highlight(arg1.name)} is set to ${fmt.highlightSecondary(arg1.value)}, Cypress will use the version of Node bundled with electron. This can cause problems running certain plugins or integrations.
As the ${fmt.highlight(arg1.name)} configuration option will be removed in a future release, it is recommended to remove the ${fmt.highlight(arg1.name)} configuration option from ${fmt.highlightTertiary(arg1.configFile)}.
`
},
// V10 Added:
MULTIPLE_SUPPORT_FILES_FOUND: (arg1: string, arg2: string[]) => {
return errTemplate`\
There were multiple support files found matching your ${fmt.highlightSecondary(`supportFile`)} pattern.
Your supportFile is set to: ${fmt.highlight(arg1)}
We found the following files:
${fmt.listItems(arg2)}
Please remove or combine the support files into a single file.`
},
CONFIG_FILE_MIGRATION_NEEDED: (projectRoot: string) => {
return errTemplate`
There is a ${fmt.highlight(`cypress.json`)} file at the path: ${fmt.path(projectRoot)}
${fmt.cypressVersion('10.0.0')} no longer supports cypress.json.
Please run ${fmt.highlightTertiary('cypress open')} to launch the migration tool to migrate to ${fmt.highlightSecondary('cypress.config.{ts|js}')}.
`
},
LEGACY_CONFIG_ERROR_DURING_MIGRATION: (file: string, error: Error) => {
return errTemplate`
Your ${fmt.highlight(file)} at ${fmt.path(`${file}`)} threw an error. ${fmt.stackTrace(error)}
Please ensure your pluginsFile is valid and relaunch the migration tool to migrate to ${fmt.cypressVersion('10.0.0')}.
`
},
LEGACY_CONFIG_FILE: (baseFileName: string, projectRoot: string, legacyConfigFile: string = 'cypress.json') => {
return errTemplate`
There is both a ${fmt.highlight(baseFileName)} and a ${fmt.highlight(legacyConfigFile)} file at the location below:
${fmt.path(projectRoot)}
Cypress no longer supports ${fmt.off(legacyConfigFile)}, please remove it from your project.
`
},
SETUP_NODE_EVENTS_DO_NOT_SUPPORT_DEV_SERVER: (configFilePath: string) => {
const code = errPartial`
{
component: {
devServer (cypressDevServerConfig, devServerConfig) {
${fmt.comment(`// start dev server here`)
}
}
}`
return errTemplate`\
Your ${fmt.highlightSecondary(`configFile`)} is invalid: ${fmt.path(configFilePath)}
Binding to the ${fmt.highlightSecondary(`on('dev-server:start')`)} event is no longer necessary.
Please update your code to use the ${fmt.highlight(`component.devServer()`)} function.
${fmt.code(code)}
Learn more: https://on.cypress.io/dev-server
`
},
PLUGINS_FILE_CONFIG_OPTION_REMOVED: (_errShape: BreakingErrResult) => {
const code = errPartial`
{
e2e: {
setupNodeEvents()
},
component: {
setupNodeEvents()
},
}`
return errTemplate`\
The ${fmt.highlight('pluginsFile')} configuration option you have supplied has been replaced with ${fmt.highlightSecondary('setupNodeEvents')}.
This new option is not a one-to-one correlation and it must be configured separately as a testing type property: ${fmt.highlightSecondary('e2e.setupNodeEvents')} and ${fmt.highlightSecondary('component.setupNodeEvents')}
${fmt.code(code)}
https://on.cypress.io/migration-guide`
},
CONFIG_FILE_INVALID_ROOT_CONFIG: (errShape: BreakingErrResult) => {
const code = errPartial`
{
e2e: {
specPattern: '...',
},
component: {
specPattern: '...',
},
}`
return errTemplate`\
The ${fmt.highlight(errShape.name)} configuration option is now invalid when set from the root of the config object in ${fmt.cypressVersion(`10.0.0`)}.
It is now configured separately as a testing type property: ${fmt.highlightSecondary(`e2e.${errShape.name}`)} and ${fmt.highlightSecondary(`component.${errShape.name}`)}
${fmt.code(code)}
https://on.cypress.io/migration-guide`
},
CONFIG_FILE_INVALID_ROOT_CONFIG_E2E: (errShape: BreakingErrResult) => {
const code = errPartial`
{
e2e: {
${fmt.off(errShape.name)}: '...',
}
}`
return errTemplate`\
The ${fmt.highlight(errShape.name)} configuration option is now invalid when set from the root of the config object in ${fmt.cypressVersion(`10.0.0`)}.
It is now configured separately as a testing type property: ${fmt.highlightSecondary(`e2e.${errShape.name}`)}
${fmt.code(code)}
https://on.cypress.io/migration-guide`
},
CONFIG_FILE_INVALID_ROOT_CONFIG_COMPONENT: (errShape: BreakingErrResult) => {
const code = errPartial`
{
component: {
${fmt.off(errShape.name)}: '...',
}
}`
return errTemplate`\
The ${fmt.highlight(errShape.name)} configuration option is now invalid when set from the root of the config object in ${fmt.cypressVersion(`10.0.0`)}.
It is now configured separately as a testing type property: ${fmt.highlightSecondary(`component.${errShape.name}`)}
${fmt.code(code)}
https://on.cypress.io/migration-guide`
},
// TODO: add path to config file
CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_COMPONENT: (errShape: BreakingErrResult) => {
const code = errPartial`
{
e2e: {
${fmt.off(errShape.name)}: '...',
}
}`
return errTemplate`\
The ${fmt.highlight(`component.${errShape.name}`)} configuration option is not valid for component testing.
Please remove this option or add this as an e2e testing type property: ${fmt.highlightSecondary(`e2e.${errShape.name}`)}
${fmt.code(code)}
https://on.cypress.io/migration-guide`
},
CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_E2E: (errShape: BreakingErrResult) => {
const code = errPartial`
{
e2e: {
${fmt.off(errShape.name)}: '...',
}
}`
return errTemplate`\
The ${fmt.highlight(`e2e.${errShape.name}`)} configuration option is not valid for e2e testing.
Please remove this option or add this as a component testing type property: ${fmt.highlightSecondary(`component.${errShape.name}`)}
${fmt.code(code)}
https://on.cypress.io/migration-guide`
},
CONFIG_FILE_DEV_SERVER_IS_NOT_VALID: (configFilePath: string, setupNodeEvents: any) => {
const re = /.?(cypress\.config.*)/
const configFile = configFilePath.match(re)?.[1] ?? `configFile`
const code = errPartial`
{
component: {
devServer: {
framework: 'create-react-app', ${fmt.comment('// Your framework')}
bundler: 'webpack' ${fmt.comment('// Your dev server')}
}
}
}`
return errTemplate`\
Your ${fmt.highlightSecondary(configFile)} is invalid: ${fmt.path(configFilePath)}
The ${fmt.highlight(`component.devServer`)} must be an object with a supported ${fmt.highlight('framework')} and ${fmt.highlight('bundler')}.
${fmt.code(code)}
Instead, we saw:
${fmt.stringify(setupNodeEvents)}
Learn more: https://on.cypress.io/dev-server
`
},
UNEXPECTED_MUTATION_ERROR: (mutationField: string, args: any, err: Error) => {
return errTemplate`
An unexpected internal error occurred while executing the ${fmt.highlight(mutationField)} operation with payload:
${fmt.stringify(args)}
${fmt.stackTrace(err)}
`
},
DASHBOARD_GRAPHQL_ERROR: (err: Error) => {
return errTemplate`
We received an unexpected error response from the request to the Cypress dashboard:
${fmt.stringify(err.message)}
`
},
UNEXPECTED_INTERNAL_ERROR: (err: Error) => {
return errTemplate`
We encountered an unexpected internal error. Please check GitHub or open a new issue
if you don't see one already with the details below:
${fmt.stackTrace(err)}
`
},
MIGRATION_ALREADY_OCURRED: (configFile: string, legacyConfigFile: string) => {
return errTemplate`
You are attempting to use Cypress with an older config file: ${fmt.highlight(legacyConfigFile)}
When you upgraded to Cypress v10.0 the config file was updated and moved to a new location: ${fmt.highlight(configFile)}
You may need to update any CLI scripts to ensure that they are referring the new version. This would typically look something like:
"${fmt.highlight(`cypress open --config-file=${configFile}`)}"
https://on.cypress.io/migration-guide
`
},
TEST_FILES_RENAMED: (errShape: BreakingErrResult, err?: Error) => {
const stackTrace = err ? fmt.stackTrace(err) : null
const newName = errShape.newName || '<unknown>'
const testingTypedHelpMessage = errShape.testingType
? errPartial`${fmt.highlightSecondary(`${errShape.testingType}.${newName}`)}`
: errPartial`${fmt.highlightSecondary(`e2e.${newName}`)} or ${fmt.highlightSecondary(`component.${newName}`)}`
const code = errShape.testingType
? errPartial`
{
${fmt.off(errShape.testingType)}: {
specPattern: '...',
},
}`
: errPartial`
{
e2e: {
specPattern: '...',
},
component: {
specPattern: '...',
},
}`
return errTemplate`\
The ${fmt.highlight(errShape.name)} configuration option is now invalid when set on the config object in ${fmt.cypressVersion(`10.0.0`)}.
It is now renamed to ${fmt.highlight(newName)} and configured separately as a testing type property: ${testingTypedHelpMessage}
${fmt.code(code)}
https://on.cypress.io/migration-guide
${stackTrace}
`
},
COMPONENT_FOLDER_REMOVED: (errShape: BreakingErrResult, err?: Error) => {
const stackTrace = err ? fmt.stackTrace(err) : null
const code = errPartial`
{
component: {
specPattern: '...',
},
}`
return errTemplate`\
The ${fmt.highlight(errShape.name)} configuration option is now invalid when set on the config object in ${fmt.cypressVersion(`10.0.0`)}.
It is now renamed to ${fmt.highlight('specPattern')} and configured separately as a component testing property: ${fmt.highlightSecondary('component.specPattern')}
${fmt.code(code)}
https://on.cypress.io/migration-guide
${stackTrace}
`
},
INTEGRATION_FOLDER_REMOVED: (errShape: BreakingErrResult, err?: Error) => {
const stackTrace = err ? fmt.stackTrace(err) : null
const code = errPartial`
{
e2e: {
specPattern: '...',
},
}`
return errTemplate`\
The ${fmt.highlight(errShape.name)} configuration option is now invalid when set on the config object in ${fmt.cypressVersion(`10.0.0`)}.
It is now renamed to ${fmt.highlight('specPattern')} and configured separately as a end to end testing property: ${fmt.highlightSecondary('e2e.specPattern')}
${fmt.code(code)}
https://on.cypress.io/migration-guide
${stackTrace}
`
},
} as const
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _typeCheck: Record<keyof AllCypressErrorObj, (...args: any[]) => ErrTemplateResult> = AllCypressErrors
type AllCypressErrorObj = typeof AllCypressErrors
export type AllCypressErrorNames = keyof typeof AllCypressErrors
export function getMsgByType<Type extends keyof AllCypressErrorObj> (type: Type, ...args: Parameters<AllCypressErrorObj[Type]>): string {
const err = getError(type, ...args)
return err.message
}
/**
* Given an error name & params for the error, returns a "CypressError",
* with a forBrowser property, used when we want to format the value for sending to
* the browser rather than the terminal.
*
* @param type
* @param args
* @returns
*/
export const getError = function <Type extends keyof AllCypressErrorObj> (type: Type, ...args: Parameters<AllCypressErrorObj[Type]>): CypressError {
// If we don't know this "type" of error, return as a non-cypress error
if (!AllCypressErrors[type]) {
const err = new Error(`UNKNOWN ERROR ${JSON.stringify(type)}`) as CypressError
err.isCypressErr = false
err.type = type
err.messageMarkdown = err.message
return err
}
// @ts-expect-error
const result = AllCypressErrors[type](...args) as ErrTemplateResult
const { message, details, originalError, messageMarkdown } = result
const err = new Error(message) as CypressError
err.isCypressErr = true
err.type = type
err.details = details
err.messageMarkdown = messageMarkdown
err.originalError = originalError
if (originalError) {
err.stack = originalError.stack
err.stackWithoutMessage = stackWithoutMessage(originalError.stack ?? '')
} else {
const newErr = new Error()
Error.captureStackTrace(newErr, getError)
err.stack = newErr.stack
err.stackWithoutMessage = stackWithoutMessage(err.stack ?? '')
}
return err
}
export const logWarning = function <Type extends keyof AllCypressErrorObj> (type: Type, ...args: Parameters<AllCypressErrorObj[Type]>) {
const err = getError(type, ...args)
logError(err, 'magenta')
return null
}
export const throwErr = function <Type extends keyof AllCypressErrorObj> (type: Type, ...args: Parameters<AllCypressErrorObj[Type]>) {
const err = getError(type, ...args)
if (!err.originalError) {
Error.captureStackTrace(err, throwErr)
err.stackWithoutMessage = stackWithoutMessage(err.stack ?? '')
}
throw err
}
// For when the error is passed via the socket-base
interface GenericError extends Error {
forBrowser?: never
stackWithoutMessage?: never
[key: string]: any
}
export const cloneErr = function (err: CypressError | GenericError, options: {html?: boolean} = {}) {
_.defaults(options, {
html: false,
})
// pull off these properties
const obj = _.pick(err, 'message', 'messageMarkdown', 'type', 'name', 'stack', 'fileName', 'lineNumber', 'columnNumber') as ClonedError
if (options.html) {
obj.message = ansi_up.ansi_to_html(err.message)
// revert back the distorted characters
// in case there is an error in a child_process
// that contains quotes
.replace(/\&\#x27;/g, '\'')
.replace(/\&quot\;/g, '"')
}
// and any own (custom) properties
// of the err object
Object.entries(err || {}).forEach(([prop, val]) => {
obj[prop] = val
})
if (err.stackWithoutMessage) {
obj.stack = err.stackWithoutMessage
}
return obj
}
export {
stripAnsi,
getError as get,
logWarning as warning,
}
// Re-exporting old namespaces for legacy server access
export {
logError as log,
isCypressErr,
} from './errorUtils'