| // Copyright 2026 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import * as SDK from '../../core/sdk/sdk.js'; |
| import * as Protocol from '../../generated/protocol.js'; |
| import {setupRuntimeHooks} from '../../testing/RuntimeHelpers.js'; |
| import {setupSettingsHooks} from '../../testing/SettingsHelpers.js'; |
| import {TestUniverse} from '../../testing/TestUniverse.js'; |
| import * as StackTrace from '../stack_trace/stack_trace.js'; |
| import type * as Workspace from '../workspace/workspace.js'; |
| |
| import * as Bindings from './bindings.js'; |
| |
| describe('SymbolizedError', () => { |
| setupRuntimeHooks(); |
| setupSettingsHooks(); |
| |
| let universe: TestUniverse; |
| |
| beforeEach(() => { |
| universe = new TestUniverse(); |
| }); |
| |
| async function createSymbolizedErrorWithCause() { |
| const target = universe.createTarget({}); |
| const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); |
| assert.exists(runtimeModel); |
| |
| const errorStack = 'Error: some error\n at http://example.com/script.js:1:1'; |
| const causeStack = 'Error: cause error\n at http://example.com/script.js:2:2'; |
| |
| const causeRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| description: causeStack, |
| }); |
| |
| const errorRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| description: errorStack, |
| objectId: '1' as Protocol.Runtime.RemoteObjectId, |
| }); |
| |
| sinon.stub(errorRemoteObject, 'getAllProperties').resolves({ |
| properties: [new SDK.RemoteObject.RemoteObjectProperty('cause', causeRemoteObject)], |
| internalProperties: [], |
| }); |
| |
| return await universe.debuggerWorkspaceBinding.createSymbolizedError(errorRemoteObject); |
| } |
| |
| it('can create a SymbolizedError from a RemoteObject', async () => { |
| const symbolizedError = await createSymbolizedErrorWithCause(); |
| |
| assert.instanceOf(symbolizedError, Bindings.SymbolizedError.SymbolizedErrorObject); |
| assert.strictEqual(symbolizedError.message, 'Error: some error'); |
| assert.strictEqual(symbolizedError.stackTrace.syncFragment.frames[0].url, 'http://example.com/script.js'); |
| |
| const cause = symbolizedError.cause; |
| assert.instanceOf(cause, Bindings.SymbolizedError.SymbolizedErrorObject); |
| assert.strictEqual(cause.message, 'Error: cause error'); |
| assert.strictEqual(cause.stackTrace.syncFragment.frames[0].url, 'http://example.com/script.js'); |
| assert.strictEqual(cause.stackTrace.syncFragment.frames[0].line, 1); // 0-based in frames |
| }); |
| |
| it('returns null if the RemoteObject is not an error', async () => { |
| const target = universe.createTarget({}); |
| const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); |
| assert.exists(runtimeModel); |
| |
| const nonErrorRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Null, |
| }); |
| |
| const result = await universe.debuggerWorkspaceBinding.createSymbolizedError(nonErrorRemoteObject); |
| assert.isNull(result); |
| }); |
| |
| it('returns an UnparsableError if the error stack cannot be parsed', async () => { |
| const target = universe.createTarget({}); |
| const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); |
| assert.exists(runtimeModel); |
| |
| const errorRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| description: 'Error: message\n at http://example.com/script.js:1:1\ninvalid line', |
| objectId: '1' as Protocol.Runtime.RemoteObjectId, |
| }); |
| |
| sinon.stub(errorRemoteObject, 'getAllProperties').resolves({ |
| properties: [], |
| internalProperties: [], |
| }); |
| |
| const result = await universe.debuggerWorkspaceBinding.createSymbolizedError(errorRemoteObject); |
| assert.instanceOf(result, Bindings.SymbolizedError.UnparsableError); |
| assert.strictEqual(result.errorStack, errorRemoteObject.description); |
| assert.isNull(result.cause); |
| }); |
| |
| it('can create an UnparsableError with a parsable cause', async () => { |
| const target = universe.createTarget({}); |
| const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); |
| assert.exists(runtimeModel); |
| |
| const causeStack = 'Error: cause error\n at http://example.com/script.js:2:2'; |
| const causeRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| description: causeStack, |
| }); |
| |
| const errorRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| description: 'Error: message\n at http://example.com/script.js:1:1\ninvalid line', |
| objectId: '1' as Protocol.Runtime.RemoteObjectId, |
| }); |
| |
| sinon.stub(errorRemoteObject, 'getAllProperties').resolves({ |
| properties: [new SDK.RemoteObject.RemoteObjectProperty('cause', causeRemoteObject)], |
| internalProperties: [], |
| }); |
| |
| const result = await universe.debuggerWorkspaceBinding.createSymbolizedError(errorRemoteObject); |
| assert.instanceOf(result, Bindings.SymbolizedError.UnparsableError); |
| assert.strictEqual(result.errorStack, errorRemoteObject.description); |
| assert.instanceOf(result.cause, Bindings.SymbolizedError.SymbolizedErrorObject); |
| assert.strictEqual(result.cause.message, 'Error: cause error'); |
| assert.strictEqual(result.cause.stackTrace.syncFragment.frames[0].url, 'http://example.com/script.js'); |
| }); |
| |
| it('can create a SymbolizedError from a string RemoteObject', async () => { |
| const target = universe.createTarget({}); |
| const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); |
| assert.exists(runtimeModel); |
| |
| const stringRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.String, |
| value: 'Error: string error\n at http://example.com/script.js:1:1', |
| description: 'Error: string error\n at http://example.com/script.js:1:1', |
| }); |
| |
| const symbolizedError = await universe.debuggerWorkspaceBinding.createSymbolizedError(stringRemoteObject); |
| |
| assert.instanceOf(symbolizedError, Bindings.SymbolizedError.SymbolizedErrorObject); |
| assert.strictEqual(symbolizedError.message, 'Error: string error'); |
| assert.strictEqual(symbolizedError.stackTrace.syncFragment.frames[0].url, 'http://example.com/script.js'); |
| assert.isNull(symbolizedError.cause); |
| }); |
| |
| it('can create a SymbolizedSyntaxError from a SyntaxError RemoteObject', async () => { |
| const target = universe.createTarget({}); |
| const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); |
| assert.exists(runtimeModel); |
| const debuggerModel = target.model(SDK.DebuggerModel.DebuggerModel); |
| assert.exists(debuggerModel); |
| |
| const scriptId = '1' as Protocol.Runtime.ScriptId; |
| const exceptionDetails = { |
| exception: { |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| className: 'SyntaxError', |
| description: 'SyntaxError: Unexpected token', |
| }, |
| scriptId, |
| lineNumber: 1, |
| columnNumber: 1, |
| } as Protocol.Runtime.ExceptionDetails; |
| |
| const errorRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| className: 'SyntaxError', |
| objectId: '1' as Protocol.Runtime.RemoteObjectId, |
| }); |
| |
| sinon.stub(errorRemoteObject, 'getAllProperties').resolves({properties: [], internalProperties: []}); |
| |
| sinon.stub(debuggerModel, 'scriptForId').withArgs(scriptId).returns({} as SDK.Script.Script); |
| |
| const uiLocation = {} as Workspace.UISourceCode.UILocation; |
| const liveLocation = { |
| uiLocation: async () => uiLocation, |
| dispose: () => {}, |
| } as Bindings.LiveLocation.LiveLocation; |
| |
| sinon.stub(universe.debuggerWorkspaceBinding, 'createLiveLocation') |
| .callsFake(async (_rawLocation, updateDelegate, _pool) => { |
| await updateDelegate(liveLocation); |
| return liveLocation as unknown as Bindings.DebuggerWorkspaceBinding.Location; |
| }); |
| |
| const symbolizedError = |
| await universe.debuggerWorkspaceBinding.createSymbolizedError(errorRemoteObject, exceptionDetails); |
| |
| assert.instanceOf(symbolizedError, Bindings.SymbolizedError.SymbolizedSyntaxError); |
| assert.strictEqual(symbolizedError.message, 'SyntaxError: Unexpected token'); |
| }); |
| |
| it('returns null for a basic string RemoteObject', async () => { |
| const target = universe.createTarget({}); |
| const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); |
| assert.exists(runtimeModel); |
| |
| const stringRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.String, |
| value: 'just a regular string', |
| description: 'just a regular string', |
| }); |
| |
| const result = await universe.debuggerWorkspaceBinding.createSymbolizedError(stringRemoteObject); |
| assert.isNull(result); |
| }); |
| |
| it('returns an UnparsableError for a string RemoteObject if the stack trace cannot be parsed', async () => { |
| const target = universe.createTarget({}); |
| const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); |
| assert.exists(runtimeModel); |
| |
| const description = 'Error: string error\n at http://example.com/script.js:1:1\ninvalid line'; |
| const stringRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.String, |
| value: description, |
| description, |
| }); |
| |
| const result = await universe.debuggerWorkspaceBinding.createSymbolizedError(stringRemoteObject); |
| assert.instanceOf(result, Bindings.SymbolizedError.UnparsableError); |
| assert.strictEqual(result.errorStack, stringRemoteObject.description); |
| assert.isNull(result.cause); |
| }); |
| |
| it('uses the provided exceptionDetails preferentially', async () => { |
| const target = universe.createTarget({}); |
| const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); |
| assert.exists(runtimeModel); |
| |
| const errorRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| description: 'Error: error\n at http://example.com/script.js:1:1', |
| objectId: '1' as Protocol.Runtime.RemoteObjectId, |
| }); |
| |
| sinon.stub(errorRemoteObject, 'getAllProperties').resolves({properties: [], internalProperties: []}); |
| |
| const exceptionDetails = { |
| exceptionId: 1, |
| text: 'Uncaught', |
| lineNumber: 0, |
| columnNumber: 0, |
| } as Protocol.Runtime.ExceptionDetails; |
| |
| const invokeGetExceptionDetailsSpy = sinon.spy(target.runtimeAgent(), 'invoke_getExceptionDetails'); |
| |
| const symbolizedError = |
| await universe.debuggerWorkspaceBinding.createSymbolizedError(errorRemoteObject, exceptionDetails); |
| |
| assert.instanceOf(symbolizedError, Bindings.SymbolizedError.SymbolizedErrorObject); |
| assert.strictEqual(symbolizedError.message, 'Error: error'); |
| sinon.assert.notCalled(invokeGetExceptionDetailsSpy); |
| }); |
| |
| it('includes issueSummary in the message if provided in exceptionDetails', async () => { |
| const target = universe.createTarget({}); |
| const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); |
| assert.exists(runtimeModel); |
| |
| const errorRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| description: 'Error: error\n at http://example.com/script.js:1:1', |
| objectId: '1' as Protocol.Runtime.RemoteObjectId, |
| }); |
| |
| sinon.stub(errorRemoteObject, 'getAllProperties').resolves({properties: [], internalProperties: []}); |
| |
| const exceptionDetails = { |
| exceptionId: 1, |
| text: 'Uncaught', |
| lineNumber: 0, |
| columnNumber: 0, |
| exceptionMetaData: { |
| issueSummary: 'This is an issue summary', |
| }, |
| } as Protocol.Runtime.ExceptionDetails; |
| |
| const symbolizedError = |
| await universe.debuggerWorkspaceBinding.createSymbolizedError(errorRemoteObject, exceptionDetails); |
| |
| assert.instanceOf(symbolizedError, Bindings.SymbolizedError.SymbolizedErrorObject); |
| assert.strictEqual(symbolizedError.message, 'Error: error. This is an issue summary'); |
| assert.strictEqual(symbolizedError.stackTrace.syncFragment.frames[0].url, 'http://example.com/script.js'); |
| assert.strictEqual(symbolizedError.stackTrace.syncFragment.frames[0].line, 0); |
| }); |
| |
| it('includes issueSummary in the errorStack for an UnparsableError', async () => { |
| const target = universe.createTarget({}); |
| const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); |
| assert.exists(runtimeModel); |
| |
| const errorRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| description: 'Error: message\n at http://example.com/script.js:1:1\ninvalid line', |
| objectId: '1' as Protocol.Runtime.RemoteObjectId, |
| }); |
| |
| sinon.stub(errorRemoteObject, 'getAllProperties').resolves({properties: [], internalProperties: []}); |
| |
| const exceptionDetails = { |
| exceptionId: 1, |
| text: 'Uncaught', |
| lineNumber: 0, |
| columnNumber: 0, |
| exceptionMetaData: { |
| issueSummary: 'This is an issue summary', |
| }, |
| } as Protocol.Runtime.ExceptionDetails; |
| |
| const symbolizedError = |
| await universe.debuggerWorkspaceBinding.createSymbolizedError(errorRemoteObject, exceptionDetails); |
| |
| assert.instanceOf(symbolizedError, Bindings.SymbolizedError.UnparsableError); |
| assert.strictEqual( |
| symbolizedError.errorStack, |
| 'Error: message. This is an issue summary\n at http://example.com/script.js:1:1\ninvalid line'); |
| }); |
| |
| it('emits UPDATED when stackTrace or cause updates', async () => { |
| const symbolizedError = await createSymbolizedErrorWithCause(); |
| assert.instanceOf(symbolizedError, Bindings.SymbolizedError.SymbolizedErrorObject); |
| |
| const listener = sinon.stub(); |
| symbolizedError.addEventListener(Bindings.SymbolizedError.Events.UPDATED, listener); |
| |
| // Trigger update on the main error's stackTrace |
| symbolizedError.stackTrace.dispatchEventToListeners(StackTrace.StackTrace.Events.UPDATED); |
| sinon.assert.callCount(listener, 1); |
| |
| // Trigger update on the cause error's stackTrace |
| const cause = symbolizedError.cause; |
| assert.instanceOf(cause, Bindings.SymbolizedError.SymbolizedErrorObject); |
| cause.stackTrace.dispatchEventToListeners(StackTrace.StackTrace.Events.UPDATED); |
| sinon.assert.callCount(listener, 2); |
| |
| // Trigger update on the cause error directly |
| symbolizedError.cause?.dispatchEventToListeners(Bindings.SymbolizedError.Events.UPDATED); |
| sinon.assert.callCount(listener, 3); |
| }); |
| |
| it('removes listeners when dispose is called', async () => { |
| const symbolizedError = await createSymbolizedErrorWithCause(); |
| assert.instanceOf(symbolizedError, Bindings.SymbolizedError.SymbolizedErrorObject); |
| |
| const listener = sinon.stub(); |
| symbolizedError.addEventListener(Bindings.SymbolizedError.Events.UPDATED, listener); |
| |
| symbolizedError.dispose(); |
| |
| // Trigger update on the main error's stackTrace |
| symbolizedError.stackTrace.dispatchEventToListeners(StackTrace.StackTrace.Events.UPDATED); |
| sinon.assert.notCalled(listener); |
| |
| // Trigger update on the cause error's stackTrace |
| const cause = symbolizedError.cause; |
| assert.instanceOf(cause, Bindings.SymbolizedError.SymbolizedErrorObject); |
| cause.stackTrace.dispatchEventToListeners(StackTrace.StackTrace.Events.UPDATED); |
| sinon.assert.notCalled(listener); |
| |
| // Trigger update on the cause error directly |
| symbolizedError.cause?.dispatchEventToListeners(Bindings.SymbolizedError.Events.UPDATED); |
| sinon.assert.notCalled(listener); |
| }); |
| |
| it('UnparsableError emits UPDATED when its cause updates', async () => { |
| const target = universe.createTarget({}); |
| const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); |
| assert.exists(runtimeModel); |
| |
| const causeStack = 'Error: cause error\n at http://example.com/script.js:2:2'; |
| const causeRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| description: causeStack, |
| }); |
| |
| const errorRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| description: 'Error: message\n at http://example.com/script.js:1:1\ninvalid line', |
| objectId: '1' as Protocol.Runtime.RemoteObjectId, |
| }); |
| |
| sinon.stub(errorRemoteObject, 'getAllProperties').resolves({ |
| properties: [new SDK.RemoteObject.RemoteObjectProperty('cause', causeRemoteObject)], |
| internalProperties: [], |
| }); |
| |
| const symbolizedError = await universe.debuggerWorkspaceBinding.createSymbolizedError(errorRemoteObject); |
| assert.instanceOf(symbolizedError, Bindings.SymbolizedError.UnparsableError); |
| |
| const listener = sinon.stub(); |
| symbolizedError.addEventListener(Bindings.SymbolizedError.Events.UPDATED, listener); |
| |
| // Trigger update on the cause error's stackTrace |
| const cause = symbolizedError.cause; |
| assert.instanceOf(cause, Bindings.SymbolizedError.SymbolizedErrorObject); |
| cause.stackTrace.dispatchEventToListeners(StackTrace.StackTrace.Events.UPDATED); |
| sinon.assert.callCount(listener, 1); |
| |
| // Trigger update on the cause error directly |
| symbolizedError.cause?.dispatchEventToListeners(Bindings.SymbolizedError.Events.UPDATED); |
| sinon.assert.callCount(listener, 2); |
| }); |
| |
| it('UnparsableError removes listeners when dispose is called', async () => { |
| const target = universe.createTarget({}); |
| const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); |
| assert.exists(runtimeModel); |
| |
| const causeStack = 'Error: cause error\n at http://example.com/script.js:2:2'; |
| const causeRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| description: causeStack, |
| }); |
| |
| const errorRemoteObject = runtimeModel.createRemoteObject({ |
| type: Protocol.Runtime.RemoteObjectType.Object, |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| description: 'Error: message\n at http://example.com/script.js:1:1\ninvalid line', |
| objectId: '1' as Protocol.Runtime.RemoteObjectId, |
| }); |
| |
| sinon.stub(errorRemoteObject, 'getAllProperties').resolves({ |
| properties: [new SDK.RemoteObject.RemoteObjectProperty('cause', causeRemoteObject)], |
| internalProperties: [], |
| }); |
| |
| const symbolizedError = await universe.debuggerWorkspaceBinding.createSymbolizedError(errorRemoteObject); |
| assert.instanceOf(symbolizedError, Bindings.SymbolizedError.UnparsableError); |
| |
| const listener = sinon.stub(); |
| symbolizedError.addEventListener(Bindings.SymbolizedError.Events.UPDATED, listener); |
| |
| symbolizedError.dispose(); |
| |
| // Trigger update on the cause error's stackTrace |
| const cause = symbolizedError.cause; |
| assert.instanceOf(cause, Bindings.SymbolizedError.SymbolizedErrorObject); |
| cause.stackTrace.dispatchEventToListeners(StackTrace.StackTrace.Events.UPDATED); |
| sinon.assert.notCalled(listener); |
| |
| // Trigger update on the cause error directly |
| symbolizedError.cause?.dispatchEventToListeners(Bindings.SymbolizedError.Events.UPDATED); |
| sinon.assert.notCalled(listener); |
| }); |
| |
| describe('SymbolizedSyntaxError', () => { |
| it('can create a SymbolizedSyntaxError from exception details', async () => { |
| const target = universe.createTarget({}); |
| const debuggerModel = target.model(SDK.DebuggerModel.DebuggerModel); |
| assert.exists(debuggerModel); |
| |
| const scriptId = '1' as Protocol.Runtime.ScriptId; |
| const exceptionDetails = { |
| exception: { |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| className: 'SyntaxError', |
| description: 'SyntaxError: Unexpected token', |
| }, |
| scriptId, |
| lineNumber: 1, |
| columnNumber: 1, |
| } as Protocol.Runtime.ExceptionDetails; |
| |
| sinon.stub(debuggerModel, 'scriptForId').withArgs(scriptId).returns({} as SDK.Script.Script); |
| |
| const uiLocation = {} as Workspace.UISourceCode.UILocation; |
| const liveLocation = { |
| uiLocation: async () => uiLocation, |
| dispose: () => {}, |
| } as Bindings.LiveLocation.LiveLocation; |
| |
| const createLiveLocationStub = sinon.stub(universe.debuggerWorkspaceBinding, 'createLiveLocation') |
| .callsFake(async (_rawLocation, updateDelegate, _pool) => { |
| await updateDelegate(liveLocation); |
| return liveLocation as unknown as Bindings.DebuggerWorkspaceBinding.Location; |
| }); |
| |
| const symbolizedError = await Bindings.SymbolizedError.SymbolizedSyntaxError.fromExceptionDetails( |
| target, universe.debuggerWorkspaceBinding, exceptionDetails); |
| |
| assert.instanceOf(symbolizedError, Bindings.SymbolizedError.SymbolizedSyntaxError); |
| assert.strictEqual(symbolizedError.message, 'SyntaxError: Unexpected token'); |
| assert.strictEqual(symbolizedError.uiLocation, uiLocation); |
| |
| sinon.assert.calledOnce(createLiveLocationStub); |
| }); |
| |
| it('throws if the exception is not a SyntaxError', async () => { |
| const target = universe.createTarget({}); |
| const exceptionDetails = { |
| exception: { |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| className: 'TypeError', |
| }, |
| } as Protocol.Runtime.ExceptionDetails; |
| |
| let error: Error|null = null; |
| try { |
| await Bindings.SymbolizedError.SymbolizedSyntaxError.fromExceptionDetails( |
| target, universe.debuggerWorkspaceBinding, exceptionDetails); |
| } catch (e) { |
| error = e as Error; |
| } |
| assert.exists(error); |
| assert.strictEqual(error?.message, 'SymbolizedSyntaxError.fromExceptionDetails expects a SyntaxError'); |
| }); |
| |
| it('returns null if scriptId is missing', async () => { |
| const target = universe.createTarget({}); |
| const exceptionDetails = { |
| exception: { |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| className: 'SyntaxError', |
| }, |
| } as Protocol.Runtime.ExceptionDetails; |
| |
| const result = await Bindings.SymbolizedError.SymbolizedSyntaxError.fromExceptionDetails( |
| target, universe.debuggerWorkspaceBinding, exceptionDetails); |
| assert.isNull(result); |
| }); |
| |
| it('emits UPDATED when the live location updates', async () => { |
| const target = universe.createTarget({}); |
| const debuggerModel = target.model(SDK.DebuggerModel.DebuggerModel); |
| assert.exists(debuggerModel); |
| |
| const scriptId = '1' as Protocol.Runtime.ScriptId; |
| const exceptionDetails = { |
| exception: { |
| subtype: Protocol.Runtime.RemoteObjectSubtype.Error, |
| className: 'SyntaxError', |
| description: 'SyntaxError: Unexpected token', |
| }, |
| scriptId, |
| lineNumber: 1, |
| columnNumber: 1, |
| } as Protocol.Runtime.ExceptionDetails; |
| |
| sinon.stub(debuggerModel, 'scriptForId').withArgs(scriptId).returns({} as SDK.Script.Script); |
| |
| let updateDelegateCallback: ((liveLocation: Bindings.LiveLocation.LiveLocation) => Promise<void>)|null = null; |
| const liveLocation = { |
| uiLocation: sinon.stub(), |
| dispose: () => {}, |
| } as unknown as Bindings.LiveLocation.LiveLocation; |
| |
| sinon.stub(universe.debuggerWorkspaceBinding, 'createLiveLocation') |
| .callsFake(async (_rawLocation, updateDelegate, _pool) => { |
| updateDelegateCallback = updateDelegate; |
| await updateDelegate(liveLocation); |
| return liveLocation as unknown as Bindings.DebuggerWorkspaceBinding.Location; |
| }); |
| |
| const symbolizedError = await Bindings.SymbolizedError.SymbolizedSyntaxError.fromExceptionDetails( |
| target, universe.debuggerWorkspaceBinding, exceptionDetails); |
| assert.instanceOf(symbolizedError, Bindings.SymbolizedError.SymbolizedSyntaxError); |
| |
| const updatedListener = sinon.stub(); |
| symbolizedError.addEventListener(Bindings.SymbolizedError.Events.UPDATED, updatedListener); |
| |
| assert.exists(updateDelegateCallback); |
| await (updateDelegateCallback as (liveLocation: Bindings.LiveLocation.LiveLocation) => |
| Promise<void>)(liveLocation); |
| |
| sinon.assert.calledOnce(updatedListener); |
| }); |
| }); |
| }); |