Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/server/lib/cloud/api/studio/report_studio_error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ export function reportStudioError ({
let errorObject: Error

if (!(error instanceof Error)) {
errorObject = new Error(String(error))
// For strings, use them directly. For objects, serialize them.
const message = typeof error === 'string' ? error : JSON.stringify(error)

errorObject = new Error(message)
} else {
errorObject = error
}
Expand Down
10 changes: 8 additions & 2 deletions packages/server/lib/cloud/studio/studio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,10 @@ export class StudioManager implements StudioManagerShape {
let actualError: Error

if (!(error instanceof Error)) {
actualError = new Error(String(error))
// For strings, use them directly. For objects, serialize them.
const message = typeof error === 'string' ? error : JSON.stringify(error)

actualError = new Error(message)
} else {
actualError = error
}
Expand Down Expand Up @@ -154,7 +157,10 @@ export class StudioManager implements StudioManagerShape {
let actualError: Error

if (!(error instanceof Error)) {
actualError = new Error(String(error))
// For strings, use them directly. For objects, serialize them.
const message = typeof error === 'string' ? error : JSON.stringify(error)

actualError = new Error(message)
} else {
actualError = error
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,45 @@ describe('lib/cloud/api/studio/report_studio_error', () => {
)
})

it('serializes object errors properly instead of showing [object Object]', () => {
const objectError = {
additionalData: { type: 'studio:panel:opened' },
message: 'Something went wrong',
code: 'TELEMETRY_ERROR',
}

reportStudioError({
cloudApi,
studioHash: 'abc123',
projectSlug: 'test-project',
error: objectError,
studioMethod: 'telemetryService',
})

expect(cloudRequestStub).to.be.calledWithMatch(
'http://localhost:1234/studio/errors',
{
studioHash: 'abc123',
projectSlug: 'test-project',
errors: [{
name: 'Error',
message: JSON.stringify(objectError),
stack: sinon.match((stack) => stack.includes('<stripped-path>report_studio_error_spec.ts')),
code: undefined,
errno: undefined,
studioMethod: 'telemetryService',
studioMethodArgs: undefined,
}],
},
{
headers: {
'Content-Type': 'application/json',
'x-cypress-version': '1.2.3',
},
},
)
})

it('handles Error objects correctly', () => {
const error = new Error('test error')

Expand Down
66 changes: 66 additions & 0 deletions packages/server/test/unit/cloud/studio/studio_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,28 @@ describe('lib/cloud/studio', () => {
expect(studioManager.status).to.eq('IN_ERROR')
expect(studio.reportError).to.be.calledWithMatch(error, 'initializeRoutes', {})
})

it('serializes object errors properly instead of showing [object Object]', () => {
const objectError = {
additionalData: { type: 'studio:panel:opened' },
message: 'Something went wrong',
}

sinon.stub(studio, 'initializeRoutes').throws(objectError)
sinon.stub(studio, 'reportError')

studioManager.initializeRoutes({} as any)

expect(studioManager.status).to.eq('IN_ERROR')
expect(studio.reportError).to.be.calledWithMatch(
sinon.match((error) => {
return error instanceof Error &&
error.message === JSON.stringify(objectError)
}),
'initializeRoutes',
{},
)
})
})

describe('asynchronous method invocation', () => {
Expand All @@ -92,6 +114,28 @@ describe('lib/cloud/studio', () => {
expect(studio.reportError).to.be.calledWithMatch(error, 'initializeStudioAI', {})
})

it('serializes object errors properly in async methods instead of showing [object Object]', async () => {
const objectError = {
additionalData: { type: 'studio:panel:opened' },
message: 'Async error occurred',
}

sinon.stub(studio, 'initializeStudioAI').throws(objectError)
sinon.stub(studio, 'reportError')

await studioManager.initializeStudioAI({} as any)

expect(studioManager.status).to.eq('IN_ERROR')
expect(studio.reportError).to.be.calledWithMatch(
sinon.match((error) => {
return error instanceof Error &&
error.message === JSON.stringify(objectError)
}),
'initializeStudioAI',
{},
)
})

it('does not set state IN_ERROR when a non-essential async method fails', async () => {
const error = new Error('foo')

Expand All @@ -101,6 +145,28 @@ describe('lib/cloud/studio', () => {

expect(studioManager.status).to.eq('ENABLED')
})

it('serializes object errors properly in non-essential async methods', async () => {
const objectError = {
additionalData: { type: 'studio:panel:opened' },
message: 'Non-essential error occurred',
}

sinon.stub(studio, 'captureStudioEvent').throws(objectError)
sinon.stub(studio, 'reportError')

await studioManager.captureStudioEvent({} as any)

expect(studioManager.status).to.eq('ENABLED')
expect(studio.reportError).to.be.calledWithMatch(
sinon.match((error) => {
return error instanceof Error &&
error.message === JSON.stringify(objectError)
}),
'captureStudioEvent',
{},
)
})
})

describe('initializeRoutes', () => {
Expand Down
Loading