Skip to content

Commit 33277c1

Browse files
chore: Share error utils with the cloud (#31887)
* share error utils with cloud * additional rework * Fix command, add isOpenMode * Add / fix test * fix ts --------- Co-authored-by: Ryan Manuel <[email protected]>
1 parent 04e8212 commit 33277c1

File tree

11 files changed

+149
-43
lines changed

11 files changed

+149
-43
lines changed

packages/driver/cypress/e2e/cypress/error_utils.cy.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,27 @@ describe('driver/src/cypress/error_utils', () => {
624624
})
625625
})
626626

627+
context('.extendErrorMessages', () => {
628+
it('extends error messages', () => {
629+
$errUtils.extendErrorMessages({
630+
testErrors: {
631+
test: 'test error message',
632+
},
633+
})
634+
635+
const fn = () => {
636+
$errUtils.throwErrByPath('testErrors.test')
637+
}
638+
639+
expect(fn).to.throw().and.satisfy((err) => {
640+
expect(err.message).to.equal('test error message')
641+
expect(err.name).to.eq('CypressError')
642+
643+
return true
644+
})
645+
})
646+
})
647+
627648
context('.getUnsupportedPlugin', () => {
628649
it('returns unsupported plugin if the error msg is the expected one', () => {
629650
const unsupportedPlugin = $errUtils.getUnsupportedPlugin({

packages/driver/src/cy/commands/prompt/index.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,21 +79,26 @@ const initializeCloudCyPrompt = async (Cypress: Cypress.Cypress, cy: Cypress.Cyp
7979
Cypress: Cypress as CypressInternal,
8080
cy,
8181
eventManager: window.getEventManager ? window.getEventManager() : undefined,
82+
errorUtils: {
83+
extendErrorMessages: $errUtils.extendErrorMessages,
84+
throwErrByPath: $errUtils.throwErrByPath,
85+
},
8286
})
8387
} catch (error) {
8488
return error
8589
}
8690
}
8791

88-
export default (Commands, Cypress, cy) => {
92+
export default (Commands: Cypress.Cypress['Commands'], Cypress: Cypress.Cypress, cy: Cypress.Cypress['cy']) => {
93+
// @ts-expect-error - these types are not yet implemented until the prompt command is rolled out
8994
if (Cypress.config('experimentalPromptCommand')) {
9095
let initializeCloudCyPromptPromise: Promise<ReturnType<CyPromptDriverDefaultShape['createCyPrompt']> | Error> | undefined
9196

9297
if (Cypress.browser.family === 'chromium' || Cypress.browser.name === 'electron') {
9398
initializeCloudCyPromptPromise = initializeCloudCyPrompt(Cypress, cy)
9499
}
95100

96-
const prompt = async (message: string, options: object = {}) => {
101+
const prompt = (message: string, options: object = {}) => {
97102
if (Cypress.testingType === 'component') {
98103
$errUtils.throwErrByPath('prompt.promptTestingTypeError')
99104

@@ -108,22 +113,16 @@ export default (Commands, Cypress, cy) => {
108113
return
109114
}
110115

111-
try {
112-
const bundleResult = await initializeCloudCyPromptPromise
113-
116+
// TODO: figure out how to handle timeout more generally
117+
return cy.wrap(initializeCloudCyPromptPromise, { log: false, timeout: 45000 }).then((bundleResult: ReturnType<CyPromptDriverDefaultShape['createCyPrompt']> | Error) => {
114118
if (bundleResult instanceof Error) {
115119
throw bundleResult
116120
}
117121

118122
const cyPrompt = bundleResult
119123

120-
return await cyPrompt(message, options)
121-
} catch (error) {
122-
// TODO: Check error that the user is logged in / record key
123-
124-
// TODO: handle this better
125-
throw new Error(`CyPromptDriver not found: ${error}`)
126-
}
124+
return cyPrompt(message, options)
125+
})
127126
}
128127

129128
// For testing purposes, we can reset the prompt command initialization

packages/driver/src/cy/commands/prompt/prompt-driver-types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ export interface CyPromptOptions {
4646
// Note that the eventManager is present in same origin AUTs, but not cross origin
4747
// so we need to check for it's presence before using it
4848
eventManager?: CyPromptEventManager
49+
errorUtils: {
50+
extendErrorMessages: (errorMessages: any) => void
51+
throwErrByPath: (err: any, path: string) => void
52+
}
4953
}
5054

5155
export interface CyPromptDriverDefaultShape {

packages/driver/src/cypress/error_utils.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const ERR_PREPARED_FOR_SERIALIZATION = Symbol('ERR_PREPARED_FOR_SERIALIZATION')
2222

2323
const crossOriginScriptRe = /^script error/i
2424

25+
let allErrorMessages = $errorMessages
26+
2527
if (!Error.captureStackTrace) {
2628
Error.captureStackTrace = (err, fn) => {
2729
const stack = (new Error()).stack
@@ -396,7 +398,7 @@ const docsUrlByParents = (msgPath) => {
396398
return // reached root
397399
}
398400

399-
const obj = _.get($errorMessages, msgPath)
401+
const obj = _.get(allErrorMessages, msgPath)
400402

401403
if (obj.hasOwnProperty('docsUrl')) {
402404
return obj.docsUrl
@@ -406,7 +408,7 @@ const docsUrlByParents = (msgPath) => {
406408
}
407409

408410
const errByPath = (msgPath, args?) => {
409-
let msgValue = _.get($errorMessages, msgPath)
411+
let msgValue = _.get(allErrorMessages, msgPath)
410412

411413
if (!msgValue) {
412414
return internalErr({ message: `Error message path '${msgPath}' does not exist` })
@@ -655,6 +657,13 @@ const getUnsupportedPlugin = (runnable) => {
655657
return null
656658
}
657659

660+
const extendErrorMessages = (errorMessages: any) => {
661+
allErrorMessages = {
662+
...allErrorMessages,
663+
...errorMessages,
664+
}
665+
}
666+
658667
export default {
659668
stackWithReplacedProps,
660669
appendErrMsg,
@@ -679,4 +688,5 @@ export default {
679688
throwErrByPath,
680689
warnByPath,
681690
wrapErr,
691+
extendErrorMessages,
682692
}

packages/server/lib/cloud/cy-prompt/CyPromptLifecycleManager.ts

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { readFile } from 'fs-extra'
1212
import { ensureCyPromptBundle } from './ensure_cy_prompt_bundle'
1313
import chokidar from 'chokidar'
1414
import { getCloudMetadata } from '../get_cloud_metadata'
15+
import type { CyPromptAuthenticatedUserShape } from '@packages/types'
1516

1617
const debug = Debug('cypress:server:cy-prompt-lifecycle-manager')
1718

@@ -33,22 +34,34 @@ export class CyPromptLifecycleManager {
3334
* @param ctx Data context to register this instance with
3435
*/
3536
initializeCyPromptManager ({
36-
projectId,
3737
cloudDataSource,
3838
ctx,
39+
record,
40+
key,
3941
}: {
40-
projectId?: string
4142
cloudDataSource: CloudDataSource
4243
ctx: DataContext
44+
record?: boolean
45+
key?: string
4346
}): void {
4447
// Register this instance in the data context
4548
ctx.update((data) => {
4649
data.cyPromptLifecycleManager = this
4750
})
4851

52+
const getProjectOptions = async () => {
53+
return {
54+
user: await ctx.actions.auth.authApi.getUser(),
55+
projectSlug: (await ctx.project.getConfig()).projectId || undefined,
56+
record,
57+
key,
58+
isOpenMode: ctx.isOpenMode,
59+
}
60+
}
61+
4962
const cyPromptManagerPromise = this.createCyPromptManager({
50-
projectId,
5163
cloudDataSource,
64+
getProjectOptions,
5265
}).catch(async (error) => {
5366
debug('Error during cy prompt manager setup: %o', error)
5467

@@ -81,8 +94,8 @@ export class CyPromptLifecycleManager {
8194
this.cyPromptManagerPromise = cyPromptManagerPromise
8295

8396
this.setupWatcher({
84-
projectId,
8597
cloudDataSource,
98+
getProjectOptions,
8699
})
87100
}
88101

@@ -97,17 +110,25 @@ export class CyPromptLifecycleManager {
97110
}
98111

99112
private async createCyPromptManager ({
100-
projectId,
101113
cloudDataSource,
114+
getProjectOptions,
102115
}: {
103116
projectId?: string
104117
cloudDataSource: CloudDataSource
118+
getProjectOptions: () => Promise<{
119+
user?: CyPromptAuthenticatedUserShape
120+
projectSlug?: string
121+
record?: boolean
122+
key?: string
123+
}>
105124
}): Promise<{ cyPromptManager?: CyPromptManager, error?: Error }> {
106125
let cyPromptHash: string
107126
let cyPromptPath: string
108127

128+
const currentProjectOptions = await getProjectOptions()
129+
const projectId = currentProjectOptions.projectSlug
109130
const cyPromptSession = await postCyPromptSession({
110-
projectId,
131+
projectId: currentProjectOptions.projectSlug,
111132
})
112133

113134
if (!process.env.CYPRESS_LOCAL_CY_PROMPT_PATH) {
@@ -138,20 +159,19 @@ export class CyPromptLifecycleManager {
138159
const script = await readFile(serverFilePath, 'utf8')
139160
const cyPromptManager = new CyPromptManager()
140161

141-
const { cloudUrl, cloudHeaders } = await getCloudMetadata(cloudDataSource)
162+
const { cloudUrl } = await getCloudMetadata(cloudDataSource)
142163

143164
await cyPromptManager.setup({
144165
script,
145166
cyPromptPath,
146167
cyPromptHash,
147-
projectSlug: projectId,
148168
cloudApi: {
149169
cloudUrl,
150-
cloudHeaders,
151170
CloudRequest,
152171
isRetryableError,
153172
asyncRetry,
154173
},
174+
getProjectOptions,
155175
})
156176

157177
debug('cy prompt is ready')
@@ -181,9 +201,16 @@ export class CyPromptLifecycleManager {
181201
private setupWatcher ({
182202
projectId,
183203
cloudDataSource,
204+
getProjectOptions,
184205
}: {
185206
projectId?: string
186207
cloudDataSource: CloudDataSource
208+
getProjectOptions: () => Promise<{
209+
user?: CyPromptAuthenticatedUserShape
210+
projectSlug?: string
211+
record?: boolean
212+
key?: string
213+
}>
187214
}) {
188215
// Don't setup a watcher if the cy prompt bundle is NOT local
189216
if (!process.env.CYPRESS_LOCAL_CY_PROMPT_PATH) {
@@ -204,6 +231,7 @@ export class CyPromptLifecycleManager {
204231
this.cyPromptManagerPromise = this.createCyPromptManager({
205232
projectId,
206233
cloudDataSource,
234+
getProjectOptions,
207235
}).catch((error) => {
208236
debug('Error during reload of cy prompt manager: %o', error)
209237

packages/server/lib/cloud/cy-prompt/CyPromptManager.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CyPromptManagerShape, CyPromptStatus, CyPromptServerDefaultShape, CyPromptServerShape, CyPromptCloudApi, CyPromptCDPClient } from '@packages/types'
1+
import type { CyPromptManagerShape, CyPromptStatus, CyPromptServerDefaultShape, CyPromptServerShape, CyPromptCloudApi, CyPromptCDPClient, CyPromptAuthenticatedUserShape } from '@packages/types'
22
import type { Router } from 'express'
33
import Debug from 'debug'
44
import { requireScript } from '../require_script'
@@ -12,6 +12,12 @@ interface SetupOptions {
1212
cyPromptHash?: string
1313
projectSlug?: string
1414
cloudApi: CyPromptCloudApi
15+
getProjectOptions: () => Promise<{
16+
user?: CyPromptAuthenticatedUserShape
17+
projectSlug?: string
18+
record?: boolean
19+
key?: string
20+
}>
1521
}
1622

1723
const debug = Debug('cypress:server:cy-prompt')
@@ -20,14 +26,14 @@ export class CyPromptManager implements CyPromptManagerShape {
2026
status: CyPromptStatus = 'NOT_INITIALIZED'
2127
private _cyPromptServer: CyPromptServerShape | undefined
2228

23-
async setup ({ script, cyPromptPath, cyPromptHash, projectSlug, cloudApi }: SetupOptions): Promise<void> {
29+
async setup ({ script, cyPromptPath, cyPromptHash, getProjectOptions, cloudApi }: SetupOptions): Promise<void> {
2430
const { createCyPromptServer } = requireScript<CyPromptServer>(script).default
2531

2632
this._cyPromptServer = await createCyPromptServer({
2733
cyPromptHash,
2834
cyPromptPath,
29-
projectSlug,
3035
cloudApi,
36+
getProjectOptions,
3137
})
3238

3339
this.status = 'INITIALIZED'

packages/server/lib/modes/run.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ const openProjectCreate = (projectRoot, socketId, args) => {
160160
onWarning,
161161
spec: args.spec,
162162
onError: args.onError,
163+
record: args.record,
164+
key: args.key,
163165
}
164166

165167
return openProject.create(projectRoot, args, options)

packages/server/lib/project-base.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,10 @@ export class ProjectBase extends EE {
166166
const cyPromptLifecycleManager = new CyPromptLifecycleManager()
167167

168168
cyPromptLifecycleManager.initializeCyPromptManager({
169-
projectId: cfg.projectId,
170169
cloudDataSource: this.ctx.cloud,
171170
ctx: this.ctx,
171+
record: this.options.record,
172+
key: this.options.key,
172173
})
173174
}
174175

0 commit comments

Comments
 (0)