misc: convert server/lib/modes files to ts + add more cli options to Cloud terminal error outputs (#31211)

* chore: convert server/lib/modes files to ts

* update snapshots

* add back require

* fix call to errors.warning

* Add changelog entry.
This commit is contained in:
Jennifer Shehane
2025-03-06 12:39:24 -05:00
committed by GitHub
parent 17e76f8270
commit 6bd522bb74
15 changed files with 214 additions and 176 deletions
+4
View File
@@ -3,6 +3,10 @@
_Released 3/11/2025 (PENDING)_
**Misc:**
- Additional CLI options will be displayed in the terminal for some Cloud error messages. Addressed in [#31211](https://github.com/cypress-io/cypress/pull/31211).
**Dependency Updates:**
- Upgraded `cli-table3` from `0.5.1` to `0.6.5`. Addressed in [#31166](https://github.com/cypress-io/cypress/pull/31166).
+8 -8
View File
@@ -240,7 +240,7 @@ export const AllCypressErrors = {
https://on.cypress.io/stale-run`
},
CLOUD_ALREADY_COMPLETE: (props: {runUrl: string}) => {
CLOUD_ALREADY_COMPLETE: (props: {runUrl: string, tags: string, group: string, parallel: string, ciBuildId: string}) => {
return errTemplate`\
The run you are attempting to access is already complete and will not accept new groups.
@@ -257,7 +257,7 @@ export const AllCypressErrors = {
https://on.cypress.io/already-complete`
},
CLOUD_PARALLEL_REQUIRED: (arg1: {runUrl: string}) => {
CLOUD_PARALLEL_REQUIRED: (arg1: {tags: string, group: string, runUrl: string, ciBuildId: string }) => {
return errTemplate`\
You did not pass the ${fmt.flag(`--parallel`)} flag, but this run's group was originally created with the --parallel flag.
@@ -274,13 +274,14 @@ export const AllCypressErrors = {
https://on.cypress.io/parallel-required`
},
CLOUD_PARALLEL_DISALLOWED: (arg1: {runUrl: string}) => {
CLOUD_PARALLEL_DISALLOWED: (arg1: {tags: string, group: string, runUrl: string, ciBuildId: 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, {
tags: '--tag',
group: '--group',
parallel: '--parallel',
ciBuildId: '--ciBuildId',
@@ -290,7 +291,7 @@ export const AllCypressErrors = {
https://on.cypress.io/parallel-disallowed`
},
CLOUD_PARALLEL_GROUP_PARAMS_MISMATCH: (arg1: {runUrl: string, parameters: any, payload: any }) => {
CLOUD_PARALLEL_GROUP_PARAMS_MISMATCH: (arg1: {group: string, runUrl: string, ciBuildId: string, parameters: any, payload: any }) => {
let params: any = arg1.parameters
if (arg1.payload?.differentParams) {
@@ -344,7 +345,7 @@ export const AllCypressErrors = {
https://on.cypress.io/parallel-group-params-mismatch`
},
CLOUD_RUN_GROUP_NAME_NOT_UNIQUE: (arg1: {runUrl: string, ciBuildId?: string | null}) => {
CLOUD_RUN_GROUP_NAME_NOT_UNIQUE: (arg1: {group: string, 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.
@@ -370,7 +371,7 @@ export const AllCypressErrors = {
${fmt.off(arg1.link)}`
},
CLOUD_AUTO_CANCEL_MISMATCH: (arg1: {runUrl: string}) => {
CLOUD_AUTO_CANCEL_MISMATCH: (arg1: {runUrl: string, tags: string, group: string, parallel: string, ciBuildId: string, autoCancelAfterFailures: string }) => {
return errTemplate`\
You passed the ${fmt.flag(`--auto-cancel-after-failures`)} flag, but this run originally started with a different value for the ${fmt.flag(`--auto-cancel-after-failures`)} flag.
@@ -1397,7 +1398,6 @@ export const AllCypressErrors = {
https://on.cypress.io/test-retries
`
},
// TODO: test this
INVALID_CONFIG_OPTION: (arg1: string[]) => {
const phrase = arg1.length > 1 ? 'options are' : 'option is'
@@ -1886,7 +1886,7 @@ export const AllCypressErrors = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _typeCheck: Record<keyof AllCypressErrorObj, (...args: any[]) => ErrTemplateResult> = AllCypressErrors
type AllCypressErrorObj = typeof AllCypressErrors
export type AllCypressErrorObj = typeof AllCypressErrors
export type AllCypressErrorNames = keyof typeof AllCypressErrors
@@ -2,6 +2,8 @@ exports['RECORD_PARAMS_WITHOUT_RECORDING-ciBuildId 1'] = `
You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag.
The --ci-build-id flag you passed was: ciBuildId123
The --parallel flag you passed was: undefined
The --auto-cancel-after-failures flag you passed was: undefined
These flags can only be used when recording to Cypress Cloud.
@@ -22,6 +24,8 @@ exports['RECORD_PARAMS_WITHOUT_RECORDING-group 1'] = `
You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag.
The --group flag you passed was: e2e-tests
The --parallel flag you passed was: undefined
The --auto-cancel-after-failures flag you passed was: undefined
These flags can only be used when recording to Cypress Cloud.
@@ -32,6 +36,7 @@ exports['RECORD_PARAMS_WITHOUT_RECORDING-parallel 1'] = `
You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag.
The --parallel flag you passed was: true
The --auto-cancel-after-failures flag you passed was: undefined
These flags can only be used when recording to Cypress Cloud.
@@ -43,6 +48,7 @@ You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after
The --group flag you passed was: electron-smoke-tests
The --parallel flag you passed was: true
The --auto-cancel-after-failures flag you passed was: undefined
These flags can only be used when recording to Cypress Cloud.
@@ -53,6 +59,7 @@ exports['INDETERMINATE_CI_BUILD_ID-group 1'] = `
You passed the --group or --parallel flag but we could not automatically determine or generate a ciBuildId.
The --group flag you passed was: e2e-tests
The --parallel flag you passed was: undefined
In order to use either of these features a ciBuildId must be determined.
@@ -221,6 +228,7 @@ You passed the --parallel flag, but this run group was originally created withou
The existing run is: https://cloud.cypress.io/runs/12345
The --tag flag you passed was:
The --group flag you passed was: electron-smoke-tests
The --ciBuildId flag you passed was: ciBuildId123
@@ -275,6 +283,9 @@ https://on.cypress.io/stale-run
exports['RECORD_PARAMS_WITHOUT_RECORDING-tag 1'] = `
You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag.
The --parallel flag you passed was: undefined
The --auto-cancel-after-failures flag you passed was: undefined
These flags can only be used when recording to Cypress Cloud.
https://on.cypress.io/record-params-without-recording
@@ -422,6 +433,7 @@ https://on.cypress.io/record-params-without-recording
exports['RECORD_PARAMS_WITHOUT_RECORDING-auto-cancel-after-failures 1'] = `
You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag.
The --parallel flag you passed was: undefined
The --auto-cancel-after-failures flag you passed was: 4
These flags can only be used when recording to Cypress Cloud.
+5 -1
View File
@@ -239,7 +239,10 @@ const isRetriableError = (err) => {
export type CreateRunOptions = {
projectRoot: string
ci: string
ci: {
params: string
provider: string
}
ciBuildId: string
projectId: string
recordKey: string
@@ -253,6 +256,7 @@ export type CreateRunOptions = {
testingType: 'e2e' | 'component'
timeout?: number
project: ProjectBase
autoCancelAfterFailures?: number | undefined
}
type CreateRunResponse = {
@@ -68,7 +68,7 @@ const toUploadReportPayload = (acc: {
type UploadArtifactOptions = {
protocolManager?: ProtocolManager
videoUploadUrl?: string
video?: string // filepath to the video artifact
video?: string | null // filepath to the video artifact
screenshots?: {
screenshotId: string
path: string
@@ -9,16 +9,17 @@ require('./environment')
// essentially do it all again when we boot the correct
// mode.
const Promise = require('bluebird')
const debug = require('debug')('cypress:server:cypress')
const { getPublicConfigKeys } = require('@packages/config')
const argsUtils = require('./util/args')
const { telemetry } = require('@packages/telemetry')
const { getCtx, hasCtx } = require('@packages/data-context')
import Promise from 'bluebird'
import Debug from 'debug'
import { getPublicConfigKeys } from '@packages/config'
import argsUtils from './util/args'
import { telemetry } from '@packages/telemetry'
import { getCtx, hasCtx } from '@packages/data-context'
import { warning as errorsWarning } from './errors'
const warning = (code, args) => {
return require('./errors').warning(code, args)
}
const debug = Debug('cypress:server:cypress')
type Mode = 'exit' | 'info' | 'interactive' | 'pkg' | 'record' | 'results' | 'run' | 'smokeTest' | 'version' | 'returnPkg' | 'exitWithCode'
const exit = async (code = 0) => {
// TODO: we shouldn't have to do this
@@ -27,7 +28,7 @@ const exit = async (code = 0) => {
debug('about to exit with code', code)
if (hasCtx()) {
await getCtx().lifecycleManager.mainProcessWillDisconnect().catch((err) => {
await getCtx().lifecycleManager.mainProcessWillDisconnect().catch((err: any) => {
debug('mainProcessWillDisconnect errored with: ', err)
})
}
@@ -37,14 +38,14 @@ const exit = async (code = 0) => {
span?.setAttribute('exitCode', code)
span?.end()
await telemetry.shutdown().catch((err) => {
await telemetry.shutdown().catch((err: any) => {
debug('telemetry shutdown errored with: ', err)
})
return process.exit(code)
}
const showWarningForInvalidConfig = (options) => {
const showWarningForInvalidConfig = (options: any) => {
const publicConfigKeys = getPublicConfigKeys()
const invalidConfigOptions = require('lodash').keys(options.config).reduce((invalid, option) => {
if (!publicConfigKeys.find((configKey) => configKey === option)) {
@@ -55,15 +56,17 @@ const showWarningForInvalidConfig = (options) => {
}, [])
if (invalidConfigOptions.length && options.invokedFromCli) {
return warning('INVALID_CONFIG_OPTION', invalidConfigOptions)
return errorsWarning('INVALID_CONFIG_OPTION', invalidConfigOptions)
}
return undefined
}
const exit0 = () => {
return exit(0)
}
const exitErr = (err) => {
const exitErr = (err: any) => {
// log errors to the console
// and potentially raygun
// and exit with 1
@@ -77,12 +80,12 @@ const exitErr = (err) => {
})
}
module.exports = {
export = {
isCurrentlyRunningElectron () {
return require('./util/electron-app').isRunning()
},
runElectron (mode, options) {
runElectron (mode: Mode, options: any) {
// wrap all of this in a promise to force the
// promise interface - even if it doesn't matter
// in dev mode due to cp.spawn
@@ -95,7 +98,7 @@ module.exports = {
// if we weren't invoked from the CLI
// then display a warning to the user
if (!options.invokedFromCli) {
warning('INVOKED_BINARY_OUTSIDE_NPM_MODULE')
errorsWarning('INVOKED_BINARY_OUTSIDE_NPM_MODULE')
}
debug('running Electron currently')
@@ -107,7 +110,7 @@ module.exports = {
debug('starting Electron')
const cypressElectron = require('@packages/electron')
const fn = (code) => {
const fn = (code: number) => {
// juggle up the totalFailed since our outer
// promise is expecting this object structure
debug('electron finished with', code)
@@ -131,7 +134,7 @@ module.exports = {
})
},
start (argv = []) {
start (argv: any = []) {
debug('starting cypress with argv %o', argv)
// if the CLI passed "--" somewhere, we need to remove it
@@ -144,7 +147,7 @@ module.exports = {
options = argsUtils.toObject(argv)
showWarningForInvalidConfig(options)
} catch (argumentsError) {
} catch (argumentsError: any) {
debug('could not parse CLI arguments: %o', argv)
// note - this is promise-returned call
@@ -153,6 +156,7 @@ module.exports = {
debug('from argv %o got options %o', argv, options)
// @ts-expect-error TODO: Fix type that says attachRecordKey is not a function
telemetry.exporter()?.attachRecordKey(options.key)
if (options.headless) {
@@ -202,14 +206,14 @@ module.exports = {
})
},
startInMode (mode, options) {
startInMode (mode: Mode, options: any) {
debug('starting in mode %s with options %o', mode, options)
switch (mode) {
case 'version':
return require('./modes/pkg')(options)
.get('version')
.then((version) => {
.then((version: any) => {
return console.log(version) // eslint-disable-line no-console
}).then(exit0)
.catch(exitErr)
@@ -221,7 +225,7 @@ module.exports = {
case 'smokeTest':
return this.runElectron(mode, options)
.then((pong) => {
.then((pong: any) => {
if (!this.isCurrentlyRunningElectron()) {
return pong
}
@@ -236,7 +240,7 @@ module.exports = {
case 'returnPkg':
return require('./modes/pkg')(options)
.then((pkg) => {
.then((pkg: any) => {
return console.log(JSON.stringify(pkg)) // eslint-disable-line no-console
}).then(exit0)
.catch(exitErr)
@@ -250,7 +254,7 @@ module.exports = {
// run headlessly and exit
// with num of totalFailed
return this.runElectron(mode, options)
.then((results) => {
.then((results: any) => {
if (results.runs) {
const isCanceled = results.runs.filter((run) => run.skippedSpec).length
-8
View File
@@ -1,8 +0,0 @@
const _ = require('lodash')
const Promise = require('bluebird')
module.exports = (options) => {
return Promise.try(() => {
return _.toNumber(options.exitWithCode)
})
}
+8
View File
@@ -0,0 +1,8 @@
import { toNumber } from 'lodash'
import Promise from 'bluebird'
export = (options) => {
return Promise.try(() => {
return toNumber(options.exitWithCode)
})
}
@@ -1,13 +1,15 @@
/* eslint-disable no-console */
const debug = require('debug')('cypress:server:info')
const launcher = require('@packages/launcher')
const pluralize = require('pluralize')
const { stripIndent } = require('common-tags')
const browserUtils = require('../browsers/utils')
const _ = require('lodash')
const chalk = require('chalk')
const { fs } = require('../util/fs')
import Debug from 'debug'
import { detect as launcherDetect } from '@packages/launcher'
import pluralize from 'pluralize'
import { stripIndent } from 'common-tags'
import browserUtils from '../browsers/utils'
import _ from 'lodash'
import chalk from 'chalk'
import { fs } from '../util/fs'
import type { FoundBrowser } from '@packages/types/src/browser'
const debug = Debug('cypress:server:info')
// color for numbers and short values
const n = chalk.green
// color for paths
@@ -21,7 +23,7 @@ const link = chalk.blue.underline
* If the list has at least 1 item, picks a random item
* and returns it AND the remaining items.
*/
const pickRandomItem = (list) => {
const pickRandomItem = (list: any) => {
if (!list.length) {
return {
item: null,
@@ -40,7 +42,7 @@ const pickRandomItem = (list) => {
// Usually the full browser name to pass via --browser
// is <name>:<channel>. If the channel is stable, you
// can just do "--browser <name>"
const formBrowserName = (browser) => {
const formBrowserName = (browser: FoundBrowser) => {
if (browser.channel === 'stable') {
return browser.name
}
@@ -51,9 +53,9 @@ const formBrowserName = (browser) => {
// for each browser computes the profile folder
// and checks if the folder exists. If exists,
// adds it to the browser object as a property
const addProfilePath = async (browsers = []) => {
const addProfilePath = async (browsers: FoundBrowser[] = []) => {
for (const browser of browsers) {
const profilePath = browserUtils.getBrowserPath(browser)
const profilePath: string = browserUtils.getBrowserPath(browser)
debug('checking profile path %s for browser %s:%s', profilePath, browser.name, browser.channel)
try {
@@ -71,11 +73,11 @@ const addProfilePath = async (browsers = []) => {
return browsers
}
const print = (browsers = []) => {
const print = (browsers: FoundBrowser[] = []) => {
console.log('Displaying Cypress info...')
console.log('')
if (browsers.length) {
console.log('Detected %s %s installed:', n(browsers.length), pluralize('browser', browsers.length))
console.log('Detected %s %s installed:', n(`${browsers.length}`), pluralize('browser', browsers.length))
} else {
console.log('Detected no known browsers installed')
}
@@ -126,7 +128,7 @@ const print = (browsers = []) => {
}
const info = () => {
return launcher.detect()
return launcherDetect()
.then(addProfilePath)
.then(print)
}
-6
View File
@@ -1,6 +0,0 @@
const Promise = require('bluebird')
const pkg = require('@packages/root')
module.exports = () => {
return Promise.resolve(pkg)
}
+6
View File
@@ -0,0 +1,6 @@
import Promise from 'bluebird'
import pkg from '@packages/root'
export = () => {
return Promise.resolve(pkg)
}
@@ -1,36 +1,49 @@
const _ = require('lodash')
const path = require('path')
const la = require('lazy-ass')
const check = require('check-more-types')
const debug = require('debug')('cypress:server:record')
const debugCiInfo = require('debug')('cypress:server:record:ci-info')
const Promise = require('bluebird')
const isForkPr = require('is-fork-pr')
const commitInfo = require('@cypress/commit-info')
const { telemetry } = require('@packages/telemetry')
import _ from 'lodash'
import path from 'path'
import la from 'lazy-ass'
import check from 'check-more-types'
import Debug from 'debug'
import Promise from 'bluebird'
import isForkPr from 'is-fork-pr'
import commitInfo from '@cypress/commit-info'
import { telemetry } from '@packages/telemetry'
import { hideKeys } from '@packages/config'
const { hideKeys } = require('@packages/config')
import { default as api } from '../cloud/api'
import exception from '../cloud/exception'
import { get as getErrors, warning as errorsWarning, throwErr } from '../errors'
import capture from '../capture'
import { getResolvedRuntimeConfig } from '../config'
import env from '../util/env'
import ciProvider from '../util/ci_provider'
import { flattenSuiteIntoRunnables } from '../util/tests_utils'
import { countStudioUsage } from '../util/spec_writer'
import { uploadArtifacts } from '../cloud/artifacts/upload_artifacts'
const api = require('../cloud/api').default
const exception = require('../cloud/exception')
import type { Cfg } from '../project-base'
import type { RunResult } from './results'
import type { ReadyOptions } from './run'
const errors = require('../errors')
const capture = require('../capture')
const Config = require('../config')
const env = require('../util/env')
const ciProvider = require('../util/ci_provider')
interface InstanceOptions {
spec?: string
runId?: string
group?: string
groupId?: string
parallel?: boolean
machineId?: string
ciBuildId?: string
platform?: any
}
const testsUtils = require('../util/tests_utils')
const specWriter = require('../util/spec_writer')
const { uploadArtifacts } = require('../cloud/artifacts/upload_artifacts')
const debug = Debug('cypress:server:record')
const debugCiInfo = Debug('cypress:server:record:ci-info')
// dont yell about any errors either
const runningInternalTests = () => {
return env.get('CYPRESS_INTERNAL_SYSTEM_TESTS') === '1'
}
const haveProjectIdAndKeyButNoRecordOption = (projectId, options) => {
const haveProjectIdAndKeyButNoRecordOption = (projectId: Cfg['projectId'], options: ReadyOptions) => {
// if we have a project id and we have a key
// and record hasn't been set to true or false
return (projectId && options.key) && (
@@ -38,20 +51,22 @@ const haveProjectIdAndKeyButNoRecordOption = (projectId, options) => {
)
}
const warnIfProjectIdButNoRecordOption = (projectId, options) => {
const warnIfProjectIdButNoRecordOption = (projectId: Cfg['projectId'], options: ReadyOptions) => {
if (haveProjectIdAndKeyButNoRecordOption(projectId, options)) {
// log a warning telling the user
// that they either need to provide us
// with a RECORD_KEY or turn off
// record mode
return errors.warning('PROJECT_ID_AND_KEY_BUT_MISSING_RECORD_OPTION', projectId)
return errorsWarning('PROJECT_ID_AND_KEY_BUT_MISSING_RECORD_OPTION', `${projectId}`)
}
return undefined
}
const throwCloudCannotProceed = ({ parallel, ciBuildId, group, err }) => {
const errMsg = parallel ? 'CLOUD_CANNOT_PROCEED_IN_PARALLEL' : 'CLOUD_CANNOT_PROCEED_IN_SERIAL'
const errToThrow = errors.get(errMsg, {
const errToThrow = getErrors(errMsg, {
response: err,
flags: {
group,
@@ -65,46 +80,46 @@ const throwCloudCannotProceed = ({ parallel, ciBuildId, group, err }) => {
throw errToThrow
}
const throwIfIndeterminateCiBuildId = (ciBuildId, parallel, group) => {
const throwIfIndeterminateCiBuildId = (ciBuildId: ReadyOptions['ciBuildId'], parallel: ReadyOptions['parallel'], group: ReadyOptions['group']) => {
if ((!ciBuildId && !ciProvider.provider()) && (parallel || group)) {
errors.throwErr(
throwErr(
'INDETERMINATE_CI_BUILD_ID',
{
group,
parallel,
parallel: `${parallel}`,
},
ciProvider.detectableCiBuildIdProviders(),
)
}
}
const throwIfRecordParamsWithoutRecording = (record, ciBuildId, parallel, group, tag, autoCancelAfterFailures) => {
const throwIfRecordParamsWithoutRecording = (record: ReadyOptions['record'], ciBuildId: ReadyOptions['ciBuildId'], parallel: ReadyOptions['parallel'], group: ReadyOptions['group'], tag: ReadyOptions['tag'], autoCancelAfterFailures: ReadyOptions['autoCancelAfterFailures']) => {
if (!record && _.some([ciBuildId, parallel, group, tag, autoCancelAfterFailures !== undefined])) {
errors.throwErr('RECORD_PARAMS_WITHOUT_RECORDING', {
throwErr('RECORD_PARAMS_WITHOUT_RECORDING', {
ciBuildId,
tag,
group,
parallel,
autoCancelAfterFailures,
parallel: `${parallel}`,
autoCancelAfterFailures: `${autoCancelAfterFailures}`,
})
}
}
const throwIfIncorrectCiBuildIdUsage = (ciBuildId, parallel, group) => {
const throwIfIncorrectCiBuildIdUsage = (ciBuildId: ReadyOptions['ciBuildId'], parallel: ReadyOptions['parallel'], group: ReadyOptions['group']) => {
// we've been given an explicit ciBuildId
// but no parallel or group flag
if (ciBuildId && (!parallel && !group)) {
errors.throwErr('INCORRECT_CI_BUILD_ID_USAGE', { ciBuildId })
throwErr('INCORRECT_CI_BUILD_ID_USAGE', { ciBuildId })
}
}
const throwIfNoProjectId = (projectId, configFile) => {
const throwIfNoProjectId = (projectId: Cfg['projectId'], configFile: any) => {
if (!projectId) {
errors.throwErr('CANNOT_RECORD_NO_PROJECT_ID', configFile)
throwErr('CANNOT_RECORD_NO_PROJECT_ID', configFile)
}
}
const getSpecRelativePath = (spec) => {
const getSpecRelativePath = (spec: ReadyOptions['spec']) => {
return _.get(spec, 'relative', null)
}
@@ -135,7 +150,7 @@ returns:
]
*/
const updateInstanceStdout = async (options = {}) => {
const updateInstanceStdout = async (options: any = {}) => {
const { runId, instanceId, captured } = options
const stdout = captured.toString()
@@ -144,21 +159,23 @@ const updateInstanceStdout = async (options = {}) => {
runId,
stdout,
instanceId,
}).catch((err) => {
}).catch((err: any) => {
debug('failed updating instance stdout %o', {
stack: err.stack,
})
errors.warning('CLOUD_CANNOT_CREATE_RUN_OR_INSTANCE', err)
errorsWarning('CLOUD_CANNOT_CREATE_RUN_OR_INSTANCE', err)
// dont log exceptions if we have a 503 status code
if (err.statusCode !== 503) {
return exception.create(err)
}
return undefined
}).finally(capture.restore)
}
const postInstanceResults = (options = {}) => {
const postInstanceResults = (options: any = {}) => {
const { runId, instanceId, results, group, parallel, ciBuildId, metadata } = options
let { stats, tests, video, screenshots, reporterStats, error } = results
@@ -187,7 +204,7 @@ const postInstanceResults = (options = {}) => {
screenshots,
metadata,
})
.catch((err) => {
.catch((err: any) => {
debug('failed updating instance %o', {
stack: err.stack,
})
@@ -196,7 +213,7 @@ const postInstanceResults = (options = {}) => {
})
}
const getCommitFromGitOrCi = (git) => {
const getCommitFromGitOrCi = (git: any) => {
la(check.object(git), 'expected git information object', git)
return ciProvider.commitDefaults({
@@ -210,7 +227,7 @@ const getCommitFromGitOrCi = (git) => {
})
}
const billingLink = (orgId) => {
const billingLink = (orgId: any) => {
if (orgId) {
return `https://on.cypress.io/dashboard/organizations/${orgId}/billing`
}
@@ -218,11 +235,11 @@ const billingLink = (orgId) => {
return 'https://on.cypress.io/set-up-billing'
}
const gracePeriodMessage = (gracePeriodEnds) => {
const gracePeriodMessage = (gracePeriodEnds: any) => {
return gracePeriodEnds || 'the grace period ends'
}
const createRun = Promise.method((options = {}) => {
const createRun = Promise.method((options: any = {}) => {
_.defaults(options, {
group: null,
tags: null,
@@ -242,11 +259,11 @@ const createRun = Promise.method((options = {}) => {
// a PR because this logic triggers unintended here
if (isForkPr.isForkPr() && !runningInternalTests()) {
// bail with a warning
return errors.warning('RECORDING_FROM_FORK_PR')
return errorsWarning('RECORDING_FROM_FORK_PR')
}
// else throw
errors.throwErr('RECORD_KEY_MISSING')
throwErr('RECORD_KEY_MISSING')
}
// go back to being a string
@@ -289,66 +306,66 @@ const createRun = Promise.method((options = {}) => {
autoCancelAfterFailures,
project,
})
.tap((response) => {
.tap((response: any) => {
if (!(response && response.warnings && response.warnings.length)) {
return
}
return _.each(response.warnings, (warning) => {
return _.each(response.warnings, (warning: any) => {
switch (warning.code) {
case 'FREE_PLAN_IN_GRACE_PERIOD_EXCEEDS_MONTHLY_PRIVATE_TESTS':
return errors.warning('FREE_PLAN_IN_GRACE_PERIOD_EXCEEDS_MONTHLY_PRIVATE_TESTS', {
return errorsWarning('FREE_PLAN_IN_GRACE_PERIOD_EXCEEDS_MONTHLY_PRIVATE_TESTS', {
limit: warning.limit,
usedTestsMessage: 'private test',
gracePeriodMessage: gracePeriodMessage(warning.gracePeriodEnds),
link: billingLink(warning.orgId),
})
case 'FREE_PLAN_IN_GRACE_PERIOD_EXCEEDS_MONTHLY_TESTS':
return errors.warning('FREE_PLAN_IN_GRACE_PERIOD_EXCEEDS_MONTHLY_TESTS', {
return errorsWarning('FREE_PLAN_IN_GRACE_PERIOD_EXCEEDS_MONTHLY_TESTS', {
limit: warning.limit,
usedTestsMessage: 'test',
gracePeriodMessage: gracePeriodMessage(warning.gracePeriodEnds),
link: billingLink(warning.orgId),
})
case 'FREE_PLAN_IN_GRACE_PERIOD_PARALLEL_FEATURE':
return errors.warning('FREE_PLAN_IN_GRACE_PERIOD_PARALLEL_FEATURE', {
return errorsWarning('FREE_PLAN_IN_GRACE_PERIOD_PARALLEL_FEATURE', {
gracePeriodMessage: gracePeriodMessage(warning.gracePeriodEnds),
link: billingLink(warning.orgId),
})
case 'FREE_PLAN_EXCEEDS_MONTHLY_TESTS_V2':
return errors.warning('PLAN_EXCEEDS_MONTHLY_TESTS', {
return errorsWarning('PLAN_EXCEEDS_MONTHLY_TESTS', {
planType: 'free',
limit: warning.limit,
usedTestsMessage: 'test',
link: billingLink(warning.orgId),
})
case 'PAID_PLAN_EXCEEDS_MONTHLY_PRIVATE_TESTS':
return errors.warning('PLAN_EXCEEDS_MONTHLY_TESTS', {
return errorsWarning('PLAN_EXCEEDS_MONTHLY_TESTS', {
planType: 'current',
limit: warning.limit,
usedTestsMessage: 'private test',
link: billingLink(warning.orgId),
})
case 'PAID_PLAN_EXCEEDS_MONTHLY_TESTS':
return errors.warning('PLAN_EXCEEDS_MONTHLY_TESTS', {
return errorsWarning('PLAN_EXCEEDS_MONTHLY_TESTS', {
planType: 'current',
limit: warning.limit,
usedTestsMessage: 'test',
link: billingLink(warning.orgId),
})
case 'PLAN_IN_GRACE_PERIOD_RUN_GROUPING_FEATURE_USED':
return errors.warning('PLAN_IN_GRACE_PERIOD_RUN_GROUPING_FEATURE_USED', {
return errorsWarning('PLAN_IN_GRACE_PERIOD_RUN_GROUPING_FEATURE_USED', {
gracePeriodMessage: gracePeriodMessage(warning.gracePeriodEnds),
link: billingLink(warning.orgId),
})
default:
return errors.warning('CLOUD_UNKNOWN_CREATE_RUN_WARNING', {
return errorsWarning('CLOUD_UNKNOWN_CREATE_RUN_WARNING', {
message: warning.message,
props: _.omit(warning, 'message'),
})
}
})
}).catch((err) => {
}).catch((err: any) => {
debug('failed creating run with status %o',
_.pick(err, ['name', 'message', 'statusCode', 'stack']))
@@ -362,7 +379,7 @@ const createRun = Promise.method((options = {}) => {
recordKey = 'undefined'
}
return errors.throwErr('CLOUD_RECORD_KEY_NOT_VALID', recordKey, projectId)
return throwErr('CLOUD_RECORD_KEY_NOT_VALID', recordKey, projectId)
case 402: {
const { code, payload } = err.error
@@ -371,31 +388,31 @@ const createRun = Promise.method((options = {}) => {
switch (code) {
case 'FREE_PLAN_EXCEEDS_MONTHLY_PRIVATE_TESTS':
return errors.throwErr('FREE_PLAN_EXCEEDS_MONTHLY_PRIVATE_TESTS', {
return throwErr('FREE_PLAN_EXCEEDS_MONTHLY_PRIVATE_TESTS', {
limit,
usedTestsMessage: 'private test',
link: billingLink(orgId),
})
case 'FREE_PLAN_EXCEEDS_MONTHLY_TESTS':
return errors.throwErr('FREE_PLAN_EXCEEDS_MONTHLY_TESTS', {
return throwErr('FREE_PLAN_EXCEEDS_MONTHLY_TESTS', {
limit,
usedTestsMessage: 'test',
link: billingLink(orgId),
})
case 'PARALLEL_FEATURE_NOT_AVAILABLE_IN_PLAN':
return errors.throwErr('PARALLEL_FEATURE_NOT_AVAILABLE_IN_PLAN', {
return throwErr('PARALLEL_FEATURE_NOT_AVAILABLE_IN_PLAN', {
link: billingLink(orgId),
})
case 'RUN_GROUPING_FEATURE_NOT_AVAILABLE_IN_PLAN':
return errors.throwErr('RUN_GROUPING_FEATURE_NOT_AVAILABLE_IN_PLAN', {
return throwErr('RUN_GROUPING_FEATURE_NOT_AVAILABLE_IN_PLAN', {
link: billingLink(orgId),
})
case 'AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN':
return errors.throwErr('CLOUD_AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN', {
return throwErr('CLOUD_AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN', {
link: billingLink(orgId),
})
default:
return errors.throwErr('CLOUD_UNKNOWN_INVALID_REQUEST', {
return throwErr('CLOUD_UNKNOWN_INVALID_REQUEST', {
response: err,
flags: {
group,
@@ -407,17 +424,17 @@ const createRun = Promise.method((options = {}) => {
}
}
case 404:
return errors.throwErr('CLOUD_PROJECT_NOT_FOUND', projectId, path.basename(options.configFile))
return throwErr('CLOUD_PROJECT_NOT_FOUND', projectId, path.basename(options.configFile))
case 412:
return errors.throwErr('CLOUD_INVALID_RUN_REQUEST', err.error)
return throwErr('CLOUD_INVALID_RUN_REQUEST', err.error)
case 422: {
const { code, payload } = err.error
const runUrl = _.get(payload, 'runUrl')
const runUrl: string = _.get(payload, 'runUrl')
switch (code) {
case 'RUN_GROUP_NAME_NOT_UNIQUE':
return errors.throwErr('CLOUD_RUN_GROUP_NAME_NOT_UNIQUE', {
return throwErr('CLOUD_RUN_GROUP_NAME_NOT_UNIQUE', {
group,
runUrl,
ciBuildId,
@@ -425,7 +442,7 @@ const createRun = Promise.method((options = {}) => {
case 'PARALLEL_GROUP_PARAMS_MISMATCH': {
const { browserName, browserVersion, osName, osVersion } = platform
return errors.throwErr('CLOUD_PARALLEL_GROUP_PARAMS_MISMATCH', {
return throwErr('CLOUD_PARALLEL_GROUP_PARAMS_MISMATCH', {
group,
runUrl,
ciBuildId,
@@ -440,21 +457,21 @@ const createRun = Promise.method((options = {}) => {
})
}
case 'PARALLEL_DISALLOWED':
return errors.throwErr('CLOUD_PARALLEL_DISALLOWED', {
return throwErr('CLOUD_PARALLEL_DISALLOWED', {
tags,
group,
runUrl,
ciBuildId,
})
case 'PARALLEL_REQUIRED':
return errors.throwErr('CLOUD_PARALLEL_REQUIRED', {
return throwErr('CLOUD_PARALLEL_REQUIRED', {
tags,
group,
runUrl,
ciBuildId,
})
case 'ALREADY_COMPLETE':
return errors.throwErr('CLOUD_ALREADY_COMPLETE', {
return throwErr('CLOUD_ALREADY_COMPLETE', {
runUrl,
tags,
group,
@@ -462,7 +479,7 @@ const createRun = Promise.method((options = {}) => {
ciBuildId,
})
case 'STALE_RUN':
return errors.throwErr('CLOUD_STALE_RUN', {
return throwErr('CLOUD_STALE_RUN', {
runUrl,
tags,
group,
@@ -470,7 +487,7 @@ const createRun = Promise.method((options = {}) => {
ciBuildId,
})
case 'AUTO_CANCEL_MISMATCH':
return errors.throwErr('CLOUD_AUTO_CANCEL_MISMATCH', {
return throwErr('CLOUD_AUTO_CANCEL_MISMATCH', {
runUrl,
tags,
group,
@@ -479,7 +496,7 @@ const createRun = Promise.method((options = {}) => {
autoCancelAfterFailures,
})
default:
return errors.throwErr('CLOUD_UNKNOWN_INVALID_REQUEST', {
return throwErr('CLOUD_UNKNOWN_INVALID_REQUEST', {
response: err,
flags: {
tags,
@@ -496,10 +513,10 @@ const createRun = Promise.method((options = {}) => {
})
})
const createInstance = (options = {}) => {
const createInstance = (options: InstanceOptions = {}) => {
let { runId, group, groupId, parallel, machineId, ciBuildId, platform, spec } = options
spec = getSpecRelativePath(spec)
spec = spec ? getSpecRelativePath(spec) : null
return api.createInstance({
spec,
@@ -508,7 +525,7 @@ const createInstance = (options = {}) => {
platform,
machineId,
})
.catch((err) => {
.catch((err: any) => {
debug('failed creating instance %o', {
stack: err.stack,
})
@@ -539,12 +556,12 @@ const _postInstanceTests = ({
tests,
hooks,
})
.catch((err) => {
.catch((err: any) => {
throwCloudCannotProceed({ parallel, ciBuildId, group, err })
})
}
const createRunAndRecordSpecs = (options = {}) => {
const createRunAndRecordSpecs = (options: any = {}) => {
const { specPattern,
specs,
sys,
@@ -568,7 +585,7 @@ const createRunAndRecordSpecs = (options = {}) => {
const tags = _.split(options.tag, ',')
return commitInfo.commitInfo(projectRoot)
.then((git) => {
.then((git: any) => {
debugCiInfo('found the following git information')
debugCiInfo(git)
@@ -583,6 +600,7 @@ const createRunAndRecordSpecs = (options = {}) => {
telemetry.startSpan({ name: 'record:createRun' })
// @ts-expect-error TODO: Fix this saying its expecting 0 args
return createRun({
projectRoot,
git,
@@ -600,7 +618,7 @@ const createRunAndRecordSpecs = (options = {}) => {
autoCancelAfterFailures,
project,
})
.then((resp) => {
.then((resp: any) => {
telemetry.getSpan('record:createRun')?.end()
if (!resp) {
// if a forked run, can't record and can't be parallel
@@ -616,7 +634,7 @@ const createRunAndRecordSpecs = (options = {}) => {
let captured = null
let instanceId = null
const beforeSpecRun = (spec) => {
const beforeSpecRun = (spec: any) => {
telemetry.startSpan({ name: 'record:beforeSpecRun' })
project.setOnTestsReceived(onTestsReceived)
capture.restore()
@@ -633,7 +651,7 @@ const createRunAndRecordSpecs = (options = {}) => {
ciBuildId,
machineId,
})
.then((resp = {}) => {
.then((resp: any = {}) => {
instanceId = resp.instanceId
// pull off only what we need
@@ -652,7 +670,7 @@ const createRunAndRecordSpecs = (options = {}) => {
})
}
const afterSpecRun = (spec, results, config) => {
const afterSpecRun = (spec: any, results: RunResult, config: any) => {
// don't do anything if we failed to
// create the instance
if (!instanceId || results.skippedSpec) {
@@ -663,7 +681,7 @@ const createRunAndRecordSpecs = (options = {}) => {
debug('after spec run %o', { spec })
return specWriter.countStudioUsage(spec.absolute)
return countStudioUsage(spec.absolute)
.then((metadata) => {
return postInstanceResults({
group,
@@ -675,7 +693,7 @@ const createRunAndRecordSpecs = (options = {}) => {
metadata,
})
})
.then((resp) => {
.then((resp: any) => {
if (!resp) {
return
}
@@ -686,6 +704,7 @@ const createRunAndRecordSpecs = (options = {}) => {
return uploadArtifacts({
runId,
// @ts-expect-error TODO: Fix this saying instanceId cannot be null here - we returned earlier if null
instanceId,
video,
screenshots,
@@ -712,7 +731,7 @@ const createRunAndRecordSpecs = (options = {}) => {
})
}
const onTestsReceived = (async (runnables, cb) => {
const onTestsReceived = (async (runnables: any, cb: any) => {
// we failed createInstance earlier, nothing to do
if (!instanceId) {
return cb()
@@ -722,9 +741,9 @@ const createRunAndRecordSpecs = (options = {}) => {
// this also means runtimeConfig will be missing
runnables = runnables || {}
const r = testsUtils.flattenSuiteIntoRunnables(runnables)
const r = flattenSuiteIntoRunnables(runnables)
const runtimeConfig = runnables.runtimeConfig
const resolvedRuntimeConfig = Config.getResolvedRuntimeConfig(config, runtimeConfig)
const resolvedRuntimeConfig = getResolvedRuntimeConfig(config, runtimeConfig)
const tests = _.chain(r[0])
.uniqBy('id')
@@ -774,7 +793,7 @@ const createRunAndRecordSpecs = (options = {}) => {
ciBuildId,
group,
})
.catch((err) => {
.catch((err: any) => {
onError(err)
return responseDidFail
@@ -788,7 +807,7 @@ const createRunAndRecordSpecs = (options = {}) => {
}
if (_.some(response.actions, { type: 'SPEC', action: 'SKIP' })) {
errors.warning('CLOUD_CANCEL_SKIPPED_SPEC')
errorsWarning('CLOUD_CANCEL_SKIPPED_SPEC')
// set a property on the response so the browser runner
// knows not to start executing tests
@@ -812,7 +831,7 @@ const createRunAndRecordSpecs = (options = {}) => {
})
}
module.exports = {
export = {
createRun,
createInstance,
+3 -11
View File
@@ -5,10 +5,10 @@ import type { FoundBrowser, SpecWithRelativeRoot } from '@packages/types'
import path from 'path'
import type { Cfg } from '../project-base'
import type { ScreenshotMetadata } from './run'
type dateTimeISO = string
type ms = number
type pixels = number
interface TestError {
message: string
@@ -48,20 +48,12 @@ interface HookInformation {
body: string
}
interface ScreenshotInformation {
height: pixels
name: string
path: string
takenAt: dateTimeISO
width: pixels
}
interface RunResult {
export interface RunResult {
error: string | null
hooks: HookInformation[]
reporter: string
reporterStats: object
screenshots: ScreenshotInformation[]
screenshots: ScreenshotMetadata[]
skippedSpec: boolean
spec: SpecWithRelativeRoot
stats: {
+4 -3
View File
@@ -32,7 +32,8 @@ import { CDPFailedToStartFirefox } from '../browsers/firefox'
import type { CypressError } from '@packages/errors'
type SetScreenshotMetadata = (data: TakeScreenshotProps) => void
type ScreenshotMetadata = ReturnType<typeof screenshotMetadata>
export type ScreenshotMetadata = ReturnType<typeof screenshotMetadata>
type TakeScreenshotProps = any
type RunEachSpec = any
type BeforeSpecRun = any
@@ -753,7 +754,7 @@ async function waitForTestsToFinishRunning (options: { project: Project, screens
return results
}
function screenshotMetadata (data, resp) {
function screenshotMetadata (data: any, resp: any) {
return {
screenshotId: random.id(),
name: data.name || null,
@@ -1006,7 +1007,7 @@ async function runSpec (config, spec: SpecWithRelativeRoot, options: { project:
return { results }
}
interface ReadyOptions {
export interface ReadyOptions {
autoCancelAfterFailures: number | false
browser: string
browsers?: FoundBrowser[]
@@ -1,5 +1,5 @@
module.exports = {
run (options) {
export = {
run (options: any) {
// eslint-disable-next-line no-console
console.log(options.pong)