From 6bd522bb7475a04030431a919c07dba0bc056e36 Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Thu, 6 Mar 2025 12:39:24 -0500 Subject: [PATCH] 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. --- cli/CHANGELOG.md | 4 + packages/errors/src/errors.ts | 16 +- packages/server/__snapshots__/cypress_spec.js | 12 + packages/server/lib/cloud/api/index.ts | 6 +- .../lib/cloud/artifacts/upload_artifacts.ts | 2 +- .../server/lib/{cypress.js => cypress.ts} | 56 ++--- packages/server/lib/modes/exit.js | 8 - packages/server/lib/modes/exit.ts | 8 + .../server/lib/modes/{info.js => info.ts} | 34 +-- packages/server/lib/modes/pkg.js | 6 - packages/server/lib/modes/pkg.ts | 6 + .../server/lib/modes/{record.js => record.ts} | 217 ++++++++++-------- packages/server/lib/modes/results.ts | 14 +- packages/server/lib/modes/run.ts | 7 +- .../modes/{smoke_test.js => smoke_test.ts} | 4 +- 15 files changed, 219 insertions(+), 181 deletions(-) rename packages/server/lib/{cypress.js => cypress.ts} (84%) delete mode 100644 packages/server/lib/modes/exit.js create mode 100644 packages/server/lib/modes/exit.ts rename packages/server/lib/modes/{info.js => info.ts} (78%) delete mode 100644 packages/server/lib/modes/pkg.js create mode 100644 packages/server/lib/modes/pkg.ts rename packages/server/lib/modes/{record.js => record.ts} (74%) rename packages/server/lib/modes/{smoke_test.js => smoke_test.ts} (73%) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index e48c8958e728..b52ac1a333dc 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -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). diff --git a/packages/errors/src/errors.ts b/packages/errors/src/errors.ts index 702ce8d5eb97..9f31c5b3933a 100644 --- a/packages/errors/src/errors.ts +++ b/packages/errors/src/errors.ts @@ -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 ErrTemplateResult> = AllCypressErrors -type AllCypressErrorObj = typeof AllCypressErrors +export type AllCypressErrorObj = typeof AllCypressErrors export type AllCypressErrorNames = keyof typeof AllCypressErrors diff --git a/packages/server/__snapshots__/cypress_spec.js b/packages/server/__snapshots__/cypress_spec.js index 500b9230cae9..e37d89dfe53b 100644 --- a/packages/server/__snapshots__/cypress_spec.js +++ b/packages/server/__snapshots__/cypress_spec.js @@ -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. diff --git a/packages/server/lib/cloud/api/index.ts b/packages/server/lib/cloud/api/index.ts index 15c185ed864f..22626014f833 100644 --- a/packages/server/lib/cloud/api/index.ts +++ b/packages/server/lib/cloud/api/index.ts @@ -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 = { diff --git a/packages/server/lib/cloud/artifacts/upload_artifacts.ts b/packages/server/lib/cloud/artifacts/upload_artifacts.ts index d41ef303225b..0b6207c7c3d0 100644 --- a/packages/server/lib/cloud/artifacts/upload_artifacts.ts +++ b/packages/server/lib/cloud/artifacts/upload_artifacts.ts @@ -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 diff --git a/packages/server/lib/cypress.js b/packages/server/lib/cypress.ts similarity index 84% rename from packages/server/lib/cypress.js rename to packages/server/lib/cypress.ts index 3d029d570edb..427b6abeb5e4 100644 --- a/packages/server/lib/cypress.js +++ b/packages/server/lib/cypress.ts @@ -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') - -const warning = (code, args) => { - return require('./errors').warning(code, args) -} +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 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 diff --git a/packages/server/lib/modes/exit.js b/packages/server/lib/modes/exit.js deleted file mode 100644 index 6cfef4dba0a8..000000000000 --- a/packages/server/lib/modes/exit.js +++ /dev/null @@ -1,8 +0,0 @@ -const _ = require('lodash') -const Promise = require('bluebird') - -module.exports = (options) => { - return Promise.try(() => { - return _.toNumber(options.exitWithCode) - }) -} diff --git a/packages/server/lib/modes/exit.ts b/packages/server/lib/modes/exit.ts new file mode 100644 index 000000000000..426ccb0bcc91 --- /dev/null +++ b/packages/server/lib/modes/exit.ts @@ -0,0 +1,8 @@ +import { toNumber } from 'lodash' +import Promise from 'bluebird' + +export = (options) => { + return Promise.try(() => { + return toNumber(options.exitWithCode) + }) +} diff --git a/packages/server/lib/modes/info.js b/packages/server/lib/modes/info.ts similarity index 78% rename from packages/server/lib/modes/info.js rename to packages/server/lib/modes/info.ts index 81f7c667f17f..dbfc55655c73 100644 --- a/packages/server/lib/modes/info.js +++ b/packages/server/lib/modes/info.ts @@ -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 :. If the channel is stable, you // can just do "--browser " -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) } diff --git a/packages/server/lib/modes/pkg.js b/packages/server/lib/modes/pkg.js deleted file mode 100644 index 4682448f5810..000000000000 --- a/packages/server/lib/modes/pkg.js +++ /dev/null @@ -1,6 +0,0 @@ -const Promise = require('bluebird') -const pkg = require('@packages/root') - -module.exports = () => { - return Promise.resolve(pkg) -} diff --git a/packages/server/lib/modes/pkg.ts b/packages/server/lib/modes/pkg.ts new file mode 100644 index 000000000000..2e781081247c --- /dev/null +++ b/packages/server/lib/modes/pkg.ts @@ -0,0 +1,6 @@ +import Promise from 'bluebird' +import pkg from '@packages/root' + +export = () => { + return Promise.resolve(pkg) +} diff --git a/packages/server/lib/modes/record.js b/packages/server/lib/modes/record.ts similarity index 74% rename from packages/server/lib/modes/record.js rename to packages/server/lib/modes/record.ts index 234123efdb5c..06299a202d8e 100644 --- a/packages/server/lib/modes/record.js +++ b/packages/server/lib/modes/record.ts @@ -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') - -const { hideKeys } = require('@packages/config') - -const api = require('../cloud/api').default -const exception = require('../cloud/exception') - -const errors = require('../errors') -const capture = require('../capture') -const Config = require('../config') -const env = require('../util/env') -const ciProvider = require('../util/ci_provider') - -const testsUtils = require('../util/tests_utils') -const specWriter = require('../util/spec_writer') +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' + +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' + +import type { Cfg } from '../project-base' +import type { RunResult } from './results' +import type { ReadyOptions } from './run' + +interface InstanceOptions { + spec?: string + runId?: string + group?: string + groupId?: string + parallel?: boolean + machineId?: string + ciBuildId?: string + platform?: any +} -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, diff --git a/packages/server/lib/modes/results.ts b/packages/server/lib/modes/results.ts index 9da4b263aa83..dc939f9238eb 100644 --- a/packages/server/lib/modes/results.ts +++ b/packages/server/lib/modes/results.ts @@ -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: { diff --git a/packages/server/lib/modes/run.ts b/packages/server/lib/modes/run.ts index b4899dc9343a..721667ffcb17 100644 --- a/packages/server/lib/modes/run.ts +++ b/packages/server/lib/modes/run.ts @@ -32,7 +32,8 @@ import { CDPFailedToStartFirefox } from '../browsers/firefox' import type { CypressError } from '@packages/errors' type SetScreenshotMetadata = (data: TakeScreenshotProps) => void -type ScreenshotMetadata = ReturnType +export type ScreenshotMetadata = ReturnType + 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[] diff --git a/packages/server/lib/modes/smoke_test.js b/packages/server/lib/modes/smoke_test.ts similarity index 73% rename from packages/server/lib/modes/smoke_test.js rename to packages/server/lib/modes/smoke_test.ts index 14cedbc429a6..b20375a623c9 100644 --- a/packages/server/lib/modes/smoke_test.js +++ b/packages/server/lib/modes/smoke_test.ts @@ -1,5 +1,5 @@ -module.exports = { - run (options) { +export = { + run (options: any) { // eslint-disable-next-line no-console console.log(options.pong)