Skip to content

Commit

Permalink
Script error: Browser exception message not providing information typ…
Browse files Browse the repository at this point in the history
…e and method #363
  • Loading branch information
MSNev committed May 7, 2021
1 parent 029d296 commit ab216a7
Show file tree
Hide file tree
Showing 51 changed files with 4,627 additions and 526 deletions.
4 changes: 2 additions & 2 deletions AISKU/Tests/TelemetryValidation/ExceptionValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ export class ExceptionValidator implements ITypeValidator {
// verify exceptions has typeName, message, hasFullStack, stack, parsedStack fields
if (!exceptions[0].typeName ||
!exceptions[0].message ||
!exceptions[0].hasFullStack ||
!("hasFullStack" in exceptions[0]) ||
!exceptions[0].stack ||
!exceptions[0].parsedStack) {
!("parsedStack" in exceptions[0])) {
return false;
}

Expand Down
6 changes: 3 additions & 3 deletions AISKU/Tests/TestFramework/PollingAssert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ export class PollingAssert {
Assert.ok(false, "assert didn't succeed for " + timeout + " seconds: " + assertDescription + "[" + (TestClass.currentTestInfo ? TestClass.currentTestInfo.name : "<null>") + "]");
nextTestStep();
} else {
setTimeout(polling, pollIntervalMs);
TestClass.orgSetTimeout(polling, pollIntervalMs);
}
} catch (e) {
Assert.ok(true, "Polling exception - " + e);
setTimeout(polling, pollIntervalMs);
TestClass.orgSetTimeout(polling, pollIntervalMs);
}
}
setTimeout(polling, pollIntervalMs);
TestClass.orgSetTimeout(polling, pollIntervalMs);
}

pollingAssert[TestClass.isPollingStepFlag] = true;
Expand Down
31 changes: 21 additions & 10 deletions AISKU/Tests/TestFramework/TestClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ export class TestClass {
/** The instance of the currently running suite. */
public static currentTestClass: TestClass;
public static currentTestInfo: TestCase|TestCaseAsync;
public static orgSetTimeout: (handler: Function, timeout?: number) => number;
public static orgClearTimeout: (handle?: number) => void;

/** Turns on/off sinon's fake implementation of XMLHttpRequest. On by default. */
public sandboxConfig: any = {};

public static orgSetTimeout: (handler: Function, timeout?: number) => number;
public static orgClearTimeout: (handle?: number) => void;
public static orgObjectDefineProperty = Object.defineProperty;

protected _xhrRequests: FakeXMLHttpRequest[] = [];
Expand Down Expand Up @@ -96,6 +96,9 @@ export class TestClass {
if (!testInfo.steps) {
throw new Error("Must specify 'steps' to take asynchronously");
}
if (testInfo.autoComplete === undefined) {
testInfo.autoComplete = true;
}

if (testInfo.autoComplete === undefined) {
testInfo.autoComplete = true;
Expand Down Expand Up @@ -219,9 +222,10 @@ export class TestClass {
} catch (e) {
console.error("Failed: Unexpected Exception: " + e);
Assert.ok(false, e.toString());

this._testCompleted(true);
// done is QUnit callback indicating the end of the test
testDone();
done();

return;
}
} else if (!testComplete) {
Expand Down Expand Up @@ -267,7 +271,6 @@ export class TestClass {
// Save off the instance of the currently running suite.
TestClass.currentTestClass = this;
TestClass.currentTestInfo = testInfo;

function _testFinished(failed?: boolean) {
TestClass.currentTestClass._testCompleted(failed);
done();
Expand Down Expand Up @@ -302,6 +305,16 @@ export class TestClass {
this._emulateEs3();
}

let failed = false;

TestClass.orgSetTimeout = (handler:Function, timeout?:number) => {
return orgSetTimeout(handler, timeout);
}

TestClass.orgClearTimeout = (handler:number) => {
orgClearTimeout(handler);
}

// Run the test.
try {
this._testStarting();
Expand All @@ -320,17 +333,15 @@ export class TestClass {
result.then(() => {
promiseTimeout && clearTimeout(promiseTimeout);
_testFinished();
},
(reason) => {
}).catch((reason) => {
promiseTimeout && clearTimeout(promiseTimeout);
QUnit.assert.ok(false, "Returned Promise rejected: " + reason);
_testFinished(true);
});
} else {
_testFinished();
}
}
catch (ex) {
} catch (ex) {
console.error("Failed: Unexpected Exception: " + ex);
Assert.ok(false, "Unexpected Exception: " + ex);
_testFinished(true);
Expand Down Expand Up @@ -375,6 +386,7 @@ export class TestClass {

if (_self.isEmulatingEs3) {
// As we removed Object.define we need to temporarily restore this for each sandbox call
// tslint:disable-next-line:forin
for (var field in _self.sandbox) {
var value = _self.sandbox[field];
if (CoreUtils.isFunction(value)) {
Expand All @@ -397,7 +409,6 @@ export class TestClass {

// Restore the Object properties/functions
_self._restoreObject(saveObjectProps);

return result;
}
})(value);
Expand Down
132 changes: 126 additions & 6 deletions AISKU/Tests/applicationinsights.e2e.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import { TestClass } from './TestFramework/TestClass';
import { SinonSpy } from 'sinon';
import { ApplicationInsights, IApplicationInsights } from '../src/applicationinsights-web'
import { Sender } from '@microsoft/applicationinsights-channel-js';
import { IDependencyTelemetry, ContextTagKeys, Util, Event, Trace, Exception, Metric, PageView, PageViewPerformance, RemoteDependencyData, DistributedTracingModes, RequestHeaders } from '@microsoft/applicationinsights-common';
import { IDependencyTelemetry, ContextTagKeys, Util, Event, Trace, Exception, Metric, PageView, PageViewPerformance, RemoteDependencyData, DistributedTracingModes, RequestHeaders, IAutoExceptionTelemetry } from '@microsoft/applicationinsights-common';
import { AppInsightsCore, ITelemetryItem, getGlobal } from "@microsoft/applicationinsights-core-js";
import { TelemetryContext } from '@microsoft/applicationinsights-properties-js';
import { AjaxPlugin } from '@microsoft/applicationinsights-dependencies-js';
import { EventValidator } from './TelemetryValidation/EventValidator';
import { TraceValidator } from './TelemetryValidation/TraceValidator';
import { ExceptionValidator } from './TelemetryValidation/ExceptionValidator';
Expand Down Expand Up @@ -181,7 +180,7 @@ export class ApplicationInsightsTests extends TestClass {
});

this.testCaseAsync({
name: 'E2E.GenericTests: trackException sends to backend',
name: 'E2E.GenericTests: legacy trackException sends to backend',
stepDelay: 1,
steps: [() => {
let exception: Error = null;
Expand All @@ -190,28 +189,141 @@ export class ApplicationInsightsTests extends TestClass {
Assert.ok(false, 'trackException test not run');
} catch (e) {
exception = e;
this._ai.trackException({ exception });
this._ai.trackException({ error: exception } as any);
}
Assert.ok(exception);
}].concat(this.asserts(1))
});

this.testCaseAsync({
name: 'E2E.GenericTests: legacy trackException sends to backend',
name: 'E2E.GenericTests: trackException with auto telemetry sends to backend',
stepDelay: 1,
steps: [() => {
let exception: Error = null;
try {
window['a']['b']();
Assert.ok(false, 'trackException test not run');
} catch (e) {
// Simulating window.onerror option
let autoTelemetry = {
message: e.message,
url: "https://dummy.auto.example.com",
lineNumber: 42,
columnNumber: 53,
error: e,
evt: null
} as IAutoExceptionTelemetry;

exception = e;
this._ai.trackException({ error: exception } as any);
this._ai.trackException({ exception: autoTelemetry });
}
Assert.ok(exception);
}].concat(this.asserts(1))
});

this.testCaseAsync({
name: 'E2E.GenericTests: trackException with message only sends to backend',
stepDelay: 1,
steps: [() => {
let exception: Error = null;
try {
window['a']['b']();
Assert.ok(false, 'trackException test not run');
} catch (e) {
// Simulating window.onerror option
let autoTelemetry = {
message: e.toString(),
url: "https://dummy.message.example.com",
lineNumber: 42,
columnNumber: 53,
error: e.toString(),
evt: null
} as IAutoExceptionTelemetry;

exception = e;
this._ai.trackException({ exception: autoTelemetry });
}
Assert.ok(exception);
}].concat(this.asserts(1))
});

this.testCaseAsync({
name: 'E2E.GenericTests: trackException with message holding error sends to backend',
stepDelay: 1,
steps: [() => {
let exception: Error = null;
try {
window['a']['b']();
Assert.ok(false, 'trackException test not run');
} catch (e) {
// Simulating window.onerror option
let autoTelemetry = {
message: e,
url: "https://dummy.error.example.com",
lineNumber: 42,
columnNumber: 53,
error: undefined,
evt: null
} as IAutoExceptionTelemetry;

try {
exception = e;
this._ai.trackException({ exception: autoTelemetry });
} catch (e) {
console.log(e);
console.log(e.stack);
Assert.ok(false, e.stack);
}
}
Assert.ok(exception);
}].concat(this.asserts(1))
});

this.testCaseAsync({
name: 'E2E.GenericTests: trackException with no Error sends to backend',
stepDelay: 1,
steps: [() => {
let autoTelemetry = {
message: "Test Message",
url: "https://dummy.no.error.example.com",
lineNumber: 42,
columnNumber: 53,
error: this,
evt: null
} as IAutoExceptionTelemetry;
this._ai.trackException({ exception: autoTelemetry });
Assert.ok(autoTelemetry);
}].concat(this.asserts(1))
});

this.testCaseAsync({
name: 'E2E.GenericTests: trackException with CustomError sends to backend',
stepDelay: 1,
steps: [() => {
this._ai.trackException({ exception: new CustomTestError("Test Custom Error!") });
}].concat(this.asserts(1)).concat(() => {
const payloadStr: string[] = this.getPayloadMessages(this.successSpy);
if (payloadStr.length > 0) {
const payload = JSON.parse(payloadStr[0]);
const data = payload.data;
Assert.ok(data, "Has Data");
if (data) {
Assert.ok(data.baseData, "Has BaseData");
let baseData = data.baseData;
if (baseData) {
const ex = baseData.exceptions[0];
Assert.ok(ex.message.indexOf("Test Custom Error!") !== -1, "Make sure the error message is present [" + ex.message + "]");
Assert.ok(ex.message.indexOf("CustomTestError") !== -1, "Make sure the error type is present [" + ex.message + "]");
Assert.equal("CustomTestError", ex.typeName, "Got the correct typename");
Assert.ok(ex.stack.length > 0, "Has stack");
Assert.ok(ex.parsedStack, "Stack was parsed");
Assert.ok(ex.hasFullStack, "Stack has been decoded");
}
}
}
})
});

this.testCaseAsync({
name: "TelemetryContext: track metric",
stepDelay: 1,
Expand Down Expand Up @@ -766,3 +878,11 @@ export class ApplicationInsightsTests extends TestClass {
}, "sender succeeded", 60, 1000))
];
}

class CustomTestError extends Error {
constructor(message = "", ...args) {
super(message, ...args);
this.name = "CustomTestError";
this.message = message + " -- test error.";
}
}
3 changes: 2 additions & 1 deletion AISKU/snippet/snippet.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,8 @@
url: url,
lineNumber: lineNumber,
columnNumber: columnNumber,
error: error
error: error,
evt: win.event
});
}

Expand Down
13 changes: 10 additions & 3 deletions AISKU/src/Initialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
Envelope, Event, Exception, Metric, PageView, PageViewData, RemoteDependencyData, IEventTelemetry,
ITraceTelemetry, IMetricTelemetry, IDependencyTelemetry, IExceptionTelemetry, IAutoExceptionTelemetry,
IPageViewTelemetry, IPageViewPerformanceTelemetry, Trace, PageViewPerformance, Data, SeverityLevel,
IConfig, ConfigurationManager, ContextTagKeys, DataSanitizer, TelemetryItemCreator, IAppInsights, CtxTagKeys, Extensions,
IConfig, ConfigurationManager, ContextTagKeys, IDataSanitizer, DataSanitizer, TelemetryItemCreator, IAppInsights, CtxTagKeys, Extensions,
IPropertiesPlugin, DistributedTracingModes, PropertiesPluginIdentifier, BreezeChannelIdentifier, AnalyticsPluginIdentifier,
ITelemetryContext as Common_ITelemetryContext, parseConnectionString
} from "@microsoft/applicationinsights-common"
Expand Down Expand Up @@ -57,6 +57,13 @@ export interface IApplicationInsights extends IAppInsights, IDependenciesPlugin,
// import * as Common from "@microsoft/applicationinsights-common"
// export const Telemetry = Common;

let fieldType = {
Default: FieldType.Default,
Required: FieldType.Required,
Array: FieldType.Array,
Hidden: FieldType.Hidden
};

/**
* Telemetry type classes, e.g. PageView, Exception, etc
*/
Expand All @@ -70,7 +77,7 @@ export const Telemetry = {
UrlHelper,
DateTimeUtils,
ConnectionStringParser,
FieldType,
FieldType : fieldType,
RequestHeaders,
DisabledPropertyName,
ProcessLegacy,
Expand All @@ -92,7 +99,7 @@ export const Telemetry = {
SeverityLevel,
ConfigurationManager,
ContextTagKeys,
DataSanitizer,
DataSanitizer: DataSanitizer as IDataSanitizer,
TelemetryItemCreator,
CtxTagKeys,
Extensions,
Expand Down
Loading

0 comments on commit ab216a7

Please sign in to comment.