fix lint error for analytics and dependencies plugin
This commit is contained in:
Родитель
156c25f928
Коммит
60da0f7506
|
@ -31,16 +31,16 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
name: 'enableAutoRouteTracking: event listener is added to the popstate event',
|
||||
test: () => {
|
||||
// Setup
|
||||
var appInsights = new ApplicationInsights();
|
||||
var core = new AppInsightsCore();
|
||||
var channel = new ChannelPlugin();
|
||||
var eventListenerStub = this.sandbox.stub(window, 'addEventListener');
|
||||
const appInsights = new ApplicationInsights();
|
||||
const core = new AppInsightsCore();
|
||||
const channel = new ChannelPlugin();
|
||||
const eventListenerStub = this.sandbox.stub(window, 'addEventListener');
|
||||
|
||||
// Act
|
||||
core.initialize(<IConfig & IConfiguration>{
|
||||
core.initialize({
|
||||
instrumentationKey: '',
|
||||
enableAutoRouteTracking: true
|
||||
}, [appInsights, channel]);
|
||||
} as IConfig & IConfiguration, [appInsights, channel]);
|
||||
|
||||
// Assert
|
||||
Assert.ok(eventListenerStub.calledTwice);
|
||||
|
@ -53,22 +53,22 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
name: 'enableAutoRouteTracking: route changes trigger a new pageview',
|
||||
test: () => {
|
||||
// Setup
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.autoRoutePVDelay = 500;
|
||||
var core = new AppInsightsCore();
|
||||
var channel = new ChannelPlugin();
|
||||
appInsights['_properties'] = <any>{
|
||||
const core = new AppInsightsCore();
|
||||
const channel = new ChannelPlugin();
|
||||
appInsights['_properties'] = ({
|
||||
context: { telemetryTrace: { traceID: 'not set', name: 'name not set' } }
|
||||
}
|
||||
} as any)
|
||||
appInsights['_prevUri'] = "firstUri";
|
||||
appInsights['_currUri'] = "secondUri";
|
||||
const trackPageViewStub = this.sandbox.stub(appInsights, 'trackPageView');
|
||||
|
||||
// Act
|
||||
core.initialize(<IConfig & IConfiguration>{
|
||||
core.initialize({
|
||||
instrumentationKey: '',
|
||||
enableAutoRouteTracking: true
|
||||
}, [appInsights, channel]);
|
||||
} as IConfig & IConfiguration, [appInsights, channel]);
|
||||
window.dispatchEvent(Util.createDomEvent('locationchange'));
|
||||
this.clock.tick(500);
|
||||
|
||||
|
@ -88,21 +88,21 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
name: 'enableAutoRouteTracking: route changes trigger a new pageview with correct refUri when route changes happening before the timer autoRoutePVDelay stops',
|
||||
test: () => {
|
||||
// Setup
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.autoRoutePVDelay = 500;
|
||||
var core = new AppInsightsCore();
|
||||
var channel = new ChannelPlugin();
|
||||
appInsights['_properties'] = <any>{
|
||||
const core = new AppInsightsCore();
|
||||
const channel = new ChannelPlugin();
|
||||
appInsights['_properties'] = ({
|
||||
context: { telemetryTrace: { traceID: 'not set', name: 'name not set' } }
|
||||
}
|
||||
} as any)
|
||||
appInsights['_prevUri'] = "firstUri";
|
||||
const trackPageViewStub = this.sandbox.stub(appInsights, 'trackPageView');
|
||||
|
||||
// Act
|
||||
core.initialize(<IConfig & IConfiguration>{
|
||||
core.initialize({
|
||||
instrumentationKey: '',
|
||||
enableAutoRouteTracking: true
|
||||
}, [appInsights, channel]);
|
||||
} as IConfig & IConfiguration, [appInsights, channel]);
|
||||
window.dispatchEvent(Util.createDomEvent('locationchange'));
|
||||
this.clock.tick(200);
|
||||
|
||||
|
@ -133,19 +133,19 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
const originalReplaceState = history.replaceState;
|
||||
history.pushState = null;
|
||||
history.replaceState = null;
|
||||
var appInsights = new ApplicationInsights();
|
||||
var core = new AppInsightsCore();
|
||||
var channel = new ChannelPlugin();
|
||||
appInsights['_properties'] = <any>{
|
||||
const appInsights = new ApplicationInsights();
|
||||
const core = new AppInsightsCore();
|
||||
const channel = new ChannelPlugin();
|
||||
appInsights['_properties'] = ({
|
||||
context: { telemetryTrace: { traceID: 'not set'}}
|
||||
}
|
||||
} as any)
|
||||
this.sandbox.stub(appInsights, 'trackPageView');
|
||||
|
||||
// Act
|
||||
core.initialize(<IConfig & IConfiguration>{
|
||||
core.initialize({
|
||||
instrumentationKey: '',
|
||||
enableAutoRouteTracking: true
|
||||
}, [appInsights, channel]);
|
||||
} as IConfig & IConfiguration, [appInsights, channel]);
|
||||
window.dispatchEvent(Util.createDomEvent('locationchange'));
|
||||
|
||||
// Assert
|
||||
|
@ -161,12 +161,12 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
name: 'AppInsightsTests: PageVisitTimeManager is constructed when analytics plugin is initialized',
|
||||
test: () => {
|
||||
// Setup
|
||||
var channel = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
var appInsights: ApplicationInsights = new ApplicationInsights();
|
||||
const channel = new ChannelPlugin();
|
||||
const core = new AppInsightsCore();
|
||||
const appInsights: ApplicationInsights = new ApplicationInsights();
|
||||
|
||||
// Act
|
||||
var config = {
|
||||
const config = {
|
||||
instrumentationKey: 'ikey'
|
||||
};
|
||||
|
||||
|
@ -187,11 +187,11 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
name: 'AppInsightsTests: PageVisitTimeManager is available when config.autoTrackPageVisitTime is true and trackPageView is called',
|
||||
test: () => {
|
||||
// Setup
|
||||
var channel = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
var appInsights: ApplicationInsights = new ApplicationInsights();
|
||||
const channel = new ChannelPlugin();
|
||||
const core = new AppInsightsCore();
|
||||
const appInsights: ApplicationInsights = new ApplicationInsights();
|
||||
|
||||
var config = {
|
||||
const config = {
|
||||
instrumentationKey: 'ikey',
|
||||
autoTrackPageVisitTime: true
|
||||
};
|
||||
|
@ -217,10 +217,10 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
name: 'AppInsightsTests: config can be set from root',
|
||||
test: () => {
|
||||
// Setup
|
||||
var appInsights: ApplicationInsights = new ApplicationInsights();
|
||||
const appInsights: ApplicationInsights = new ApplicationInsights();
|
||||
|
||||
// Act
|
||||
var config = {
|
||||
const config = {
|
||||
instrumentationKey: 'instrumentation_key',
|
||||
samplingPercentage: 12,
|
||||
accountId: 'aaa',
|
||||
|
@ -244,16 +244,16 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
name: "AppInsightsTests: public members are correct",
|
||||
test: () => {
|
||||
// setup
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
// the assert test will only see config as part of an object member if it has been initialized. Not sure how it worked before
|
||||
appInsights.config = {};
|
||||
var leTest = (name) => {
|
||||
const leTest = (name) => {
|
||||
// assert
|
||||
Assert.ok(name in appInsights, name + " exists");
|
||||
}
|
||||
|
||||
// act
|
||||
var members = [
|
||||
const members = [
|
||||
"config",
|
||||
"trackException",
|
||||
"_onerror",
|
||||
|
@ -284,20 +284,20 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
name: 'AppInsightsGenericTests: envelope type, data type, and ikey are correct',
|
||||
test: () => {
|
||||
// setup
|
||||
var iKey: string = "BDC8736D-D8E8-4B69-B19B-B0CE6B66A456";
|
||||
var iKeyNoDash: string = "BDC8736DD8E84B69B19BB0CE6B66A456";
|
||||
var plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
const iKey: string = "BDC8736D-D8E8-4B69-B19B-B0CE6B66A456";
|
||||
const iKeyNoDash: string = "BDC8736DD8E84B69B19BB0CE6B66A456";
|
||||
const plugin = new ChannelPlugin();
|
||||
const core = new AppInsightsCore();
|
||||
core.initialize(
|
||||
{instrumentationKey: iKey},
|
||||
[plugin]
|
||||
);
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.initialize({instrumentationKey: core.config.instrumentationKey}, core, []);
|
||||
var trackStub = this.sandbox.stub(appInsights.core, "track");
|
||||
const trackStub = this.sandbox.stub(appInsights.core, "track");
|
||||
|
||||
let envelope: ITelemetryItem;
|
||||
var test = (action, expectedEnvelopeType, expectedDataType, test?: () => void) => {
|
||||
const test = (action, expectedEnvelopeType, expectedDataType, test?: () => void) => {
|
||||
action();
|
||||
envelope = this.getFirstResult(action, trackStub);
|
||||
Assert.equal("", envelope.iKey, "envelope iKey");
|
||||
|
@ -322,12 +322,12 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
const core = new AppInsightsCore();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin]
|
||||
);
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, []);
|
||||
const senderStub = this.sandbox.stub(appInsights.core, "track");
|
||||
|
||||
|
@ -348,14 +348,14 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
const core = new AppInsightsCore();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin]
|
||||
);
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, []);
|
||||
let trackStub = this.sandbox.stub(appInsights.core, "track");
|
||||
const trackStub = this.sandbox.stub(appInsights.core, "track");
|
||||
|
||||
// Test
|
||||
appInsights.trackException({error: new Error(), severityLevel: SeverityLevel.Critical});
|
||||
|
@ -373,14 +373,14 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
const core = new AppInsightsCore();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin]
|
||||
);
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, []);
|
||||
let trackStub = this.sandbox.stub(appInsights.core, "track");
|
||||
const trackStub = this.sandbox.stub(appInsights.core, "track");
|
||||
|
||||
// Test
|
||||
appInsights.trackException({error: new Error(), severityLevel: SeverityLevel.Critical});
|
||||
|
@ -402,7 +402,7 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
const core = new AppInsightsCore();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin]
|
||||
|
@ -427,24 +427,24 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
const core = new AppInsightsCore();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin]
|
||||
);
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.initialize({ instrumentationKey: "ikey"}, core, []);
|
||||
var dumpSpy = this.sandbox.spy(Util, "dump")
|
||||
var unexpectedError = new Error("some message");
|
||||
var stub = this.sandbox.stub(appInsights, "trackException").throws(unexpectedError);
|
||||
const dumpSpy = this.sandbox.spy(Util, "dump")
|
||||
const unexpectedError = new Error("some message");
|
||||
const stub = this.sandbox.stub(appInsights, "trackException").throws(unexpectedError);
|
||||
|
||||
// Act
|
||||
appInsights._onerror({message: "any message", url: "any://url", lineNumber: 123, columnNumber: 456, error: unexpectedError});
|
||||
|
||||
// Test
|
||||
Assert.ok(dumpSpy.returnValues[0].indexOf("stack: ") != -1);
|
||||
Assert.ok(dumpSpy.returnValues[0].indexOf(`message: '${unexpectedError.message}'`) != -1);
|
||||
Assert.ok(dumpSpy.returnValues[0].indexOf("name: 'Error'") != -1);
|
||||
Assert.ok(dumpSpy.returnValues[0].indexOf(`message: '${unexpectedError.message}'`) !== -1);
|
||||
Assert.ok(dumpSpy.returnValues[0].indexOf("name: 'Error'") !== -1);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -482,7 +482,7 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
const core = new AppInsightsCore();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin]
|
||||
|
@ -504,18 +504,18 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
const core = new AppInsightsCore();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin]
|
||||
);
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, []);
|
||||
const trackExceptionSpy = this.sandbox.spy(appInsights, "trackException");
|
||||
|
||||
// Act
|
||||
// Last arg is not an error/null which will be treated as not CORS issue
|
||||
appInsights._onerror({message: "Script error.", url: "", lineNumber: 0, columnNumber: 0, error: <any>new Object()});
|
||||
appInsights._onerror({message: "Script error.", url: "", lineNumber: 0, columnNumber: 0, error: new Object() as any});
|
||||
|
||||
// Assert
|
||||
// properties are passed as a 3rd parameter
|
||||
|
@ -525,7 +525,7 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
}
|
||||
|
||||
private addStartStopTrackPageTests() {
|
||||
var testValues = {
|
||||
const testValues = {
|
||||
name: "name",
|
||||
url: "url",
|
||||
duration: 200,
|
||||
|
@ -543,14 +543,14 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
const core = new AppInsightsCore();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin]
|
||||
);
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, []);
|
||||
var spy = this.sandbox.spy(appInsights, "sendPageViewInternal");
|
||||
const spy = this.sandbox.spy(appInsights, "sendPageViewInternal");
|
||||
this.clock.tick(1);
|
||||
|
||||
// act
|
||||
|
@ -560,11 +560,11 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
|
||||
// verify
|
||||
Assert.ok(spy.calledOnce, "stop track page view sent data");
|
||||
var actual = spy.args[0][0];
|
||||
const actual = spy.args[0][0];
|
||||
Assert.equal(testValues.name, actual.name);
|
||||
Assert.equal(testValues.url, actual.uri);
|
||||
|
||||
var actualProperties = actual.properties;
|
||||
const actualProperties = actual.properties;
|
||||
Assert.equal(testValues.duration, actualProperties.duration, "duration is calculated and sent correctly");
|
||||
Assert.equal(testValues.properties.property1, actualProperties.property1);
|
||||
Assert.equal(testValues.properties.property2, actualProperties.property2);
|
||||
|
@ -574,11 +574,11 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
name: "Timing Tests: Start/StopPageView tracks single page view with no parameters",
|
||||
test: () => {
|
||||
// setup
|
||||
var core = new AppInsightsCore();
|
||||
const core = new AppInsightsCore();
|
||||
this.sandbox.stub(core, "getTransmissionControl");
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, []);
|
||||
var trackStub = this.sandbox.stub(appInsights.core, "track");
|
||||
const trackStub = this.sandbox.stub(appInsights.core, "track");
|
||||
this.clock.tick(10); // Needed to ensure the duration calculation works
|
||||
|
||||
// act
|
||||
|
@ -588,7 +588,7 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
Assert.ok(trackStub.calledOnce, "single page view tracking stopped");
|
||||
|
||||
// verify
|
||||
var telemetry: ITelemetryItem = trackStub.args[0][0];
|
||||
const telemetry: ITelemetryItem = trackStub.args[0][0];
|
||||
Assert.equal(window.document.title, telemetry.baseData.name);
|
||||
Assert.equal(testValues.duration, telemetry.baseData.properties.duration);
|
||||
}
|
||||
|
@ -598,11 +598,11 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
name: "Timing Tests: Multiple Start/StopPageView track single pages view ",
|
||||
test: () => {
|
||||
// setup
|
||||
var core = new AppInsightsCore();
|
||||
const core = new AppInsightsCore();
|
||||
this.sandbox.stub(core, "getTransmissionControl");
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, []);
|
||||
var trackStub = this.sandbox.stub(appInsights.core, "track");
|
||||
const trackStub = this.sandbox.stub(appInsights.core, "track");
|
||||
this.clock.tick(10); // Needed to ensure the duration calculation works
|
||||
|
||||
// act
|
||||
|
@ -620,7 +620,7 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
|
||||
// verify
|
||||
// Empty parameters
|
||||
var telemetry: ITelemetryItem = trackStub.args[0][0];
|
||||
let telemetry: ITelemetryItem = trackStub.args[0][0];
|
||||
Assert.equal(window.document.title, telemetry.baseData.name);
|
||||
Assert.equal(window.document.location.href, telemetry.baseData.uri);
|
||||
|
||||
|
@ -638,14 +638,14 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
() => {
|
||||
// setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
const core = new AppInsightsCore();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin]
|
||||
);
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, []);
|
||||
var logStub = this.sandbox.stub(core.logger, "throwInternal");
|
||||
const logStub = this.sandbox.stub(core.logger, "throwInternal");
|
||||
core.logger.consoleLoggingLevel = () => 999;
|
||||
|
||||
// act
|
||||
|
@ -663,14 +663,14 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
() => {
|
||||
// setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
const core = new AppInsightsCore();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin]
|
||||
);
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, []);
|
||||
var logStub = this.sandbox.stub(core.logger, "throwInternal");
|
||||
const logStub = this.sandbox.stub(core.logger, "throwInternal");
|
||||
core.logger.consoleLoggingLevel = () => 999;
|
||||
|
||||
// act
|
||||
|
@ -688,14 +688,14 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// Setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
const core = new AppInsightsCore();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin]
|
||||
);
|
||||
var appInsights = new ApplicationInsights();
|
||||
const appInsights = new ApplicationInsights();
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, []);
|
||||
var trackStub = this.sandbox.stub(appInsights.core, "track");
|
||||
const trackStub = this.sandbox.stub(appInsights.core, "track");
|
||||
|
||||
// Act
|
||||
appInsights.trackMetric({name: "test metric", average: 0});
|
||||
|
@ -707,7 +707,7 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
trackStub.reset();
|
||||
|
||||
// Act
|
||||
for (var i = 0; i < 100; i++) {
|
||||
for (let i = 0; i < 100; i++) {
|
||||
appInsights.trackMetric({name: "test metric", average: 0});
|
||||
}
|
||||
this.clock.tick(1);
|
||||
|
@ -724,8 +724,8 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// Setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
var appInsights = new ApplicationInsights();
|
||||
const core = new AppInsightsCore();
|
||||
const appInsights = new ApplicationInsights();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin, appInsights]
|
||||
|
@ -733,11 +733,11 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]);
|
||||
plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]);
|
||||
|
||||
var trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
var telemetryInitializer = {
|
||||
const trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
const telemetryInitializer = {
|
||||
initializer: (envelope) => { }
|
||||
}
|
||||
var spy = this.sandbox.spy(telemetryInitializer, "initializer");
|
||||
const spy = this.sandbox.spy(telemetryInitializer, "initializer");
|
||||
|
||||
// act
|
||||
appInsights.addTelemetryInitializer(telemetryInitializer.initializer);
|
||||
|
@ -755,17 +755,17 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// Setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
var appInsights = new ApplicationInsights();
|
||||
const core = new AppInsightsCore();
|
||||
const appInsights = new ApplicationInsights();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin, appInsights]
|
||||
);
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]);
|
||||
plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]);
|
||||
var trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
var nameOverride = "my unique name";
|
||||
var telemetryInitializer = {
|
||||
const trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
const nameOverride = "my unique name";
|
||||
const telemetryInitializer = {
|
||||
initializer: (envelope) => {
|
||||
envelope.name = nameOverride;
|
||||
return true;}
|
||||
|
@ -779,7 +779,7 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
// verify
|
||||
Assert.ok(trackStub.calledOnce, "channel sender was called");
|
||||
|
||||
let envelope: ITelemetryItem = trackStub.args[0][0];
|
||||
const envelope: ITelemetryItem = trackStub.args[0][0];
|
||||
Assert.equal(envelope.name, nameOverride, 'expected envelope is used');
|
||||
}
|
||||
});
|
||||
|
@ -789,25 +789,25 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// Setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
var appInsights = new ApplicationInsights();
|
||||
const core = new AppInsightsCore();
|
||||
const appInsights = new ApplicationInsights();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin, appInsights]
|
||||
);
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]);
|
||||
plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]);
|
||||
var trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
const trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
|
||||
var messageOverride = "my unique name";
|
||||
var propOverride = "val1";
|
||||
var telemetryInitializer = {
|
||||
const messageOverride = "my unique name";
|
||||
const propOverride = "val1";
|
||||
const telemetryInitializer = {
|
||||
// This illustrates how to use telemetry initializer (onBeforeSendTelemetry)
|
||||
// to access/ modify the contents of an envelope.
|
||||
initializer: (envelope) => {
|
||||
if (envelope.baseType ===
|
||||
Trace.dataType) {
|
||||
var telemetryItem = envelope.baseData;
|
||||
const telemetryItem = envelope.baseData;
|
||||
telemetryItem.message = messageOverride;
|
||||
telemetryItem.properties = telemetryItem.properties || {};
|
||||
telemetryItem.properties["prop1"] = propOverride;
|
||||
|
@ -824,7 +824,7 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
// verify
|
||||
Assert.ok(trackStub.calledOnce, "sender should be called");
|
||||
|
||||
let envelope: ITelemetryItem = trackStub.args[0][0];
|
||||
const envelope: ITelemetryItem = trackStub.args[0][0];
|
||||
Assert.equal(messageOverride, envelope.baseData.message);
|
||||
Assert.equal(propOverride, envelope.baseData.properties["prop1"]);
|
||||
}
|
||||
|
@ -835,18 +835,18 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// Setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
var appInsights = new ApplicationInsights();
|
||||
const core = new AppInsightsCore();
|
||||
const appInsights = new ApplicationInsights();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin, appInsights]
|
||||
);
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]);
|
||||
plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]);
|
||||
var initializer1 = { init: () => { } };
|
||||
var initializer2 = { init: () => { } };
|
||||
var spy1 = this.sandbox.spy(initializer1, "init");
|
||||
var spy2 = this.sandbox.spy(initializer2, "init");
|
||||
const initializer1 = { init: () => { } };
|
||||
const initializer2 = { init: () => { } };
|
||||
const spy1 = this.sandbox.spy(initializer1, "init");
|
||||
const spy2 = this.sandbox.spy(initializer2, "init");
|
||||
|
||||
// act
|
||||
appInsights.addTelemetryInitializer(initializer1.init);
|
||||
|
@ -865,18 +865,18 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// Setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
var appInsights = new ApplicationInsights();
|
||||
const core = new AppInsightsCore();
|
||||
const appInsights = new ApplicationInsights();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin, appInsights]
|
||||
);
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]);
|
||||
plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]);
|
||||
var trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
const trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
|
||||
// act
|
||||
appInsights.addTelemetryInitializer(() => { return false; });
|
||||
appInsights.addTelemetryInitializer(() => false);
|
||||
appInsights.trackTrace({message: 'test message'});
|
||||
|
||||
// verify
|
||||
|
@ -889,15 +889,15 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// Setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
var appInsights = new ApplicationInsights();
|
||||
const core = new AppInsightsCore();
|
||||
const appInsights = new ApplicationInsights();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin, appInsights]
|
||||
);
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]);
|
||||
plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]);
|
||||
var trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
const trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
|
||||
// act
|
||||
appInsights.addTelemetryInitializer(() => { return; });
|
||||
|
@ -913,18 +913,18 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// Setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
var appInsights = new ApplicationInsights();
|
||||
const core = new AppInsightsCore();
|
||||
const appInsights = new ApplicationInsights();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin, appInsights]
|
||||
);
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]);
|
||||
plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]);
|
||||
var trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
const trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
|
||||
// act
|
||||
appInsights.addTelemetryInitializer(() => { return true; });
|
||||
appInsights.addTelemetryInitializer(() => true);
|
||||
appInsights.trackTrace({message: 'test message'});
|
||||
|
||||
// verify
|
||||
|
@ -937,19 +937,19 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// Setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
var appInsights = new ApplicationInsights();
|
||||
const core = new AppInsightsCore();
|
||||
const appInsights = new ApplicationInsights();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin, appInsights]
|
||||
);
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]);
|
||||
plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]);
|
||||
var trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
const trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
|
||||
// act
|
||||
appInsights.addTelemetryInitializer(() => { return true; });
|
||||
appInsights.addTelemetryInitializer(() => { return false; });
|
||||
appInsights.addTelemetryInitializer(() => true);
|
||||
appInsights.addTelemetryInitializer(() => false);
|
||||
|
||||
appInsights.trackTrace({message: 'test message'});
|
||||
|
||||
|
@ -963,19 +963,19 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// Setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
var appInsights = new ApplicationInsights();
|
||||
const core = new AppInsightsCore();
|
||||
const appInsights = new ApplicationInsights();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin, appInsights]
|
||||
);
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]);
|
||||
plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]);
|
||||
var trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
const trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
|
||||
// act
|
||||
appInsights.addTelemetryInitializer(() => { return false; });
|
||||
appInsights.addTelemetryInitializer(() => { return true; });
|
||||
appInsights.addTelemetryInitializer(() => false);
|
||||
appInsights.addTelemetryInitializer(() => true);
|
||||
|
||||
appInsights.trackTrace({message: 'test message'});
|
||||
|
||||
|
@ -989,20 +989,20 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// Setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
var appInsights = new ApplicationInsights();
|
||||
const core = new AppInsightsCore();
|
||||
const appInsights = new ApplicationInsights();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin, appInsights]
|
||||
);
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]);
|
||||
plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]);
|
||||
var trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
const trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
|
||||
// act
|
||||
appInsights.addTelemetryInitializer(<any>(() => { return "asdf"; }));
|
||||
appInsights.addTelemetryInitializer(() => { return null; });
|
||||
appInsights.addTelemetryInitializer(() => { return undefined; });
|
||||
appInsights.addTelemetryInitializer((() => "asdf") as any);
|
||||
appInsights.addTelemetryInitializer(() => null);
|
||||
appInsights.addTelemetryInitializer(() => undefined);
|
||||
appInsights.trackTrace({message: 'test message'});
|
||||
|
||||
// verify
|
||||
|
@ -1015,16 +1015,16 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
test: () => {
|
||||
// Setup
|
||||
const plugin = new ChannelPlugin();
|
||||
var core = new AppInsightsCore();
|
||||
var appInsights = new ApplicationInsights();
|
||||
const core = new AppInsightsCore();
|
||||
const appInsights = new ApplicationInsights();
|
||||
core.initialize(
|
||||
{instrumentationKey: "key"},
|
||||
[plugin, appInsights]
|
||||
);
|
||||
appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]);
|
||||
plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]);
|
||||
var trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
var logStub = this.sandbox.spy(appInsights.core.logger, "throwInternal")
|
||||
const trackStub = this.sandbox.spy(appInsights.core['_channelController'].channelQueue[0][0], 'processTelemetry');
|
||||
const logStub = this.sandbox.spy(appInsights.core.logger, "throwInternal")
|
||||
// act
|
||||
appInsights.addTelemetryInitializer(() => { throw new Error("Test error IGNORE"); });
|
||||
appInsights.addTelemetryInitializer(() => { });
|
||||
|
@ -1042,7 +1042,7 @@ export class ApplicationInsightsTests extends TestClass {
|
|||
const index: number = skipSessionState ? 1 : 0;
|
||||
|
||||
Assert.ok(trackStub.args && trackStub.args[index] && trackStub.args[index][0], "track was called for: " + action);
|
||||
return <ITelemetryItem>trackStub.args[index][0];
|
||||
return trackStub.args[index][0] as ITelemetryItem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1053,6 +1053,10 @@ class ChannelPlugin implements IPlugin {
|
|||
public isResumeInvoked = false;
|
||||
public isPauseInvoked = false;
|
||||
|
||||
public identifier = "Sender";
|
||||
|
||||
public priority: number = 1001;
|
||||
|
||||
constructor() {
|
||||
this.processTelemetry = this._processTelemetry.bind(this);
|
||||
}
|
||||
|
@ -1077,14 +1081,10 @@ class ChannelPlugin implements IPlugin {
|
|||
|
||||
public processTelemetry(env: ITelemetryItem) {}
|
||||
|
||||
public identifier = "Sender";
|
||||
|
||||
setNextPlugin(next: any) {
|
||||
// no next setup
|
||||
}
|
||||
|
||||
public priority: number = 1001;
|
||||
|
||||
public initialize = (config: IConfiguration, core: AppInsightsCore, plugin: IPlugin[]) => {
|
||||
}
|
||||
|
||||
|
|
|
@ -34,16 +34,16 @@ export class TelemetryItemCreatorTests extends TestClass {
|
|||
name: "TelemetryItemCreatorTests: create a valid ITelemetryItem for a page view performance item",
|
||||
test: () => {
|
||||
// setup
|
||||
let name = "testName";
|
||||
let uri = "testUri";
|
||||
var pageViewPerformance = new PageViewPerformance(this._core.logger, name, uri, null);
|
||||
let properties = {
|
||||
const name = "testName";
|
||||
const uri = "testUri";
|
||||
const pageViewPerformance = new PageViewPerformance(this._core.logger, name, uri, null);
|
||||
const properties = {
|
||||
"propKey1": "PropVal1",
|
||||
"propKey2": "PropVal2"
|
||||
};
|
||||
|
||||
// act
|
||||
let telemetryItem = TelemetryItemCreator.create<PageViewPerformance>(
|
||||
const telemetryItem = TelemetryItemCreator.create<PageViewPerformance>(
|
||||
pageViewPerformance,
|
||||
PageViewPerformance.dataType,
|
||||
PageViewPerformance.envelopeType,
|
||||
|
@ -62,19 +62,19 @@ export class TelemetryItemCreatorTests extends TestClass {
|
|||
name: "TelemetryItemCreatorTests: create a valid ITelemetryItem for a page view item",
|
||||
test: () => {
|
||||
// setup
|
||||
let name = "testName";
|
||||
let uri = "testUri";
|
||||
var pageView: IPageViewTelemetry = {
|
||||
name: name,
|
||||
uri: uri
|
||||
const name = "testName";
|
||||
const uri = "testUri";
|
||||
const pageView: IPageViewTelemetry = {
|
||||
name,
|
||||
uri
|
||||
};
|
||||
let properties = {
|
||||
const properties = {
|
||||
"propKey1": "PropVal1",
|
||||
"propKey2": "PropVal2"
|
||||
};
|
||||
|
||||
// act
|
||||
let telemetryItem = TelemetryItemCreator.create<IPageViewTelemetry>(
|
||||
const telemetryItem = TelemetryItemCreator.create<IPageViewTelemetry>(
|
||||
pageView,
|
||||
PageView.dataType,
|
||||
PageView.envelopeType,
|
||||
|
@ -98,6 +98,12 @@ class ChannelPlugin implements IPlugin {
|
|||
public isResumeInvoked = false;
|
||||
public isPauseInvoked = false;
|
||||
|
||||
public processTelemetry;
|
||||
|
||||
public identifier = "Sender";
|
||||
|
||||
public priority: number = 1001;
|
||||
|
||||
constructor() {
|
||||
this.processTelemetry = this._processTelemetry.bind(this);
|
||||
}
|
||||
|
@ -119,17 +125,11 @@ class ChannelPlugin implements IPlugin {
|
|||
callBack();
|
||||
}
|
||||
}
|
||||
|
||||
public processTelemetry;
|
||||
|
||||
public identifier = "Sender";
|
||||
|
||||
setNextPlugin(next: any) {
|
||||
// no next setup
|
||||
}
|
||||
|
||||
public priority: number = 1001;
|
||||
|
||||
public initialize = (config: IConfiguration) => {
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
/// <reference path="../External/qunit.d.ts" />
|
||||
|
||||
/** Wrapper around QUnit asserts. This class has two purposes:
|
||||
/**
|
||||
* Wrapper around QUnit asserts. This class has two purposes:
|
||||
* - Make Assertion methods easy to discover.
|
||||
* - Make them consistent with XUnit assertions in the order of the actual and expected parameter values.
|
||||
*/
|
||||
class Assert {
|
||||
/**
|
||||
/**
|
||||
* A deep recursive comparison assertion, working on primitive types, arrays, objects,
|
||||
* regular expressions, dates and functions.
|
||||
*
|
||||
|
@ -21,7 +22,7 @@ class Assert {
|
|||
return deepEqual(actual, expected, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* A non-strict comparison assertion, roughly equivalent to JUnit assertEquals.
|
||||
*
|
||||
* The equal assertion uses the simple comparison operator (==) to compare the actual
|
||||
|
@ -37,7 +38,7 @@ class Assert {
|
|||
return equal(actual, expected, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* An inverted deep recursive comparison assertion, working on primitive types,
|
||||
* arrays, objects, regular expressions, dates and functions.
|
||||
*
|
||||
|
@ -53,7 +54,7 @@ class Assert {
|
|||
return notDeepEqual(actual, expected, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* A non-strict comparison assertion, checking for inequality.
|
||||
*
|
||||
* The notEqual assertion uses the simple inverted comparison operator (!=) to compare
|
||||
|
@ -77,7 +78,7 @@ class Assert {
|
|||
return propEqual(actual, expected, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* A non-strict comparison assertion, checking for inequality.
|
||||
*
|
||||
* The notStrictEqual assertion uses the strict inverted comparison operator (!==)
|
||||
|
@ -93,7 +94,7 @@ class Assert {
|
|||
return notStrictEqual(actual, expected, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* A boolean assertion, equivalent to CommonJS's assert.ok() and JUnit's assertTrue().
|
||||
* Passes if the first argument is truthy.
|
||||
*
|
||||
|
@ -108,7 +109,7 @@ class Assert {
|
|||
return ok(state, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* A strict type and value comparison assertion.
|
||||
*
|
||||
* The strictEqual() assertion provides the most rigid comparison of type and value with
|
||||
|
@ -122,7 +123,7 @@ class Assert {
|
|||
return strictEqual(actual, expected, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Assertion to test if a callback throws an exception when run.
|
||||
*
|
||||
* When testing code that is expected to throw an exception based on a specific set of
|
||||
|
@ -134,7 +135,7 @@ class Assert {
|
|||
*/
|
||||
public static throws(block: () => any, expected: any, message?: string): any;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @param block Function to execute
|
||||
* @param message A short description of the assertion
|
||||
*/
|
||||
|
|
|
@ -22,7 +22,7 @@ class ContractTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
public registerTests() {
|
||||
var name = this.name + ": ";
|
||||
const name = this.name + ": ";
|
||||
this.testCase({
|
||||
name: name + "constructor does not throw errors",
|
||||
test: () => {
|
||||
|
@ -33,7 +33,7 @@ class ContractTestHelper extends TestClass {
|
|||
this.testCase({
|
||||
name: name + "serialization does not throw errors",
|
||||
test: () => {
|
||||
var subject = this.getSubject(this.initializer, this.name);
|
||||
const subject = this.getSubject(this.initializer, this.name);
|
||||
this.serialize(subject, this.name);
|
||||
}
|
||||
});
|
||||
|
@ -74,8 +74,8 @@ class ContractTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
private allRequiredFieldsAreConstructed(initializer: () => any, name: string) {
|
||||
var subject = this.getSubject(initializer, name);
|
||||
for (var field in subject.aiDataContract) {
|
||||
const subject = this.getSubject(initializer, name);
|
||||
for (const field in subject.aiDataContract) {
|
||||
if (subject.aiDataContract[field] & Microsoft.ApplicationInsights.FieldType.Required) {
|
||||
Assert.ok(subject[field] != null, "The required field '" + field + "' is constructed for: '" + name + "'");
|
||||
}
|
||||
|
@ -83,24 +83,24 @@ class ContractTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
private extraFieldsAreRemovedBySerializer(initializer: () => any, name: string) {
|
||||
var subject = this.getSubject(initializer, name);
|
||||
const subject = this.getSubject(initializer, name);
|
||||
|
||||
var extra = "extra";
|
||||
const extra = "extra";
|
||||
subject[extra + 0] = extra;
|
||||
subject[extra + 1] = extra;
|
||||
subject[extra + 3] = extra;
|
||||
|
||||
var serializedSubject = this.serialize(subject, name);
|
||||
const serializedSubject = this.serialize(subject, name);
|
||||
|
||||
for (var field in serializedSubject) {
|
||||
for (const field in serializedSubject) {
|
||||
Assert.ok(subject.aiDataContract[field] != null, "The field '" + field + "' exists in the contract for '" + name + "' and was serialized");
|
||||
}
|
||||
}
|
||||
|
||||
private optionalFieldsAreNotRequired(initializer: () => any, name: string) {
|
||||
var subject = this.getSubject(this.initializer, this.name);
|
||||
const subject = this.getSubject(this.initializer, this.name);
|
||||
|
||||
for (var field in subject.aiDataContract) {
|
||||
for (const field in subject.aiDataContract) {
|
||||
if (!subject.aiDataContract[field]) {
|
||||
delete subject[field];
|
||||
}
|
||||
|
@ -108,13 +108,13 @@ class ContractTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
private allFieldsAreIncludedIfSpecified(initializer: () => any, name: string) {
|
||||
var subject = this.getSubject(this.initializer, this.name);
|
||||
const subject = this.getSubject(this.initializer, this.name);
|
||||
|
||||
for (var field in subject.aiDataContract) {
|
||||
for (const field in subject.aiDataContract) {
|
||||
subject[field] = field;
|
||||
}
|
||||
|
||||
var serializedSubject = this.serialize(subject, this.name);
|
||||
const serializedSubject = this.serialize(subject, this.name);
|
||||
|
||||
for (field in subject.aiDataContract) {
|
||||
Assert.ok(serializedSubject[field] === field, "Field '" + field + "' was not serialized" + this.name);
|
||||
|
@ -126,7 +126,7 @@ class ContractTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
private serialize(subject: Microsoft.ApplicationInsights.ISerializable, name: string) {
|
||||
var serialized = "";
|
||||
let serialized = "";
|
||||
|
||||
try {
|
||||
serialized = Microsoft.ApplicationInsights.Serializer.serialize(subject);
|
||||
|
@ -138,7 +138,7 @@ class ContractTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
private getSubject(construction: () => Microsoft.ApplicationInsights.ISerializable, name: string): any {
|
||||
var subject = construction();
|
||||
const subject = construction();
|
||||
Assert.ok(!!subject, "can construct " + name);
|
||||
|
||||
return subject;
|
||||
|
|
|
@ -53,12 +53,12 @@ class PerformanceTestHelper extends TestClass {
|
|||
this.useFakeTimers = false;
|
||||
this.clock.restore();
|
||||
|
||||
this.appInsights = new Microsoft.ApplicationInsights.AppInsights(<any>{
|
||||
this.appInsights = new Microsoft.ApplicationInsights.AppInsights({
|
||||
instrumentationKey: "3e6a441c-b52b-4f39-8944-f81dd6c2dc46",
|
||||
url: "file:///C:/src/sdk/src/JavaScript/JavaScriptSDK.Tests//E2ETests/ai.js",
|
||||
endpointUrl: "https://dc.services.visualstudio.com/v2/track",
|
||||
maxBatchInterval: 0
|
||||
});
|
||||
} as any);
|
||||
|
||||
this.appInsights.context._sender._sender = () => null;
|
||||
this.testProperties = { p1: "val", p2: "val", p3: "val", p4: "val", p5: "val", p6: "val", p7: "val" };
|
||||
|
@ -69,39 +69,14 @@ class PerformanceTestHelper extends TestClass {
|
|||
public testCleanup() {
|
||||
this.useFakeServer = true;
|
||||
this.useFakeTimers = true;
|
||||
var serializedPerfResults: string = window["perfResults"] || "[]";
|
||||
var perfResults: IPerfResult[] = <any>(JSON.parse(serializedPerfResults));
|
||||
const serializedPerfResults: string = window["perfResults"] || "[]";
|
||||
let perfResults: IPerfResult[] = (JSON.parse(serializedPerfResults)) as any;
|
||||
perfResults = perfResults.concat(this.results);
|
||||
window["perfResults"] = JSON.stringify(perfResults);
|
||||
window["perfResultsCsv"] = this.toCsv(perfResults).csv;
|
||||
window["perfResultsCsvHeaders"] = this.toCsv(perfResults).headers;
|
||||
}
|
||||
|
||||
private toCsv(array: any[]) {
|
||||
var headers = "";
|
||||
if (array.length > 0) {
|
||||
var names = [];
|
||||
for (var name in array[0]) {
|
||||
names.push(name);
|
||||
}
|
||||
|
||||
headers = names.join(",");
|
||||
}
|
||||
|
||||
var csv = [];
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
var datum = array[i];
|
||||
var values = [];
|
||||
for (var j = 0; j < names.length; j++) {
|
||||
values.push(datum[names[j]]);
|
||||
}
|
||||
|
||||
csv.push(values.join(","));
|
||||
}
|
||||
|
||||
return { headers: headers, csv: csv.join("\r\n") };
|
||||
}
|
||||
|
||||
public enqueueTest(name: string, action: () => void) {
|
||||
JSLitmus.test(name, (count) => {
|
||||
while (count--) {
|
||||
|
@ -115,7 +90,7 @@ class PerformanceTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
public onTestsComplete() {
|
||||
var perfLogging = new Microsoft.ApplicationInsights.AppInsights(<any>{
|
||||
const perfLogging = new Microsoft.ApplicationInsights.AppInsights({
|
||||
instrumentationKey: "1a6933ad-f260-447f-a2b0-e2233f6658eb",
|
||||
url: "file:///C:/src/sdk/src/JavaScript/JavaScriptSDK.Tests//E2ETests/ai.js",
|
||||
endpointUrl: "http://prodintdataforker.azurewebsites.net/dcservices?intKey=4d93aad0-cf1d-45b7-afc9-14f55504f6d5",
|
||||
|
@ -123,30 +98,30 @@ class PerformanceTestHelper extends TestClass {
|
|||
sessionExpirationMs: 24 * 60 * 60 * 1000,
|
||||
maxBatchSizeInBytes: 1000000,
|
||||
maxBatchInterval: 0
|
||||
});
|
||||
} as any);
|
||||
|
||||
perfLogging.context._sender._sender = (payload) => {
|
||||
var xhr = new sinon["xhr"].workingXHR();
|
||||
const xhr = new sinon["xhr"].workingXHR();
|
||||
xhr.open("POST", perfLogging.config.endpointUrl, true);
|
||||
xhr.setRequestHeader("Content-type", "application/json");
|
||||
xhr.send(payload);
|
||||
}
|
||||
|
||||
JSLitmus.stop();
|
||||
for (var i = 0; i < JSLitmus._tests.length; i++) {
|
||||
var test = JSLitmus._tests[i];
|
||||
var opsPerSec = test.count / test.time;
|
||||
for (let i = 0; i < JSLitmus._tests.length; i++) {
|
||||
const test = JSLitmus._tests[i];
|
||||
const opsPerSec = test.count / test.time;
|
||||
Assert.ok(true, test.name + " operations per sec:" + opsPerSec);
|
||||
|
||||
var timeInMs = <number>test.time;
|
||||
var date = +new Date;
|
||||
var oneHr = 60 * 60 * 1000;
|
||||
var oneHrDate = Math.floor(date / oneHr) * oneHr;
|
||||
var friendlyDate = new Date(oneHrDate).toISOString();
|
||||
var platform = <string>test.platform;
|
||||
var browser = "internetExplorer";
|
||||
var name = <string>test.name;
|
||||
var group = name.split(".")[0];
|
||||
const timeInMs = test.time as number;
|
||||
const date = +new Date;
|
||||
const oneHr = 60 * 60 * 1000;
|
||||
const oneHrDate = Math.floor(date / oneHr) * oneHr;
|
||||
const friendlyDate = new Date(oneHrDate).toISOString();
|
||||
const platform = test.platform as string;
|
||||
let browser = "internetExplorer";
|
||||
const name = test.name as string;
|
||||
const group = name.split(".")[0];
|
||||
if (platform.toLowerCase().indexOf("chrome") >= 0) {
|
||||
browser = "chrome";
|
||||
} else if (platform.toLowerCase().indexOf("firefox") >= 0) {
|
||||
|
@ -155,29 +130,29 @@ class PerformanceTestHelper extends TestClass {
|
|||
browser = "safari";
|
||||
}
|
||||
|
||||
var result: IPerfResult = {
|
||||
name: name,
|
||||
timeInMs: timeInMs,
|
||||
const result: IPerfResult = {
|
||||
name,
|
||||
timeInMs,
|
||||
operationCount: 1,
|
||||
opsPerSec: 1 / (timeInMs / 1000),
|
||||
period: 1,
|
||||
date: date,
|
||||
oneHrDate: oneHrDate,
|
||||
friendlyDate: friendlyDate,
|
||||
group: group,
|
||||
platform: platform,
|
||||
browser: browser,
|
||||
os: <string>test.os,
|
||||
date,
|
||||
oneHrDate,
|
||||
friendlyDate,
|
||||
group,
|
||||
platform,
|
||||
browser,
|
||||
os: test.os as string,
|
||||
millisecondsPerOp: (timeInMs / 1),
|
||||
microsecondsPerOp: (timeInMs / 1) * 1000,
|
||||
secondsPerOp: (timeInMs / 1) / 1000
|
||||
};
|
||||
|
||||
perfLogging.trackMetric(result.name, opsPerSec);
|
||||
var event = new Microsoft.ApplicationInsights.Telemetry.Event(result.name, opsPerSec, result);
|
||||
var data = new Microsoft.ApplicationInsights.Telemetry.Common.Data<Microsoft.ApplicationInsights.Telemetry.Event>(
|
||||
const event = new Microsoft.ApplicationInsights.Telemetry.Event(result.name, opsPerSec, result);
|
||||
const data = new Microsoft.ApplicationInsights.Telemetry.Common.Data<Microsoft.ApplicationInsights.Telemetry.Event>(
|
||||
Microsoft.ApplicationInsights.Telemetry.Event.dataType, event);
|
||||
var envelope = new Microsoft.ApplicationInsights.Telemetry.Common.Envelope(data, Microsoft.ApplicationInsights.Telemetry.Event.envelopeType);
|
||||
const envelope = new Microsoft.ApplicationInsights.Telemetry.Common.Envelope(data, Microsoft.ApplicationInsights.Telemetry.Event.envelopeType);
|
||||
perfLogging.context.track(envelope);
|
||||
|
||||
this.results.push(result);
|
||||
|
@ -195,6 +170,31 @@ class PerformanceTestHelper extends TestClass {
|
|||
}
|
||||
}
|
||||
|
||||
private toCsv(array: any[]) {
|
||||
let headers = "";
|
||||
if (array.length > 0) {
|
||||
const names = [];
|
||||
for (const name in array[0]) {
|
||||
names.push(name);
|
||||
}
|
||||
|
||||
headers = names.join(",");
|
||||
}
|
||||
|
||||
const csv = [];
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const datum = array[i];
|
||||
const values = [];
|
||||
for (let j = 0; j < names.length; j++) {
|
||||
values.push(datum[names[j]]);
|
||||
}
|
||||
|
||||
csv.push(values.join(","));
|
||||
}
|
||||
|
||||
return { headers, csv: csv.join("\r\n") };
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously loads jquery
|
||||
* we could regress the test suite and develop sublte jquery dependencies in the product code
|
||||
|
@ -204,11 +204,11 @@ class PerformanceTestHelper extends TestClass {
|
|||
private synchronouslyLoadJquery() {
|
||||
if (!window["$"]) {
|
||||
// get some kind of XMLHttpRequest
|
||||
var xhrObj = <any>false;
|
||||
let xhrObj = false as any;
|
||||
if (window["ActiveXObject"]) {
|
||||
xhrObj = <any>new ActiveXObject("Microsoft.XMLHTTP");
|
||||
xhrObj = (new ActiveXObject("Microsoft.XMLHTTP") as any);
|
||||
} else if (window["XMLHttpRequest"]) {
|
||||
xhrObj = <any>new XMLHttpRequest();
|
||||
xhrObj = (new XMLHttpRequest() as any);
|
||||
} else {
|
||||
alert("Please upgrade your browser! Your browser does not support AJAX!");
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ class PerformanceTestHelper extends TestClass {
|
|||
xhrObj.send('');
|
||||
|
||||
// add the returned content to a newly created script tag
|
||||
var script = document.createElement('script');
|
||||
const script = document.createElement('script');
|
||||
script.type = "text/javascript";
|
||||
script.text = xhrObj.responseText;
|
||||
document.getElementsByTagName('head')[0].appendChild(script);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/// <reference path="TestClass.ts" />
|
||||
|
||||
class PollingAssert {
|
||||
/**
|
||||
/**
|
||||
* Starts polling assertion function for a period of time after which it's considered failed.
|
||||
* @param {() => boolean} assertionFunctionReturnsBoolean - funciton returning true if condition passes and false if condition fails. Assertion will be done on this function's result.
|
||||
* @param {string} assertDescription - message shown with the assertion
|
||||
|
@ -11,9 +11,9 @@ class PollingAssert {
|
|||
* @returns {(nextTestStep) => void} callback which will be invoked by the TestClass
|
||||
*/
|
||||
public static createPollingAssert(assertionFunctionReturnsBoolean: () => boolean, assertDescription: string, timeoutSeconds: number = 30, pollIntervalMs: number = 500): (nextTestStep) => void {
|
||||
var pollingAssert = (nextTestStep) => {
|
||||
var timeout = new Date(new Date().getTime() + timeoutSeconds * 1000);
|
||||
var polling = () => {
|
||||
const pollingAssert = (nextTestStep) => {
|
||||
const timeout = new Date(new Date().getTime() + timeoutSeconds * 1000);
|
||||
const polling = () => {
|
||||
if (assertionFunctionReturnsBoolean.apply(this)) {
|
||||
Assert.ok(true, assertDescription);
|
||||
nextTestStep();
|
||||
|
|
|
@ -5,10 +5,6 @@
|
|||
|
||||
class TestClass {
|
||||
|
||||
constructor(name?: string) {
|
||||
QUnit.module(name);
|
||||
}
|
||||
|
||||
public static isPollingStepFlag = "isPollingStep";
|
||||
|
||||
/** The instance of the currently running suite. */
|
||||
|
@ -20,6 +16,18 @@ class TestClass {
|
|||
/** Turns on/off sinon's fake implementation of XMLHttpRequest. On by default. */
|
||||
public useFakeServer: boolean = true;
|
||||
|
||||
/**** Sinon methods and properties ***/
|
||||
|
||||
// These methods and properties are injected by Sinon and will override the implementation here.
|
||||
// These are here purely to make typescript happy.
|
||||
public clock: SinonFakeTimers;
|
||||
public server: SinonFakeServer;
|
||||
public sandbox: SinonSandbox;
|
||||
|
||||
constructor(name?: string) {
|
||||
QUnit.module(name);
|
||||
}
|
||||
|
||||
/** Method called before the start of each test method */
|
||||
public testInitialize() {
|
||||
}
|
||||
|
@ -47,8 +55,8 @@ class TestClass {
|
|||
}
|
||||
|
||||
// Create a wrapper around the test method so we can do test initilization and cleanup.
|
||||
var testMethod = (assert) => {
|
||||
var done = assert.async();
|
||||
const testMethod = (assert) => {
|
||||
const done = assert.async();
|
||||
|
||||
// Save off the instance of the currently running suite.
|
||||
TestClass.currentTestClass = this;
|
||||
|
@ -57,13 +65,13 @@ class TestClass {
|
|||
try {
|
||||
this._testStarting();
|
||||
|
||||
var steps = testInfo.steps;
|
||||
var trigger = () => {
|
||||
const steps = testInfo.steps;
|
||||
const trigger = () => {
|
||||
if (steps.length) {
|
||||
var step = steps.shift();
|
||||
const step = steps.shift();
|
||||
|
||||
// The callback which activates the next test step.
|
||||
var nextTestStepTrigger = () => {
|
||||
const nextTestStepTrigger = () => {
|
||||
setTimeout(() => {
|
||||
trigger();
|
||||
}, testInfo.stepDelay);
|
||||
|
@ -122,7 +130,7 @@ class TestClass {
|
|||
}
|
||||
|
||||
// Create a wrapper around the test method so we can do test initilization and cleanup.
|
||||
var testMethod = () => {
|
||||
const testMethod = () => {
|
||||
// Save off the instance of the currently running suite.
|
||||
TestClass.currentTestClass = this;
|
||||
|
||||
|
@ -144,46 +152,6 @@ class TestClass {
|
|||
test(testInfo.name, testMethod);
|
||||
}
|
||||
|
||||
/** Called when the test is starting. */
|
||||
private _testStarting() {
|
||||
// Initialize the sandbox similar to what is done in sinon.js "test()" override. See note on class.
|
||||
var config = (<any>sinon).getConfig(sinon.config);
|
||||
config.useFakeTimers = this.useFakeTimers;
|
||||
config.useFakeServer = this.useFakeServer;
|
||||
|
||||
config.injectInto = config.injectIntoThis && this || config.injectInto;
|
||||
this.sandbox = sinon.sandbox.create(config);
|
||||
this.server = this.sandbox.server;
|
||||
|
||||
// Allow the derived class to perform test initialization.
|
||||
this.testInitialize();
|
||||
}
|
||||
|
||||
/** Called when the test is completed. */
|
||||
private _testCompleted(failed?: boolean) {
|
||||
if (failed) {
|
||||
// Just cleanup the sandbox since the test has already failed.
|
||||
this.sandbox.restore();
|
||||
}
|
||||
else {
|
||||
// Verify the sandbox and restore.
|
||||
(<any>this.sandbox).verifyAndRestore();
|
||||
}
|
||||
|
||||
this.testCleanup();
|
||||
|
||||
// Clear the instance of the currently running suite.
|
||||
TestClass.currentTestClass = null;
|
||||
}
|
||||
|
||||
/**** Sinon methods and properties ***/
|
||||
|
||||
// These methods and properties are injected by Sinon and will override the implementation here.
|
||||
// These are here purely to make typescript happy.
|
||||
public clock: SinonFakeTimers;
|
||||
public server: SinonFakeServer;
|
||||
public sandbox: SinonSandbox;
|
||||
|
||||
/** Creates an anonymous function that records arguments, this value, exceptions and return values for all calls. */
|
||||
public spy(): SinonSpy;
|
||||
/** Spies on the provided function */
|
||||
|
@ -205,11 +173,12 @@ class TestClass {
|
|||
|
||||
/**** end: Sinon methods and properties ***/
|
||||
|
||||
/** Sends a JSON response to the provided request.
|
||||
/**
|
||||
* Sends a JSON response to the provided request.
|
||||
* @param request The request to respond to.
|
||||
* @param data Data to respond with.
|
||||
* @param errorCode Optional error code to send with the request, default is 200
|
||||
*/
|
||||
*/
|
||||
public sendJsonResponse(request: SinonFakeXMLHttpRequest, data: any, errorCode?: number) {
|
||||
if (errorCode === undefined) {
|
||||
errorCode = 200;
|
||||
|
@ -225,19 +194,51 @@ class TestClass {
|
|||
Object.defineProperty(window.navigator, 'userAgent',
|
||||
{
|
||||
configurable: true,
|
||||
get: function () {
|
||||
get () {
|
||||
return userAgent;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Called when the test is starting. */
|
||||
private _testStarting() {
|
||||
// Initialize the sandbox similar to what is done in sinon.js "test()" override. See note on class.
|
||||
const config = (sinon as any).getConfig(sinon.config);
|
||||
config.useFakeTimers = this.useFakeTimers;
|
||||
config.useFakeServer = this.useFakeServer;
|
||||
|
||||
config.injectInto = config.injectIntoThis && this || config.injectInto;
|
||||
this.sandbox = sinon.sandbox.create(config);
|
||||
this.server = this.sandbox.server;
|
||||
|
||||
// Allow the derived class to perform test initialization.
|
||||
this.testInitialize();
|
||||
}
|
||||
|
||||
/** Called when the test is completed. */
|
||||
private _testCompleted(failed?: boolean) {
|
||||
if (failed) {
|
||||
// Just cleanup the sandbox since the test has already failed.
|
||||
this.sandbox.restore();
|
||||
}
|
||||
else {
|
||||
// Verify the sandbox and restore.
|
||||
(this.sandbox as any).verifyAndRestore();
|
||||
}
|
||||
|
||||
this.testCleanup();
|
||||
|
||||
// Clear the instance of the currently running suite.
|
||||
TestClass.currentTestClass = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure Sinon
|
||||
sinon.assert.fail = function (msg?) {
|
||||
sinon.assert.fail = (msg?) => {
|
||||
Assert.ok(false, msg);
|
||||
};
|
||||
|
||||
sinon.assert.pass = function (assertion) {
|
||||
sinon.assert.pass = (assertion) => {
|
||||
Assert.ok(assertion, "sinon assert");
|
||||
};
|
||||
|
||||
|
|
|
@ -32,39 +32,6 @@ const durationProperty: string = "duration";
|
|||
|
||||
export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IAppInsightsInternal {
|
||||
public static Version = "2.2.2"; // Not currently used anywhere
|
||||
public initialize: (config: IConfiguration, core: IAppInsightsCore, extensions: IPlugin[]) => void;
|
||||
public identifier: string = "ApplicationInsightsAnalytics"; // do not change name or priority
|
||||
public priority: number = 180; // take from reserved priority range 100- 200
|
||||
public config: IConfig;
|
||||
public core: IAppInsightsCore;
|
||||
public queue: (() => void)[];
|
||||
public autoRoutePVDelay = 500; // ms; Time to wait after a route change before triggering a pageview to allow DOM changes to take place
|
||||
|
||||
private _isInitialized: boolean = false;
|
||||
private _globalconfig: IConfiguration;
|
||||
private _eventTracking: Timing;
|
||||
private _pageTracking: Timing;
|
||||
private _properties: properties.PropertiesPlugin;
|
||||
protected _nextPlugin: ITelemetryPlugin;
|
||||
protected _logger: IDiagnosticLogger; // Initialized by Core
|
||||
protected _telemetryInitializers: { (envelope: ITelemetryItem): boolean | void; }[]; // Internal telemetry initializers.
|
||||
protected _pageViewManager: PageViewManager;
|
||||
protected _pageViewPerformanceManager: PageViewPerformanceManager;
|
||||
protected _pageVisitTimeManager: PageVisitTimeManager;
|
||||
|
||||
// Counts number of trackAjax invokations.
|
||||
// By default we only monitor X ajax call per view to avoid too much load.
|
||||
// Default value is set in config.
|
||||
// This counter keeps increasing even after the limit is reached.
|
||||
private _trackAjaxAttempts: number = 0;
|
||||
|
||||
// array with max length of 2 that store current url and previous url for SPA page route change trackPageview use.
|
||||
private _prevUri: string = typeof window === "object" && window.location && window.location.href || "";
|
||||
private _currUri: string;
|
||||
|
||||
constructor() {
|
||||
this.initialize = this._initialize.bind(this);
|
||||
}
|
||||
|
||||
public static getDefaultConfig(config?: IConfig): IConfig {
|
||||
if (!config) {
|
||||
|
@ -90,13 +57,46 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
|
||||
return config;
|
||||
}
|
||||
public initialize: (config: IConfiguration, core: IAppInsightsCore, extensions: IPlugin[]) => void;
|
||||
public identifier: string = "ApplicationInsightsAnalytics"; // do not change name or priority
|
||||
public priority: number = 180; // take from reserved priority range 100- 200
|
||||
public config: IConfig;
|
||||
public core: IAppInsightsCore;
|
||||
public queue: Array<() => void>;
|
||||
public autoRoutePVDelay = 500; // ms; Time to wait after a route change before triggering a pageview to allow DOM changes to take place
|
||||
protected _nextPlugin: ITelemetryPlugin;
|
||||
protected _logger: IDiagnosticLogger; // Initialized by Core
|
||||
protected _telemetryInitializers: Array<(envelope: ITelemetryItem) => boolean | void>; // Internal telemetry initializers.
|
||||
protected _pageViewManager: PageViewManager;
|
||||
protected _pageViewPerformanceManager: PageViewPerformanceManager;
|
||||
protected _pageVisitTimeManager: PageVisitTimeManager;
|
||||
|
||||
private _isInitialized: boolean = false;
|
||||
private _globalconfig: IConfiguration;
|
||||
private _eventTracking: Timing;
|
||||
private _pageTracking: Timing;
|
||||
private _properties: properties.PropertiesPlugin;
|
||||
|
||||
// Counts number of trackAjax invokations.
|
||||
// By default we only monitor X ajax call per view to avoid too much load.
|
||||
// Default value is set in config.
|
||||
// This counter keeps increasing even after the limit is reached.
|
||||
private _trackAjaxAttempts: number = 0;
|
||||
|
||||
// array with max length of 2 that store current url and previous url for SPA page route change trackPageview use.
|
||||
private _prevUri: string = typeof window === "object" && window.location && window.location.href || "";
|
||||
private _currUri: string;
|
||||
|
||||
constructor() {
|
||||
this.initialize = this._initialize.bind(this);
|
||||
}
|
||||
|
||||
|
||||
public processTelemetry(env: ITelemetryItem) {
|
||||
var doNotSendItem = false;
|
||||
var telemetryInitializersCount = this._telemetryInitializers.length;
|
||||
for (var i = 0; i < telemetryInitializersCount; ++i) {
|
||||
var telemetryInitializer = this._telemetryInitializers[i];
|
||||
let doNotSendItem = false;
|
||||
const telemetryInitializersCount = this._telemetryInitializers.length;
|
||||
for (let i = 0; i < telemetryInitializersCount; ++i) {
|
||||
const telemetryInitializer = this._telemetryInitializers[i];
|
||||
if (telemetryInitializer) {
|
||||
try {
|
||||
if (telemetryInitializer.apply(null, [env]) === false) {
|
||||
|
@ -124,7 +124,7 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
|
||||
public trackEvent(event: IEventTelemetry, customProperties?: ICustomProperties): void {
|
||||
try {
|
||||
let telemetryItem = TelemetryItemCreator.create<IEventTelemetry>(
|
||||
const telemetryItem = TelemetryItemCreator.create<IEventTelemetry>(
|
||||
event,
|
||||
EventTelemetry.dataType,
|
||||
EventTelemetry.envelopeType,
|
||||
|
@ -142,9 +142,9 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
}
|
||||
|
||||
/**
|
||||
* Start timing an extended event. Call `stopTrackEvent` to log the event when it ends.
|
||||
* @param name A string that identifies this event uniquely within the document.
|
||||
*/
|
||||
* Start timing an extended event. Call `stopTrackEvent` to log the event when it ends.
|
||||
* @param name A string that identifies this event uniquely within the document.
|
||||
*/
|
||||
public startTrackEvent(name: string) {
|
||||
try {
|
||||
this._eventTracking.start(name);
|
||||
|
@ -181,7 +181,7 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
*/
|
||||
public trackTrace(trace: ITraceTelemetry, customProperties?: ICustomProperties): void {
|
||||
try {
|
||||
let telemetryItem = TelemetryItemCreator.create<ITraceTelemetry>(
|
||||
const telemetryItem = TelemetryItemCreator.create<ITraceTelemetry>(
|
||||
trace,
|
||||
Trace.dataType,
|
||||
Trace.envelopeType,
|
||||
|
@ -210,7 +210,7 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
*/
|
||||
public trackMetric(metric: IMetricTelemetry, customProperties?: ICustomProperties): void {
|
||||
try {
|
||||
var telemetryItem = TelemetryItemCreator.create<IMetricTelemetry>(
|
||||
const telemetryItem = TelemetryItemCreator.create<IMetricTelemetry>(
|
||||
metric,
|
||||
Metric.dataType,
|
||||
Metric.envelopeType,
|
||||
|
@ -261,7 +261,7 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
pageView.refUri = pageView.refUri === undefined ? document.referrer : pageView.refUri;
|
||||
}
|
||||
|
||||
let telemetryItem = TelemetryItemCreator.create<IPageViewTelemetryInternal>(
|
||||
const telemetryItem = TelemetryItemCreator.create<IPageViewTelemetryInternal>(
|
||||
pageView,
|
||||
PageView.dataType,
|
||||
PageView.envelopeType,
|
||||
|
@ -281,7 +281,7 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
* @param properties
|
||||
*/
|
||||
public sendPageViewPerformanceInternal(pageViewPerformance: IPageViewPerformanceTelemetryInternal, properties?: { [key: string]: any }, systemProperties?: { [key: string]: any }) {
|
||||
let telemetryItem = TelemetryItemCreator.create<IPageViewPerformanceTelemetryInternal>(
|
||||
const telemetryItem = TelemetryItemCreator.create<IPageViewPerformanceTelemetryInternal>(
|
||||
pageViewPerformance,
|
||||
PageViewPerformance.dataType,
|
||||
PageViewPerformance.envelopeType,
|
||||
|
@ -365,7 +365,7 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @ignore INTERNAL ONLY
|
||||
* @param exception
|
||||
* @param properties
|
||||
|
@ -381,7 +381,7 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
exception.id
|
||||
).toInterface();
|
||||
|
||||
let telemetryItem: ITelemetryItem = TelemetryItemCreator.create<IExceptionInternal>(
|
||||
const telemetryItem: ITelemetryItem = TelemetryItemCreator.create<IExceptionInternal>(
|
||||
exceptionPartB,
|
||||
Exception.dataType,
|
||||
Exception.envelopeType,
|
||||
|
@ -447,7 +447,7 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
_InternalMessageId.ExceptionWhileLoggingError,
|
||||
"_onError threw exception while logging error, error will not be collected: "
|
||||
+ Util.getExceptionName(e),
|
||||
{ exception: Util.dump(e), errorString: errorString }
|
||||
{ exception: Util.dump(e), errorString }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -473,18 +473,18 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
endpointUrl: config.endpointUrl || "https://dc.services.visualstudio.com/v2/track"
|
||||
};
|
||||
|
||||
this.config = config.extensionConfig && config.extensionConfig[this.identifier] ? config.extensionConfig[this.identifier] : <IConfig>{};
|
||||
this.config = config.extensionConfig && config.extensionConfig[this.identifier] ? config.extensionConfig[this.identifier] : {} as IConfig;
|
||||
|
||||
// load default values if specified
|
||||
var defaults: IConfig = ApplicationInsights.getDefaultConfig();
|
||||
const defaults: IConfig = ApplicationInsights.getDefaultConfig();
|
||||
if (defaults !== undefined) {
|
||||
for (var field in defaults) {
|
||||
for (const field in defaults) {
|
||||
// for each unspecified field, set the default value
|
||||
this.config[field] = ConfigurationManager.getConfig(config, field, this.identifier, defaults[field]);
|
||||
}
|
||||
|
||||
if (this._globalconfig) {
|
||||
for (var field in defaults) {
|
||||
for (const field in defaults) {
|
||||
if (this._globalconfig[field] === undefined) {
|
||||
this._globalconfig[field] = defaults[field];
|
||||
}
|
||||
|
@ -502,7 +502,7 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
Util.disableStorage();
|
||||
}
|
||||
|
||||
var configGetters: ITelemetryConfig = {
|
||||
const configGetters: ITelemetryConfig = {
|
||||
instrumentationKey: () => config.instrumentationKey,
|
||||
accountId: () => this.config.accountId || config.accountId,
|
||||
sessionRenewalMs: () => this.config.sessionRenewalMs || config.sessionRenewalMs,
|
||||
|
@ -530,7 +530,7 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
}
|
||||
|
||||
properties[durationProperty] = duration.toString();
|
||||
this.trackEvent(<IEventTelemetry>{ name: name, properties: properties });
|
||||
this.trackEvent({ name, properties } as IEventTelemetry);
|
||||
}
|
||||
|
||||
// initialize page view timing
|
||||
|
@ -543,11 +543,11 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
}
|
||||
properties[durationProperty] = duration.toString();
|
||||
|
||||
let pageViewItem: IPageViewTelemetry = {
|
||||
name: name,
|
||||
const pageViewItem: IPageViewTelemetry = {
|
||||
name,
|
||||
uri: url,
|
||||
properties: properties,
|
||||
measurements: measurements
|
||||
properties,
|
||||
measurements
|
||||
};
|
||||
|
||||
this.sendPageViewInternal(pageViewItem);
|
||||
|
@ -559,15 +559,15 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
// We want to enable exception auto collection and it has not been done so yet
|
||||
const onerror = "onerror";
|
||||
const originalOnError = window[onerror];
|
||||
window.onerror = function (message, url, lineNumber, columnNumber, error) {
|
||||
const handled = originalOnError && <any>originalOnError(message, url, lineNumber, columnNumber, error);
|
||||
window.onerror = (message, url, lineNumber, columnNumber, error) => {
|
||||
const handled = originalOnError && (originalOnError(message, url, lineNumber, columnNumber, error) as any);
|
||||
if (handled !== true) { // handled could be typeof function
|
||||
instance._onerror({
|
||||
message: message,
|
||||
url: url,
|
||||
lineNumber: lineNumber,
|
||||
columnNumber: columnNumber,
|
||||
error: error
|
||||
message,
|
||||
url,
|
||||
lineNumber,
|
||||
columnNumber,
|
||||
error
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -592,14 +592,14 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
});
|
||||
|
||||
history.pushState = ( f => function pushState() {
|
||||
var ret = f.apply(this, arguments);
|
||||
const ret = f.apply(this, arguments);
|
||||
window.dispatchEvent(Util.createDomEvent(_self.config.namePrefix + "pushState"));
|
||||
window.dispatchEvent(Util.createDomEvent(_self.config.namePrefix + "locationchange"));
|
||||
return ret;
|
||||
})(history.pushState);
|
||||
|
||||
history.replaceState = ( f => function replaceState(){
|
||||
var ret = f.apply(this, arguments);
|
||||
const ret = f.apply(this, arguments);
|
||||
window.dispatchEvent(Util.createDomEvent(_self.config.namePrefix + "replaceState"));
|
||||
window.dispatchEvent(Util.createDomEvent(_self.config.namePrefix + "locationchange"));
|
||||
return ret;
|
||||
|
@ -636,7 +636,7 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
* @param pageVisitDuration Duration of visit to the page in milleseconds
|
||||
*/
|
||||
private trackPageVisitTime(pageName: string, pageUrl: string, pageVisitTime: number) {
|
||||
var properties = { PageName: pageName, PageUrl: pageUrl };
|
||||
const properties = { PageName: pageName, PageUrl: pageUrl };
|
||||
this.trackMetric({
|
||||
name: "PageVisitTime",
|
||||
average: pageVisitTime,
|
||||
|
@ -649,9 +649,9 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
private _addDefaultTelemetryInitializers(configGetters: ITelemetryConfig) {
|
||||
if (!configGetters.isBrowserLinkTrackingEnabled()) {
|
||||
const browserLinkPaths = ['/browserLinkSignalR/', '/__browserLink/'];
|
||||
let dropBrowserLinkRequests = (envelope: ITelemetryItem) => {
|
||||
const dropBrowserLinkRequests = (envelope: ITelemetryItem) => {
|
||||
if (envelope.baseType === RemoteDependencyData.dataType) {
|
||||
let remoteData = envelope.baseData as IDependencyTelemetry;
|
||||
const remoteData = envelope.baseData as IDependencyTelemetry;
|
||||
if (remoteData) {
|
||||
for (let i = 0; i < browserLinkPaths.length; i++) {
|
||||
if (remoteData.target && remoteData.target.indexOf(browserLinkPaths[i]) >= 0) {
|
||||
|
@ -675,7 +675,7 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
private _sendCORSException(url: string) {
|
||||
const exception: IAutoExceptionTelemetry = {
|
||||
message: "Script error: The browser's same-origin policy prevents us from getting the details of this exception. Consider using the 'crossorigin' attribute.",
|
||||
url: url,
|
||||
url,
|
||||
lineNumber: 0,
|
||||
columnNumber: 0,
|
||||
error: undefined
|
||||
|
@ -685,7 +685,7 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
Exception.dataType,
|
||||
Exception.envelopeType,
|
||||
this._logger,
|
||||
{ url: url }
|
||||
{ url }
|
||||
);
|
||||
|
||||
this.core.track(telemetryItem);
|
||||
|
@ -696,6 +696,8 @@ export class ApplicationInsights implements IAppInsights, ITelemetryPlugin, IApp
|
|||
* Used to record timed events and page views.
|
||||
*/
|
||||
class Timing {
|
||||
|
||||
public action: (name?: string, url?: string, duration?: number, properties?: { [key: string]: string }, measurements?: { [key: string]: number }) => void;
|
||||
private _name;
|
||||
private _events: {
|
||||
[key: string]: number;
|
||||
|
@ -719,20 +721,18 @@ class Timing {
|
|||
}
|
||||
|
||||
public stop(name: string, url: string, properties?: { [key: string]: string }, measurements?: { [key: string]: number }) {
|
||||
var start = this._events[name];
|
||||
const start = this._events[name];
|
||||
if (isNaN(start)) {
|
||||
this._logger.throwInternal(
|
||||
LoggingSeverity.WARNING, _InternalMessageId.StopCalledWithoutStart, "stop was called without a corresponding start.",
|
||||
{ name: this._name, key: name }, true);
|
||||
} else {
|
||||
var end = +new Date;
|
||||
var duration = DateTimeUtils.GetDuration(start, end);
|
||||
const end = +new Date;
|
||||
const duration = DateTimeUtils.GetDuration(start, end);
|
||||
this.action(name, url, duration, properties, measurements);
|
||||
}
|
||||
|
||||
delete this._events[name];
|
||||
this._events[name] = undefined;
|
||||
}
|
||||
|
||||
public action: (name?: string, url?: string, duration?: number, properties?: { [key: string]: string }, measurements?: { [key: string]: number }) => void;
|
||||
}
|
||||
|
|
|
@ -11,16 +11,16 @@ import {
|
|||
import { PageViewPerformanceManager } from './PageViewPerformanceManager';
|
||||
|
||||
/**
|
||||
* Internal interface to pass appInsights object to subcomponents without coupling
|
||||
*/
|
||||
* Internal interface to pass appInsights object to subcomponents without coupling
|
||||
*/
|
||||
export interface IAppInsightsInternal {
|
||||
sendPageViewInternal(pageViewItem: IPageViewTelemetryInternal, properties?: Object, systemProperties?: Object);
|
||||
sendPageViewPerformanceInternal(pageViewPerformance: IPageViewPerformanceTelemetryInternal, properties?: Object, systemProperties?: Object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class encapsulates sending page views and page view performance telemetry.
|
||||
*/
|
||||
* Class encapsulates sending page views and page view performance telemetry.
|
||||
*/
|
||||
export class PageViewManager {
|
||||
private pageViewPerformanceSent: boolean = false;
|
||||
|
||||
|
@ -38,13 +38,13 @@ export class PageViewManager {
|
|||
this.appInsights = appInsights;
|
||||
this._pageViewPerformanceManager = pageViewPerformanceManager;
|
||||
if (core) {
|
||||
this._channel = () => <IChannelControls[][]>(core.getTransmissionControls());
|
||||
this._channel = () => (core.getTransmissionControls()) as IChannelControls[][];
|
||||
this._logger = core.logger;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Currently supported cases:
|
||||
* 1) (default case) track page view called with default parameters, overridePageViewDuration = false. Page view is sent with page view performance when navigation timing data is available.
|
||||
* a. If navigation timing is not supported then page view is sent right away with undefined duration. Page view performance is not sent.
|
||||
|
@ -55,12 +55,12 @@ export class PageViewManager {
|
|||
* In all cases page view performance is sent once (only for the 1st call of trackPageView), or not sent if navigation timing is not supported.
|
||||
*/
|
||||
public trackPageView(pageView: IPageViewTelemetry, customProperties?: { [key: string]: any }) {
|
||||
let name = pageView.name;
|
||||
const name = pageView.name;
|
||||
if (CoreUtils.isNullOrUndefined(name) || typeof name !== "string") {
|
||||
pageView.name = window.document && window.document.title || "";
|
||||
}
|
||||
|
||||
let uri = pageView.uri;
|
||||
const uri = pageView.uri;
|
||||
if (CoreUtils.isNullOrUndefined(uri) || typeof uri !== "string") {
|
||||
pageView.uri = window.location && window.location.href || "";
|
||||
}
|
||||
|
@ -84,11 +84,11 @@ export class PageViewManager {
|
|||
return;
|
||||
}
|
||||
|
||||
var pageViewSent = false;
|
||||
var customDuration = undefined;
|
||||
let pageViewSent = false;
|
||||
let customDuration;
|
||||
|
||||
// if the performance timing is supported by the browser, calculate the custom duration
|
||||
var start = this._pageViewPerformanceManager.getPerformanceTiming().navigationStart;
|
||||
const start = this._pageViewPerformanceManager.getPerformanceTiming().navigationStart;
|
||||
customDuration = DateTimeUtils.GetDuration(start, +new Date);
|
||||
if (!this._pageViewPerformanceManager.shouldCollectDuration(customDuration)) {
|
||||
customDuration = undefined;
|
||||
|
@ -96,7 +96,7 @@ export class PageViewManager {
|
|||
|
||||
// if the user has provided duration, send a page view telemetry with the provided duration. Otherwise, if
|
||||
// overridePageViewDuration is set to true, send a page view telemetry with the custom duration calculated earlier
|
||||
let duration = undefined;
|
||||
let duration;
|
||||
if (!CoreUtils.isNullOrUndefined(customProperties) &&
|
||||
!CoreUtils.isNullOrUndefined(customProperties.duration)) {
|
||||
duration = customProperties.duration;
|
||||
|
@ -120,17 +120,17 @@ export class PageViewManager {
|
|||
}
|
||||
|
||||
// now try to send the page view performance telemetry
|
||||
var maxDurationLimit = 60000;
|
||||
const maxDurationLimit = 60000;
|
||||
if (!customProperties) {
|
||||
customProperties = {};
|
||||
}
|
||||
var handle = setInterval((() => {
|
||||
const handle = setInterval((() => {
|
||||
try {
|
||||
if (this._pageViewPerformanceManager.isPerformanceTimingDataReady()) {
|
||||
clearInterval(handle);
|
||||
let pageViewPerformance: IPageViewPerformanceTelemetryInternal = {
|
||||
name: name,
|
||||
uri: uri
|
||||
const pageViewPerformance: IPageViewPerformanceTelemetryInternal = {
|
||||
name,
|
||||
uri
|
||||
};
|
||||
this._pageViewPerformanceManager.populatePageViewPerformanceEvent(pageViewPerformance);
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ import {
|
|||
} from '@microsoft/applicationinsights-core-js';
|
||||
|
||||
/**
|
||||
* Class encapsulates sending page view performance telemetry.
|
||||
*/
|
||||
* Class encapsulates sending page view performance telemetry.
|
||||
*/
|
||||
export class PageViewPerformanceManager {
|
||||
private _logger: IDiagnosticLogger;
|
||||
private MAX_DURATION_ALLOWED = 3600000; // 1h
|
||||
|
@ -38,8 +38,8 @@ export class PageViewPerformanceManager {
|
|||
* |---network---||---request---|---response---|---dom---|
|
||||
* |--------------------------total----------------------|
|
||||
*/
|
||||
var navigationTiming = this.getPerformanceNavigationTiming();
|
||||
var timing = this.getPerformanceTiming();
|
||||
const navigationTiming = this.getPerformanceNavigationTiming();
|
||||
const timing = this.getPerformanceTiming();
|
||||
if (navigationTiming || timing) {
|
||||
if (navigationTiming) {
|
||||
var total = navigationTiming.duration;
|
||||
|
@ -55,19 +55,19 @@ export class PageViewPerformanceManager {
|
|||
var dom = DateTimeUtils.GetDuration(timing.responseEnd, timing.loadEventEnd);
|
||||
}
|
||||
|
||||
if (total == 0) {
|
||||
if (total === 0) {
|
||||
this._logger.throwInternal(
|
||||
LoggingSeverity.WARNING,
|
||||
_InternalMessageId.ErrorPVCalc,
|
||||
"error calculating page view performance.",
|
||||
{ total: total, network: network, request: request, response: response, dom: dom });
|
||||
{ total, network, request, response, dom });
|
||||
|
||||
} else if (!this.shouldCollectDuration(total, network, request, response, dom)) {
|
||||
this._logger.throwInternal(
|
||||
LoggingSeverity.WARNING,
|
||||
_InternalMessageId.InvalidDurationValue,
|
||||
"Invalid page load duration value. Browser perf data won't be sent.",
|
||||
{ total: total, network: network, request: request, response: response, dom: dom });
|
||||
{ total, network, request, response, dom });
|
||||
|
||||
} else if (total < Math.floor(network) + Math.floor(request) + Math.floor(response) + Math.floor(dom)) {
|
||||
// some browsers may report individual components incorrectly so that the sum of the parts will be bigger than total PLT
|
||||
|
@ -76,7 +76,7 @@ export class PageViewPerformanceManager {
|
|||
LoggingSeverity.WARNING,
|
||||
_InternalMessageId.ClientPerformanceMathError,
|
||||
"client performance math error.",
|
||||
{ total: total, network: network, request: request, response: response, dom: dom });
|
||||
{ total, network, request, response, dom });
|
||||
|
||||
} else {
|
||||
pageViewPerformance.durationMs = total;
|
||||
|
@ -106,26 +106,26 @@ export class PageViewPerformanceManager {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Returns true is window PerformanceNavigationTiming API is supported, false otherwise.
|
||||
*/
|
||||
public isPerformanceNavigationTimingSupported() {
|
||||
return typeof window != "undefined" && window.performance && window.performance.getEntriesByType && window.performance.getEntriesByType("navigation").length > 0;
|
||||
return typeof window !== "undefined" && window.performance && window.performance.getEntriesByType && window.performance.getEntriesByType("navigation").length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Returns true is window performance timing API is supported, false otherwise.
|
||||
*/
|
||||
public isPerformanceTimingSupported() {
|
||||
return typeof window != "undefined" && window.performance && window.performance.timing;
|
||||
return typeof window !== "undefined" && window.performance && window.performance.timing;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* As page loads different parts of performance timing numbers get set. When all of them are set we can report it.
|
||||
* Returns true if ready, false otherwise.
|
||||
*/
|
||||
public isPerformanceTimingDataReady() {
|
||||
var timing = window.performance.timing;
|
||||
const timing = window.performance.timing;
|
||||
|
||||
return timing.domainLookupStart > 0
|
||||
&& timing.navigationStart > 0
|
||||
|
@ -137,13 +137,13 @@ export class PageViewPerformanceManager {
|
|||
&& timing.domLoading > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* This method tells if given durations should be excluded from collection.
|
||||
*/
|
||||
public shouldCollectDuration(...durations: number[]): boolean {
|
||||
// a full list of Google crawlers user agent strings - https://support.google.com/webmasters/answer/1061943?hl=en
|
||||
let botAgentNames = ['googlebot', 'adsbot-google', 'apis-google', 'mediapartners-google'];
|
||||
let userAgent = navigator.userAgent;
|
||||
const botAgentNames = ['googlebot', 'adsbot-google', 'apis-google', 'mediapartners-google'];
|
||||
const userAgent = navigator.userAgent;
|
||||
let isGoogleBot = false;
|
||||
|
||||
if (userAgent) {
|
||||
|
@ -157,7 +157,7 @@ export class PageViewPerformanceManager {
|
|||
return false;
|
||||
} else {
|
||||
// for other page views, don't report if it's outside of a reasonable range
|
||||
for (var i = 0; i < durations.length; i++) {
|
||||
for (let i = 0; i < durations.length; i++) {
|
||||
if (durations[i] >= this.MAX_DURATION_ALLOWED) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ export class PageVisitTimeManager {
|
|||
this._logger = logger;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Tracks the previous page visit time telemetry (if exists) and starts timing of new page visit time
|
||||
* @param currentPageName Name of page to begin timing for visit duration
|
||||
* @param currentPageUrl Url of page to begin timing for visit duration
|
||||
|
@ -32,7 +32,7 @@ export class PageVisitTimeManager {
|
|||
|
||||
try {
|
||||
// Restart timer for new page view
|
||||
var prevPageVisitTimeData = this.restartPageVisitTimer(currentPageName, currentPageUrl);
|
||||
const prevPageVisitTimeData = this.restartPageVisitTimer(currentPageName, currentPageUrl);
|
||||
|
||||
// If there was a page already being timed, track the visit time for it now.
|
||||
if (prevPageVisitTimeData) {
|
||||
|
@ -50,7 +50,7 @@ export class PageVisitTimeManager {
|
|||
*/
|
||||
public restartPageVisitTimer(pageName: string, pageUrl: string) {
|
||||
try {
|
||||
var prevPageVisitData = this.stopPageVisitTimer();
|
||||
const prevPageVisitData = this.stopPageVisitTimer();
|
||||
this.startPageVisitTimer(pageName, pageUrl);
|
||||
|
||||
return prevPageVisitData;
|
||||
|
@ -72,12 +72,12 @@ export class PageVisitTimeManager {
|
|||
throw new Error("Cannot call startPageVisit consecutively without first calling stopPageVisit");
|
||||
}
|
||||
|
||||
var currPageVisitData = new PageVisitData(pageName, pageUrl);
|
||||
var currPageVisitDataStr = JSON.stringify(currPageVisitData);
|
||||
const currPageVisitData = new PageVisitData(pageName, pageUrl);
|
||||
const currPageVisitDataStr = JSON.stringify(currPageVisitData);
|
||||
Util.setSessionStorage(this._logger, this.prevPageVisitDataKeyName, currPageVisitDataStr);
|
||||
}
|
||||
} catch (e) {
|
||||
//TODO: Remove this catch in next phase, since if start is called twice in a row the exception needs to be propagated out
|
||||
// TODO: Remove this catch in next phase, since if start is called twice in a row the exception needs to be propagated out
|
||||
this._logger.warnToConsole("Call to start failed: " + Util.dump(e));
|
||||
}
|
||||
}
|
||||
|
@ -91,14 +91,14 @@ export class PageVisitTimeManager {
|
|||
if (Util.canUseSessionStorage()) {
|
||||
|
||||
// Define end time of page's visit
|
||||
var pageVisitEndTime = Date.now();
|
||||
const pageVisitEndTime = Date.now();
|
||||
|
||||
// Try to retrieve page name and start time from session storage
|
||||
var pageVisitDataJsonStr = Util.getSessionStorage(this._logger, this.prevPageVisitDataKeyName);
|
||||
const pageVisitDataJsonStr = Util.getSessionStorage(this._logger, this.prevPageVisitDataKeyName);
|
||||
if (pageVisitDataJsonStr) {
|
||||
|
||||
// if previous page data exists, set end time of visit
|
||||
var prevPageVisitData: PageVisitData = JSON.parse(pageVisitDataJsonStr);
|
||||
const prevPageVisitData: PageVisitData = JSON.parse(pageVisitDataJsonStr);
|
||||
prevPageVisitData.pageVisitTime = pageVisitEndTime - prevPageVisitData.pageVisitStartTime;
|
||||
|
||||
// Remove data from storage since we already used it
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint:latest",
|
||||
"tslint-config-prettier"
|
||||
],
|
||||
"rules": {
|
||||
"insecure-random": true,
|
||||
"no-banned-terms": true,
|
||||
"no-cookies": true,
|
||||
"no-delete-expression": true,
|
||||
"no-disable-auto-sanitization": true,
|
||||
"no-document-domain": true,
|
||||
"no-document-write": true,
|
||||
"no-eval": true,
|
||||
"no-exec-script": true,
|
||||
"no-function-constructor-with-string-args": true,
|
||||
"no-http-string": [true, "http://www.example.com/?.*", "http://localhost:?.*"],
|
||||
"no-inner-html": true,
|
||||
"no-octal-literal": true,
|
||||
"no-reserved-keywords": true,
|
||||
"no-string-based-set-immediate": true,
|
||||
"no-string-based-set-interval": true,
|
||||
"no-string-based-set-timeout": true,
|
||||
"non-literal-require": true,
|
||||
"possible-timing-attack": true,
|
||||
"react-anchor-blank-noopener": true,
|
||||
"react-iframe-missing-sandbox": true,
|
||||
"react-no-dangerous-html": true,
|
||||
"ordered-imports": false,
|
||||
"variable-name": false,
|
||||
"member-access": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"no-bitwise": false,
|
||||
"one-variable-per-declaration": false,
|
||||
"max-classes-per-file": false,
|
||||
"no-console": false,
|
||||
"prefer-for-of": false,
|
||||
"class-name": false,
|
||||
"interface-name": false,
|
||||
"no-empty-interface": false,
|
||||
"no-string-literal": false,
|
||||
"no-reference": false,
|
||||
"no-empty": false,
|
||||
"forin": false,
|
||||
"ban-types": false,
|
||||
"no-shadowed-variable": false,
|
||||
"no-implicit-dependencies": false,
|
||||
"no-object-literal-type-assertion": false,
|
||||
"no-this-assignment":false,
|
||||
"unified-signatures": false
|
||||
}
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
/// <reference path="../External/qunit.d.ts" />
|
||||
|
||||
/** Wrapper around QUnit asserts. This class has two purposes:
|
||||
/**
|
||||
* Wrapper around QUnit asserts. This class has two purposes:
|
||||
* - Make Assertion methods easy to discover.
|
||||
* - Make them consistent with XUnit assertions in the order of the actual and expected parameter values.
|
||||
*/
|
||||
class Assert {
|
||||
/**
|
||||
/**
|
||||
* A deep recursive comparison assertion, working on primitive types, arrays, objects,
|
||||
* regular expressions, dates and functions.
|
||||
*
|
||||
|
@ -21,7 +22,7 @@ class Assert {
|
|||
return deepEqual(actual, expected, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* A non-strict comparison assertion, roughly equivalent to JUnit assertEquals.
|
||||
*
|
||||
* The equal assertion uses the simple comparison operator (==) to compare the actual
|
||||
|
@ -37,7 +38,7 @@ class Assert {
|
|||
return equal(actual, expected, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* An inverted deep recursive comparison assertion, working on primitive types,
|
||||
* arrays, objects, regular expressions, dates and functions.
|
||||
*
|
||||
|
@ -53,7 +54,7 @@ class Assert {
|
|||
return notDeepEqual(actual, expected, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* A non-strict comparison assertion, checking for inequality.
|
||||
*
|
||||
* The notEqual assertion uses the simple inverted comparison operator (!=) to compare
|
||||
|
@ -77,7 +78,7 @@ class Assert {
|
|||
return propEqual(actual, expected, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* A non-strict comparison assertion, checking for inequality.
|
||||
*
|
||||
* The notStrictEqual assertion uses the strict inverted comparison operator (!==)
|
||||
|
@ -93,7 +94,7 @@ class Assert {
|
|||
return notStrictEqual(actual, expected, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* A boolean assertion, equivalent to CommonJS's assert.ok() and JUnit's assertTrue().
|
||||
* Passes if the first argument is truthy.
|
||||
*
|
||||
|
@ -108,7 +109,7 @@ class Assert {
|
|||
return ok(state, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* A strict type and value comparison assertion.
|
||||
*
|
||||
* The strictEqual() assertion provides the most rigid comparison of type and value with
|
||||
|
@ -122,7 +123,7 @@ class Assert {
|
|||
return strictEqual(actual, expected, message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Assertion to test if a callback throws an exception when run.
|
||||
*
|
||||
* When testing code that is expected to throw an exception based on a specific set of
|
||||
|
@ -134,7 +135,7 @@ class Assert {
|
|||
*/
|
||||
public static throws(block: () => any, expected: any, message?: string): any;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @param block Function to execute
|
||||
* @param message A short description of the assertion
|
||||
*/
|
||||
|
|
|
@ -22,7 +22,7 @@ class ContractTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
public registerTests() {
|
||||
var name = this.name + ": ";
|
||||
const name = this.name + ": ";
|
||||
this.testCase({
|
||||
name: name + "constructor does not throw errors",
|
||||
test: () => {
|
||||
|
@ -33,7 +33,7 @@ class ContractTestHelper extends TestClass {
|
|||
this.testCase({
|
||||
name: name + "serialization does not throw errors",
|
||||
test: () => {
|
||||
var subject = this.getSubject(this.initializer, this.name);
|
||||
const subject = this.getSubject(this.initializer, this.name);
|
||||
this.serialize(subject, this.name);
|
||||
}
|
||||
});
|
||||
|
@ -74,8 +74,8 @@ class ContractTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
private allRequiredFieldsAreConstructed(initializer: () => any, name: string) {
|
||||
var subject = this.getSubject(initializer, name);
|
||||
for (var field in subject.aiDataContract) {
|
||||
const subject = this.getSubject(initializer, name);
|
||||
for (const field in subject.aiDataContract) {
|
||||
if (subject.aiDataContract[field] & Microsoft.ApplicationInsights.FieldType.Required) {
|
||||
Assert.ok(subject[field] != null, "The required field '" + field + "' is constructed for: '" + name + "'");
|
||||
}
|
||||
|
@ -83,24 +83,24 @@ class ContractTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
private extraFieldsAreRemovedBySerializer(initializer: () => any, name: string) {
|
||||
var subject = this.getSubject(initializer, name);
|
||||
const subject = this.getSubject(initializer, name);
|
||||
|
||||
var extra = "extra";
|
||||
const extra = "extra";
|
||||
subject[extra + 0] = extra;
|
||||
subject[extra + 1] = extra;
|
||||
subject[extra + 3] = extra;
|
||||
|
||||
var serializedSubject = this.serialize(subject, name);
|
||||
const serializedSubject = this.serialize(subject, name);
|
||||
|
||||
for (var field in serializedSubject) {
|
||||
for (const field in serializedSubject) {
|
||||
Assert.ok(subject.aiDataContract[field] != null, "The field '" + field + "' exists in the contract for '" + name + "' and was serialized");
|
||||
}
|
||||
}
|
||||
|
||||
private optionalFieldsAreNotRequired(initializer: () => any, name: string) {
|
||||
var subject = this.getSubject(this.initializer, this.name);
|
||||
const subject = this.getSubject(this.initializer, this.name);
|
||||
|
||||
for (var field in subject.aiDataContract) {
|
||||
for (const field in subject.aiDataContract) {
|
||||
if (!subject.aiDataContract[field]) {
|
||||
delete subject[field];
|
||||
}
|
||||
|
@ -108,13 +108,13 @@ class ContractTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
private allFieldsAreIncludedIfSpecified(initializer: () => any, name: string) {
|
||||
var subject = this.getSubject(this.initializer, this.name);
|
||||
const subject = this.getSubject(this.initializer, this.name);
|
||||
|
||||
for (var field in subject.aiDataContract) {
|
||||
for (const field in subject.aiDataContract) {
|
||||
subject[field] = field;
|
||||
}
|
||||
|
||||
var serializedSubject = this.serialize(subject, this.name);
|
||||
const serializedSubject = this.serialize(subject, this.name);
|
||||
|
||||
for (field in subject.aiDataContract) {
|
||||
Assert.ok(serializedSubject[field] === field, "Field '" + field + "' was not serialized" + this.name);
|
||||
|
@ -126,7 +126,7 @@ class ContractTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
private serialize(subject: Microsoft.ApplicationInsights.ISerializable, name: string) {
|
||||
var serialized = "";
|
||||
let serialized = "";
|
||||
|
||||
try {
|
||||
serialized = Microsoft.ApplicationInsights.Serializer.serialize(subject);
|
||||
|
@ -138,7 +138,7 @@ class ContractTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
private getSubject(construction: () => Microsoft.ApplicationInsights.ISerializable, name: string): any {
|
||||
var subject = construction();
|
||||
const subject = construction();
|
||||
Assert.ok(!!subject, "can construct " + name);
|
||||
|
||||
return subject;
|
||||
|
|
|
@ -53,12 +53,12 @@ class PerformanceTestHelper extends TestClass {
|
|||
this.useFakeTimers = false;
|
||||
this.clock.restore();
|
||||
|
||||
this.appInsights = new Microsoft.ApplicationInsights.AppInsights(<any>{
|
||||
this.appInsights = new Microsoft.ApplicationInsights.AppInsights({
|
||||
instrumentationKey: "3e6a441c-b52b-4f39-8944-f81dd6c2dc46",
|
||||
url: "file:///C:/src/sdk/src/JavaScript/JavaScriptSDK.Tests//E2ETests/ai.js",
|
||||
endpointUrl: "https://dc.services.visualstudio.com/v2/track",
|
||||
maxBatchInterval: 0
|
||||
});
|
||||
} as any);
|
||||
|
||||
this.appInsights.context._sender._sender = () => null;
|
||||
this.testProperties = { p1: "val", p2: "val", p3: "val", p4: "val", p5: "val", p6: "val", p7: "val" };
|
||||
|
@ -69,39 +69,14 @@ class PerformanceTestHelper extends TestClass {
|
|||
public testCleanup() {
|
||||
this.useFakeServer = true;
|
||||
this.useFakeTimers = true;
|
||||
var serializedPerfResults: string = window["perfResults"] || "[]";
|
||||
var perfResults: IPerfResult[] = <any>(JSON.parse(serializedPerfResults));
|
||||
const serializedPerfResults: string = window["perfResults"] || "[]";
|
||||
let perfResults: IPerfResult[] = (JSON.parse(serializedPerfResults)) as any;
|
||||
perfResults = perfResults.concat(this.results);
|
||||
window["perfResults"] = JSON.stringify(perfResults);
|
||||
window["perfResultsCsv"] = this.toCsv(perfResults).csv;
|
||||
window["perfResultsCsvHeaders"] = this.toCsv(perfResults).headers;
|
||||
}
|
||||
|
||||
private toCsv(array: any[]) {
|
||||
var headers = "";
|
||||
if (array.length > 0) {
|
||||
var names = [];
|
||||
for (var name in array[0]) {
|
||||
names.push(name);
|
||||
}
|
||||
|
||||
headers = names.join(",");
|
||||
}
|
||||
|
||||
var csv = [];
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
var datum = array[i];
|
||||
var values = [];
|
||||
for (var j = 0; j < names.length; j++) {
|
||||
values.push(datum[names[j]]);
|
||||
}
|
||||
|
||||
csv.push(values.join(","));
|
||||
}
|
||||
|
||||
return { headers: headers, csv: csv.join("\r\n") };
|
||||
}
|
||||
|
||||
public enqueueTest(name: string, action: () => void) {
|
||||
JSLitmus.test(name, (count) => {
|
||||
while (count--) {
|
||||
|
@ -115,7 +90,7 @@ class PerformanceTestHelper extends TestClass {
|
|||
}
|
||||
|
||||
public onTestsComplete() {
|
||||
var perfLogging = new Microsoft.ApplicationInsights.AppInsights(<any>{
|
||||
const perfLogging = new Microsoft.ApplicationInsights.AppInsights({
|
||||
instrumentationKey: "1a6933ad-f260-447f-a2b0-e2233f6658eb",
|
||||
url: "file:///C:/src/sdk/src/JavaScript/JavaScriptSDK.Tests//E2ETests/ai.js",
|
||||
endpointUrl: "http://prodintdataforker.azurewebsites.net/dcservices?intKey=4d93aad0-cf1d-45b7-afc9-14f55504f6d5",
|
||||
|
@ -123,30 +98,30 @@ class PerformanceTestHelper extends TestClass {
|
|||
sessionExpirationMs: 24 * 60 * 60 * 1000,
|
||||
maxBatchSizeInBytes: 1000000,
|
||||
maxBatchInterval: 0
|
||||
});
|
||||
} as any);
|
||||
|
||||
perfLogging.context._sender._sender = (payload) => {
|
||||
var xhr = new sinon["xhr"].workingXHR();
|
||||
const xhr = new sinon["xhr"].workingXHR();
|
||||
xhr.open("POST", perfLogging.config.endpointUrl, true);
|
||||
xhr.setRequestHeader("Content-type", "application/json");
|
||||
xhr.send(payload);
|
||||
}
|
||||
|
||||
JSLitmus.stop();
|
||||
for (var i = 0; i < JSLitmus._tests.length; i++) {
|
||||
var test = JSLitmus._tests[i];
|
||||
var opsPerSec = test.count / test.time;
|
||||
for (let i = 0; i < JSLitmus._tests.length; i++) {
|
||||
const test = JSLitmus._tests[i];
|
||||
const opsPerSec = test.count / test.time;
|
||||
Assert.ok(true, test.name + " operations per sec:" + opsPerSec);
|
||||
|
||||
var timeInMs = <number>test.time;
|
||||
var date = +new Date;
|
||||
var oneHr = 60 * 60 * 1000;
|
||||
var oneHrDate = Math.floor(date / oneHr) * oneHr;
|
||||
var friendlyDate = new Date(oneHrDate).toISOString();
|
||||
var platform = <string>test.platform;
|
||||
var browser = "internetExplorer";
|
||||
var name = <string>test.name;
|
||||
var group = name.split(".")[0];
|
||||
const timeInMs = test.time as number;
|
||||
const date = +new Date;
|
||||
const oneHr = 60 * 60 * 1000;
|
||||
const oneHrDate = Math.floor(date / oneHr) * oneHr;
|
||||
const friendlyDate = new Date(oneHrDate).toISOString();
|
||||
const platform = test.platform as string;
|
||||
let browser = "internetExplorer";
|
||||
const name = test.name as string;
|
||||
const group = name.split(".")[0];
|
||||
if (platform.toLowerCase().indexOf("chrome") >= 0) {
|
||||
browser = "chrome";
|
||||
} else if (platform.toLowerCase().indexOf("firefox") >= 0) {
|
||||
|
@ -155,29 +130,29 @@ class PerformanceTestHelper extends TestClass {
|
|||
browser = "safari";
|
||||
}
|
||||
|
||||
var result: IPerfResult = {
|
||||
name: name,
|
||||
timeInMs: timeInMs,
|
||||
const result: IPerfResult = {
|
||||
name,
|
||||
timeInMs,
|
||||
operationCount: 1,
|
||||
opsPerSec: 1 / (timeInMs / 1000),
|
||||
period: 1,
|
||||
date: date,
|
||||
oneHrDate: oneHrDate,
|
||||
friendlyDate: friendlyDate,
|
||||
group: group,
|
||||
platform: platform,
|
||||
browser: browser,
|
||||
os: <string>test.os,
|
||||
date,
|
||||
oneHrDate,
|
||||
friendlyDate,
|
||||
group,
|
||||
platform,
|
||||
browser,
|
||||
os: test.os as string,
|
||||
millisecondsPerOp: (timeInMs / 1),
|
||||
microsecondsPerOp: (timeInMs / 1) * 1000,
|
||||
secondsPerOp: (timeInMs / 1) / 1000
|
||||
};
|
||||
|
||||
perfLogging.trackMetric(result.name, opsPerSec);
|
||||
var event = new Microsoft.ApplicationInsights.Telemetry.Event(result.name, opsPerSec, result);
|
||||
var data = new Microsoft.ApplicationInsights.Telemetry.Common.Data<Microsoft.ApplicationInsights.Telemetry.Event>(
|
||||
const event = new Microsoft.ApplicationInsights.Telemetry.Event(result.name, opsPerSec, result);
|
||||
const data = new Microsoft.ApplicationInsights.Telemetry.Common.Data<Microsoft.ApplicationInsights.Telemetry.Event>(
|
||||
Microsoft.ApplicationInsights.Telemetry.Event.dataType, event);
|
||||
var envelope = new Microsoft.ApplicationInsights.Telemetry.Common.Envelope(data, Microsoft.ApplicationInsights.Telemetry.Event.envelopeType);
|
||||
const envelope = new Microsoft.ApplicationInsights.Telemetry.Common.Envelope(data, Microsoft.ApplicationInsights.Telemetry.Event.envelopeType);
|
||||
perfLogging.context.track(envelope);
|
||||
|
||||
this.results.push(result);
|
||||
|
@ -195,6 +170,31 @@ class PerformanceTestHelper extends TestClass {
|
|||
}
|
||||
}
|
||||
|
||||
private toCsv(array: any[]) {
|
||||
let headers = "";
|
||||
if (array.length > 0) {
|
||||
const names = [];
|
||||
for (const name in array[0]) {
|
||||
names.push(name);
|
||||
}
|
||||
|
||||
headers = names.join(",");
|
||||
}
|
||||
|
||||
const csv = [];
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const datum = array[i];
|
||||
const values = [];
|
||||
for (let j = 0; j < names.length; j++) {
|
||||
values.push(datum[names[j]]);
|
||||
}
|
||||
|
||||
csv.push(values.join(","));
|
||||
}
|
||||
|
||||
return { headers, csv: csv.join("\r\n") };
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously loads jquery
|
||||
* we could regress the test suite and develop sublte jquery dependencies in the product code
|
||||
|
@ -204,11 +204,11 @@ class PerformanceTestHelper extends TestClass {
|
|||
private synchronouslyLoadJquery() {
|
||||
if (!window["$"]) {
|
||||
// get some kind of XMLHttpRequest
|
||||
var xhrObj = <any>false;
|
||||
let xhrObj = false as any;
|
||||
if (window["ActiveXObject"]) {
|
||||
xhrObj = <any>new ActiveXObject("Microsoft.XMLHTTP");
|
||||
xhrObj = (new ActiveXObject("Microsoft.XMLHTTP") as any);
|
||||
} else if (window["XMLHttpRequest"]) {
|
||||
xhrObj = <any>new XMLHttpRequest();
|
||||
xhrObj = (new XMLHttpRequest() as any);
|
||||
} else {
|
||||
alert("Please upgrade your browser! Your browser does not support AJAX!");
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ class PerformanceTestHelper extends TestClass {
|
|||
xhrObj.send('');
|
||||
|
||||
// add the returned content to a newly created script tag
|
||||
var script = document.createElement('script');
|
||||
const script = document.createElement('script');
|
||||
script.type = "text/javascript";
|
||||
script.text = xhrObj.responseText;
|
||||
document.getElementsByTagName('head')[0].appendChild(script);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/// <reference path="TestClass.ts" />
|
||||
|
||||
class PollingAssert {
|
||||
/**
|
||||
/**
|
||||
* Starts polling assertion function for a period of time after which it's considered failed.
|
||||
* @param {() => boolean} assertionFunctionReturnsBoolean - funciton returning true if condition passes and false if condition fails. Assertion will be done on this function's result.
|
||||
* @param {string} assertDescription - message shown with the assertion
|
||||
|
@ -11,9 +11,9 @@ class PollingAssert {
|
|||
* @returns {(nextTestStep) => void} callback which will be invoked by the TestClass
|
||||
*/
|
||||
public static createPollingAssert(assertionFunctionReturnsBoolean: () => boolean, assertDescription: string, timeoutSeconds: number = 30, pollIntervalMs: number = 500): (nextTestStep) => void {
|
||||
var pollingAssert = (nextTestStep) => {
|
||||
var timeout = new Date(new Date().getTime() + timeoutSeconds * 1000);
|
||||
var polling = () => {
|
||||
const pollingAssert = (nextTestStep) => {
|
||||
const timeout = new Date(new Date().getTime() + timeoutSeconds * 1000);
|
||||
const polling = () => {
|
||||
if (assertionFunctionReturnsBoolean.apply(this)) {
|
||||
Assert.ok(true, assertDescription);
|
||||
nextTestStep();
|
||||
|
|
|
@ -5,10 +5,6 @@
|
|||
|
||||
class TestClass {
|
||||
|
||||
constructor(name?: string) {
|
||||
QUnit.module(name);
|
||||
}
|
||||
|
||||
public static isPollingStepFlag = "isPollingStep";
|
||||
|
||||
/** The instance of the currently running suite. */
|
||||
|
@ -20,6 +16,18 @@ class TestClass {
|
|||
/** Turns on/off sinon's fake implementation of XMLHttpRequest. On by default. */
|
||||
public useFakeServer: boolean = true;
|
||||
|
||||
/**** Sinon methods and properties ***/
|
||||
|
||||
// These methods and properties are injected by Sinon and will override the implementation here.
|
||||
// These are here purely to make typescript happy.
|
||||
public clock: SinonFakeTimers;
|
||||
public server: SinonFakeServer;
|
||||
public sandbox: SinonSandbox;
|
||||
|
||||
constructor(name?: string) {
|
||||
QUnit.module(name);
|
||||
}
|
||||
|
||||
/** Method called before the start of each test method */
|
||||
public testInitialize() {
|
||||
}
|
||||
|
@ -47,8 +55,8 @@ class TestClass {
|
|||
}
|
||||
|
||||
// Create a wrapper around the test method so we can do test initilization and cleanup.
|
||||
var testMethod = (assert) => {
|
||||
var done = assert.async();
|
||||
const testMethod = (assert) => {
|
||||
const done = assert.async();
|
||||
|
||||
// Save off the instance of the currently running suite.
|
||||
TestClass.currentTestClass = this;
|
||||
|
@ -57,13 +65,13 @@ class TestClass {
|
|||
try {
|
||||
this._testStarting();
|
||||
|
||||
var steps = testInfo.steps;
|
||||
var trigger = () => {
|
||||
const steps = testInfo.steps;
|
||||
const trigger = () => {
|
||||
if (steps.length) {
|
||||
var step = steps.shift();
|
||||
const step = steps.shift();
|
||||
|
||||
// The callback which activates the next test step.
|
||||
var nextTestStepTrigger = () => {
|
||||
const nextTestStepTrigger = () => {
|
||||
setTimeout(() => {
|
||||
trigger();
|
||||
}, testInfo.stepDelay);
|
||||
|
@ -122,7 +130,7 @@ class TestClass {
|
|||
}
|
||||
|
||||
// Create a wrapper around the test method so we can do test initilization and cleanup.
|
||||
var testMethod = () => {
|
||||
const testMethod = () => {
|
||||
// Save off the instance of the currently running suite.
|
||||
TestClass.currentTestClass = this;
|
||||
|
||||
|
@ -144,46 +152,6 @@ class TestClass {
|
|||
test(testInfo.name, testMethod);
|
||||
}
|
||||
|
||||
/** Called when the test is starting. */
|
||||
private _testStarting() {
|
||||
// Initialize the sandbox similar to what is done in sinon.js "test()" override. See note on class.
|
||||
var config = (<any>sinon).getConfig(sinon.config);
|
||||
config.useFakeTimers = this.useFakeTimers;
|
||||
config.useFakeServer = this.useFakeServer;
|
||||
|
||||
config.injectInto = config.injectIntoThis && this || config.injectInto;
|
||||
this.sandbox = sinon.sandbox.create(config);
|
||||
this.server = this.sandbox.server;
|
||||
|
||||
// Allow the derived class to perform test initialization.
|
||||
this.testInitialize();
|
||||
}
|
||||
|
||||
/** Called when the test is completed. */
|
||||
private _testCompleted(failed?: boolean) {
|
||||
if (failed) {
|
||||
// Just cleanup the sandbox since the test has already failed.
|
||||
this.sandbox.restore();
|
||||
}
|
||||
else {
|
||||
// Verify the sandbox and restore.
|
||||
(<any>this.sandbox).verifyAndRestore();
|
||||
}
|
||||
|
||||
this.testCleanup();
|
||||
|
||||
// Clear the instance of the currently running suite.
|
||||
TestClass.currentTestClass = null;
|
||||
}
|
||||
|
||||
/**** Sinon methods and properties ***/
|
||||
|
||||
// These methods and properties are injected by Sinon and will override the implementation here.
|
||||
// These are here purely to make typescript happy.
|
||||
public clock: SinonFakeTimers;
|
||||
public server: SinonFakeServer;
|
||||
public sandbox: SinonSandbox;
|
||||
|
||||
/** Creates an anonymous function that records arguments, this value, exceptions and return values for all calls. */
|
||||
public spy(): SinonSpy;
|
||||
/** Spies on the provided function */
|
||||
|
@ -205,11 +173,12 @@ class TestClass {
|
|||
|
||||
/**** end: Sinon methods and properties ***/
|
||||
|
||||
/** Sends a JSON response to the provided request.
|
||||
/**
|
||||
* Sends a JSON response to the provided request.
|
||||
* @param request The request to respond to.
|
||||
* @param data Data to respond with.
|
||||
* @param errorCode Optional error code to send with the request, default is 200
|
||||
*/
|
||||
*/
|
||||
public sendJsonResponse(request: SinonFakeXMLHttpRequest, data: any, errorCode?: number) {
|
||||
if (errorCode === undefined) {
|
||||
errorCode = 200;
|
||||
|
@ -225,19 +194,51 @@ class TestClass {
|
|||
Object.defineProperty(window.navigator, 'userAgent',
|
||||
{
|
||||
configurable: true,
|
||||
get: function () {
|
||||
get () {
|
||||
return userAgent;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Called when the test is starting. */
|
||||
private _testStarting() {
|
||||
// Initialize the sandbox similar to what is done in sinon.js "test()" override. See note on class.
|
||||
const config = (sinon as any).getConfig(sinon.config);
|
||||
config.useFakeTimers = this.useFakeTimers;
|
||||
config.useFakeServer = this.useFakeServer;
|
||||
|
||||
config.injectInto = config.injectIntoThis && this || config.injectInto;
|
||||
this.sandbox = sinon.sandbox.create(config);
|
||||
this.server = this.sandbox.server;
|
||||
|
||||
// Allow the derived class to perform test initialization.
|
||||
this.testInitialize();
|
||||
}
|
||||
|
||||
/** Called when the test is completed. */
|
||||
private _testCompleted(failed?: boolean) {
|
||||
if (failed) {
|
||||
// Just cleanup the sandbox since the test has already failed.
|
||||
this.sandbox.restore();
|
||||
}
|
||||
else {
|
||||
// Verify the sandbox and restore.
|
||||
(this.sandbox as any).verifyAndRestore();
|
||||
}
|
||||
|
||||
this.testCleanup();
|
||||
|
||||
// Clear the instance of the currently running suite.
|
||||
TestClass.currentTestClass = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure Sinon
|
||||
sinon.assert.fail = function (msg?) {
|
||||
sinon.assert.fail = (msg?) => {
|
||||
Assert.ok(false, msg);
|
||||
};
|
||||
|
||||
sinon.assert.pass = function (assertion) {
|
||||
sinon.assert.pass = (assertion) => {
|
||||
Assert.ok(assertion, "sinon assert");
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
import { Util } from '@microsoft/applicationinsights-common';
|
||||
|
||||
export class Traceparent {
|
||||
|
||||
public static isValidTraceId(id: string): boolean {
|
||||
return id.match(/^[0-9a-f]{32}$/) && id !== "00000000000000000000000000000000";
|
||||
}
|
||||
|
||||
public static isValidSpanId(id: string): boolean {
|
||||
return id.match(/^[0-9a-f]{16}$/) && id !== "0000000000000000";
|
||||
}
|
||||
private static DEFAULT_TRACE_FLAG = "01";
|
||||
private static DEFAULT_VERSION = "00";
|
||||
public spanId: string;
|
||||
|
@ -21,14 +29,6 @@ export class Traceparent {
|
|||
}
|
||||
}
|
||||
|
||||
public static isValidTraceId(id: string): boolean {
|
||||
return id.match(/^[0-9a-f]{32}$/) && id !== "00000000000000000000000000000000";
|
||||
}
|
||||
|
||||
public static isValidSpanId(id: string): boolean {
|
||||
return id.match(/^[0-9a-f]{16}$/) && id !== "0000000000000000";
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return `${this.version}-${this.traceId}-${this.spanId}-${this.traceFlag}`;
|
||||
}
|
||||
|
|
|
@ -28,118 +28,48 @@ export interface IInstrumentationRequirements extends IDependenciesPlugin {
|
|||
}
|
||||
|
||||
export class AjaxMonitor implements ITelemetryPlugin, IDependenciesPlugin, IInstrumentationRequirements {
|
||||
private currentWindowHost;
|
||||
protected initialized: boolean; // ajax monitoring initialized
|
||||
protected _fetchInitialized: boolean; // fetch monitoring initialized
|
||||
protected _core: IAppInsightsCore;
|
||||
protected _config: ICorrelationConfig;
|
||||
protected _nextPlugin: ITelemetryPlugin;
|
||||
protected _trackAjaxAttempts: number = 0;
|
||||
private _context: ITelemetryContext;
|
||||
private _isUsingW3CHeaders: boolean;
|
||||
private _isUsingAIHeaders: boolean;
|
||||
|
||||
constructor() {
|
||||
this.currentWindowHost = window && window.location && window.location.host && window.location.host.toLowerCase();
|
||||
this.initialized = false;
|
||||
this._fetchInitialized = false;
|
||||
}
|
||||
public static identifier: string = "AjaxDependencyPlugin";
|
||||
|
||||
///<summary>Verifies that particalar instance of XMLHttpRequest needs to be monitored</summary>
|
||||
///<param name="excludeAjaxDataValidation">Optional parameter. True if ajaxData must be excluded from verification</param>
|
||||
///<returns type="bool">True if instance needs to be monitored, otherwise false</returns>
|
||||
private isMonitoredInstance(xhr?: XMLHttpRequestInstrumented, excludeAjaxDataValidation?: boolean, request?: Request | string, init?: RequestInit): boolean {
|
||||
|
||||
let disabledProperty = false;
|
||||
let ajaxValidation = true;
|
||||
let initialized = false;
|
||||
if (typeof request !== 'undefined') { // fetch
|
||||
initialized = this._fetchInitialized;
|
||||
// Look for DisabledPropertyName in either Request or RequestInit
|
||||
disabledProperty = (typeof request === 'object' ? request[DisabledPropertyName] === true : false) ||
|
||||
(init ? init[DisabledPropertyName] === true : false);
|
||||
} else if (typeof xhr !== 'undefined') {
|
||||
initialized = this.initialized;
|
||||
disabledProperty = xhr[DisabledPropertyName] === true;
|
||||
ajaxValidation = excludeAjaxDataValidation === true || !CoreUtils.isNullOrUndefined(xhr.ajaxData);
|
||||
public static getDefaultConfig(): ICorrelationConfig {
|
||||
const config: ICorrelationConfig = {
|
||||
maxAjaxCallsPerView: 500,
|
||||
disableAjaxTracking: false,
|
||||
disableFetchTracking: true,
|
||||
disableCorrelationHeaders: false,
|
||||
distributedTracingMode: DistributedTracingModes.AI,
|
||||
correlationHeaderExcludedDomains: [
|
||||
"*.blob.core.windows.net",
|
||||
"*.blob.core.chinacloudapi.cn",
|
||||
"*.blob.core.cloudapi.de",
|
||||
"*.blob.core.usgovcloudapi.net"],
|
||||
correlationHeaderDomains: undefined,
|
||||
appId: undefined,
|
||||
enableCorsCorrelation: false,
|
||||
enableRequestHeaderTracking: false,
|
||||
enableResponseHeaderTracking: false
|
||||
}
|
||||
|
||||
// checking to see that all interested functions on xhr were instrumented
|
||||
return initialized
|
||||
|
||||
// checking on ajaxData to see that it was not removed in user code
|
||||
&& ajaxValidation
|
||||
|
||||
// check that this instance is not not used by ajax call performed inside client side monitoring to send data to collector
|
||||
&& !disabledProperty;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
///<summary>Determines whether ajax monitoring can be enabled on this document</summary>
|
||||
///<returns>True if Ajax monitoring is supported on this page, otherwise false</returns>
|
||||
private supportsAjaxMonitoring(): boolean {
|
||||
var result = true;
|
||||
if (CoreUtils.isNullOrUndefined(XMLHttpRequest) ||
|
||||
CoreUtils.isNullOrUndefined(XMLHttpRequest.prototype) ||
|
||||
CoreUtils.isNullOrUndefined(XMLHttpRequest.prototype.open) ||
|
||||
CoreUtils.isNullOrUndefined(XMLHttpRequest.prototype.send) ||
|
||||
CoreUtils.isNullOrUndefined(XMLHttpRequest.prototype.abort)) {
|
||||
result = false;
|
||||
public static getEmptyConfig(): ICorrelationConfig {
|
||||
return {
|
||||
maxAjaxCallsPerView: undefined,
|
||||
disableAjaxTracking: undefined,
|
||||
disableFetchTracking: undefined,
|
||||
disableCorrelationHeaders: undefined,
|
||||
distributedTracingMode: undefined,
|
||||
correlationHeaderExcludedDomains: undefined,
|
||||
appId: undefined,
|
||||
enableCorsCorrelation: undefined,
|
||||
correlationHeaderDomains: undefined,
|
||||
enableRequestHeaderTracking: undefined,
|
||||
enableResponseHeaderTracking: undefined
|
||||
}
|
||||
|
||||
// disable in IE8 or older (https://www.w3schools.com/jsref/jsref_trim_string.asp)
|
||||
try {
|
||||
" a ".trim();
|
||||
} catch (ex) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private instrumentOpen() {
|
||||
var originalOpen = XMLHttpRequest.prototype.open;
|
||||
var ajaxMonitorInstance = this;
|
||||
XMLHttpRequest.prototype.open = function (method, url, async) {
|
||||
try {
|
||||
if (ajaxMonitorInstance.isMonitoredInstance(this, true) &&
|
||||
(
|
||||
!(<XMLHttpRequestInstrumented>this).ajaxData ||
|
||||
!(<XMLHttpRequestInstrumented>this).ajaxData.xhrMonitoringState.openDone
|
||||
)) {
|
||||
ajaxMonitorInstance.openHandler(this, method, url, async);
|
||||
}
|
||||
} catch (e) {
|
||||
ajaxMonitorInstance._core.logger.throwInternal(
|
||||
LoggingSeverity.CRITICAL,
|
||||
_InternalMessageId.FailedMonitorAjaxOpen,
|
||||
"Failed to monitor XMLHttpRequest.open, monitoring data for this ajax call may be incorrect.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(this),
|
||||
exception: Util.dump(e)
|
||||
});
|
||||
}
|
||||
|
||||
return originalOpen.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
private openHandler(xhr: XMLHttpRequestInstrumented, method, url, async) {
|
||||
var traceID = (this._context && this._context.telemetryTrace && this._context.telemetryTrace.traceID) || Util.generateW3CId();
|
||||
var spanID = Util.generateW3CId().substr(0, 16);
|
||||
|
||||
var ajaxData = new ajaxRecord(traceID, spanID, this._core.logger);
|
||||
ajaxData.method = method;
|
||||
ajaxData.requestUrl = url;
|
||||
ajaxData.xhrMonitoringState.openDone = true;
|
||||
ajaxData.requestHeaders = {};
|
||||
xhr.ajaxData = ajaxData;
|
||||
|
||||
this.attachToOnReadyStateChange(xhr);
|
||||
}
|
||||
|
||||
private static getFailedAjaxDiagnosticsMessage(xhr: XMLHttpRequestInstrumented): string {
|
||||
var result = "";
|
||||
let result = "";
|
||||
try {
|
||||
if (!CoreUtils.isNullOrUndefined(xhr) &&
|
||||
!CoreUtils.isNullOrUndefined(xhr.ajaxData) &&
|
||||
|
@ -150,200 +80,139 @@ export class AjaxMonitor implements ITelemetryPlugin, IDependenciesPlugin, IInst
|
|||
|
||||
return result;
|
||||
}
|
||||
public identifier: string = AjaxMonitor.identifier;
|
||||
|
||||
private instrumentSend() {
|
||||
var originalSend = XMLHttpRequest.prototype.send;
|
||||
var ajaxMonitorInstance = this;
|
||||
XMLHttpRequest.prototype.send = function (content) {
|
||||
try {
|
||||
if (ajaxMonitorInstance.isMonitoredInstance(this) && !(<XMLHttpRequestInstrumented>this).ajaxData.xhrMonitoringState.sendDone) {
|
||||
ajaxMonitorInstance.sendHandler(this, content);
|
||||
}
|
||||
} catch (e) {
|
||||
ajaxMonitorInstance._core.logger.throwInternal(
|
||||
LoggingSeverity.CRITICAL,
|
||||
_InternalMessageId.FailedMonitorAjaxSend,
|
||||
"Failed to monitor XMLHttpRequest, monitoring data for this ajax call may be incorrect.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(this),
|
||||
exception: Util.dump(e)
|
||||
});
|
||||
}
|
||||
priority: number = 120;
|
||||
protected initialized: boolean; // ajax monitoring initialized
|
||||
protected _fetchInitialized: boolean; // fetch monitoring initialized
|
||||
protected _core: IAppInsightsCore;
|
||||
protected _config: ICorrelationConfig;
|
||||
protected _nextPlugin: ITelemetryPlugin;
|
||||
protected _trackAjaxAttempts: number = 0;
|
||||
private currentWindowHost;
|
||||
private _context: ITelemetryContext;
|
||||
private _isUsingW3CHeaders: boolean;
|
||||
private _isUsingAIHeaders: boolean;
|
||||
|
||||
return originalSend.apply(this, arguments);
|
||||
};
|
||||
constructor() {
|
||||
this.currentWindowHost = window && window.location && window.location.host && window.location.host.toLowerCase();
|
||||
this.initialized = false;
|
||||
this._fetchInitialized = false;
|
||||
}
|
||||
|
||||
private sendHandler(xhr: XMLHttpRequestInstrumented, content) {
|
||||
xhr.ajaxData.requestSentTime = DateTimeUtils.Now();
|
||||
xhr = this.includeCorrelationHeaders(xhr.ajaxData, undefined, undefined, xhr);
|
||||
xhr.ajaxData.xhrMonitoringState.sendDone = true;
|
||||
trackDependencyData(dependency: IDependencyTelemetry, properties?: { [key: string]: any }) {
|
||||
this.trackDependencyDataInternal(dependency, properties);
|
||||
}
|
||||
|
||||
private instrumentAbort() {
|
||||
var originalAbort = XMLHttpRequest.prototype.abort;
|
||||
var ajaxMonitorInstance = this;
|
||||
XMLHttpRequest.prototype.abort = function () {
|
||||
try {
|
||||
if (ajaxMonitorInstance.isMonitoredInstance(this) && !(<XMLHttpRequestInstrumented>this).ajaxData.xhrMonitoringState.abortDone) {
|
||||
(<XMLHttpRequestInstrumented>this).ajaxData.aborted = 1;
|
||||
(<XMLHttpRequestInstrumented>this).ajaxData.xhrMonitoringState.abortDone = true;
|
||||
}
|
||||
} catch (e) {
|
||||
ajaxMonitorInstance._core.logger.throwInternal(
|
||||
LoggingSeverity.CRITICAL,
|
||||
_InternalMessageId.FailedMonitorAjaxAbort,
|
||||
"Failed to monitor XMLHttpRequest.abort, monitoring data for this ajax call may be incorrect.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(this),
|
||||
exception: Util.dump(e)
|
||||
});
|
||||
}
|
||||
|
||||
return originalAbort.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
private instrumentSetRequestHeader() {
|
||||
if (!this._config.enableRequestHeaderTracking) {
|
||||
return;
|
||||
}
|
||||
var originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
||||
var ajaxMonitorInstance = this;
|
||||
XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
|
||||
try {
|
||||
if (ajaxMonitorInstance.isMonitoredInstance(this)) {
|
||||
this.ajaxData.requestHeaders[header] = value;
|
||||
}
|
||||
} catch (e) {
|
||||
ajaxMonitorInstance._core.logger.throwInternal(
|
||||
LoggingSeverity.CRITICAL,
|
||||
_InternalMessageId.FailedMonitorAjaxSetRequestHeader,
|
||||
"Failed to monitor XMLHttpRequest.setRequestHeader, monitoring data for this ajax call may be incorrect.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(this),
|
||||
exception: Util.dump(e)
|
||||
});
|
||||
}
|
||||
|
||||
return originalSetRequestHeader.apply(this, arguments);
|
||||
public processTelemetry(item: ITelemetryItem) {
|
||||
if (this._nextPlugin && this._nextPlugin.processTelemetry) {
|
||||
this._nextPlugin.processTelemetry(item);
|
||||
}
|
||||
}
|
||||
|
||||
private attachToOnReadyStateChange(xhr: XMLHttpRequestInstrumented) {
|
||||
var ajaxMonitorInstance = this;
|
||||
xhr.ajaxData.xhrMonitoringState.onreadystatechangeCallbackAttached = EventHelper.AttachEvent(xhr, "readystatechange", () => {
|
||||
try {
|
||||
if (ajaxMonitorInstance.isMonitoredInstance(xhr)) {
|
||||
if (xhr.readyState === 4) {
|
||||
ajaxMonitorInstance.onAjaxComplete(xhr);
|
||||
setNextPlugin(next: ITelemetryPlugin) {
|
||||
if (next) {
|
||||
this._nextPlugin = next;
|
||||
}
|
||||
}
|
||||
|
||||
public includeCorrelationHeaders(ajaxData: ajaxRecord, input?: Request | string, init?: RequestInit, xhr?: XMLHttpRequestInstrumented): any {
|
||||
if (input) { // Fetch
|
||||
if (CorrelationIdHelper.canIncludeCorrelationHeader(this._config, ajaxData.getAbsoluteUrl(), this.currentWindowHost)) {
|
||||
if (!init) {
|
||||
init = {};
|
||||
}
|
||||
// init headers override original request headers
|
||||
// so, if they exist use only them, otherwise use request's because they should have been applied in the first place
|
||||
// not using original request headers will result in them being lost
|
||||
init.headers = new Headers(init.headers || (input instanceof Request ? (input.headers || {}) : {}));
|
||||
if (this._isUsingAIHeaders) {
|
||||
const id = "|" + ajaxData.traceID + "." + ajaxData.spanID;
|
||||
init.headers.set(RequestHeaders.requestIdHeader, id);
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
ajaxData.requestHeaders[RequestHeaders.requestIdHeader] = id;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
var exceptionText = Util.dump(e);
|
||||
|
||||
// ignore messages with c00c023f, as this a known IE9 XHR abort issue
|
||||
if (!exceptionText || exceptionText.toLowerCase().indexOf("c00c023f") == -1) {
|
||||
ajaxMonitorInstance._core.logger.throwInternal(
|
||||
LoggingSeverity.CRITICAL,
|
||||
_InternalMessageId.FailedMonitorAjaxRSC,
|
||||
"Failed to monitor XMLHttpRequest 'readystatechange' event handler, monitoring data for this ajax call may be incorrect.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(xhr),
|
||||
exception: Util.dump(e)
|
||||
});
|
||||
const appId: string = this._config.appId || this._context.appId();
|
||||
if (appId) {
|
||||
init.headers.set(RequestHeaders.requestContextHeader, RequestHeaders.requestContextAppIdFormat + appId);
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
ajaxData.requestHeaders[RequestHeaders.requestContextHeader] = RequestHeaders.requestContextAppIdFormat + appId;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private onAjaxComplete(xhr: XMLHttpRequestInstrumented) {
|
||||
xhr.ajaxData.responseFinishedTime = DateTimeUtils.Now();
|
||||
xhr.ajaxData.status = xhr.status;
|
||||
xhr.ajaxData.CalculateMetrics();
|
||||
|
||||
if (xhr.ajaxData.ajaxTotalDuration < 0) {
|
||||
this._core.logger.throwInternal(
|
||||
LoggingSeverity.WARNING,
|
||||
_InternalMessageId.FailedMonitorAjaxDur,
|
||||
"Failed to calculate the duration of the ajax call, monitoring data for this ajax call won't be sent.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(xhr),
|
||||
requestSentTime: xhr.ajaxData.requestSentTime,
|
||||
responseFinishedTime: xhr.ajaxData.responseFinishedTime
|
||||
});
|
||||
} else {
|
||||
const dependency = <IDependencyTelemetry>{
|
||||
id: "|" + xhr.ajaxData.traceID + "." + xhr.ajaxData.spanID,
|
||||
target: xhr.ajaxData.getAbsoluteUrl(),
|
||||
name: xhr.ajaxData.getPathName(),
|
||||
type: "Ajax",
|
||||
duration: xhr.ajaxData.ajaxTotalDuration,
|
||||
success: (+(xhr.ajaxData.status)) >= 200 && (+(xhr.ajaxData.status)) < 400,
|
||||
responseCode: +xhr.ajaxData.status,
|
||||
method: xhr.ajaxData.method
|
||||
};
|
||||
|
||||
// enrich dependency target with correlation context from the server
|
||||
var correlationContext = this.getAjaxCorrelationContext(xhr);
|
||||
if (correlationContext) {
|
||||
dependency.correlationContext = /* dependency.target + " | " + */ correlationContext;
|
||||
}
|
||||
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
if (Object.keys(xhr.ajaxData.requestHeaders).length > 0) {
|
||||
dependency.properties = dependency.properties || {};
|
||||
dependency.properties.requestHeaders = {};
|
||||
dependency.properties.requestHeaders = xhr.ajaxData.requestHeaders;
|
||||
if (this._isUsingW3CHeaders) {
|
||||
const traceparent = new Traceparent(ajaxData.traceID, ajaxData.spanID);
|
||||
init.headers.set(RequestHeaders.traceParentHeader, traceparent.toString());
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
ajaxData.requestHeaders[RequestHeaders.traceParentHeader] = traceparent.toString();
|
||||
}
|
||||
}
|
||||
return init;
|
||||
}
|
||||
|
||||
if (this._config.enableResponseHeaderTracking) {
|
||||
var headers = xhr.getAllResponseHeaders();
|
||||
if (headers) {
|
||||
// xhr.getAllResponseHeaders() method returns all the response headers, separated by CRLF, as a string or null
|
||||
// the regex converts the header string into an array of individual headers
|
||||
var arr = headers.trim().split(/[\r\n]+/);
|
||||
const responseHeaderMap = {};
|
||||
arr.forEach(function (line) {
|
||||
var parts = line.split(': ');
|
||||
var header = parts.shift();
|
||||
var value = parts.join(': ');
|
||||
responseHeaderMap[header] = value;
|
||||
});
|
||||
if (Object.keys(responseHeaderMap).length > 0) {
|
||||
dependency.properties = dependency.properties || {};
|
||||
dependency.properties.responseHeaders = {};
|
||||
dependency.properties.responseHeaders = responseHeaderMap;
|
||||
return init;
|
||||
} else if (xhr) { // XHR
|
||||
if (this.currentWindowHost && CorrelationIdHelper.canIncludeCorrelationHeader(this._config, xhr.ajaxData.getAbsoluteUrl(),
|
||||
this.currentWindowHost)) {
|
||||
if (this._isUsingAIHeaders) {
|
||||
const id = "|" + xhr.ajaxData.traceID + "." + xhr.ajaxData.spanID;
|
||||
xhr.setRequestHeader(RequestHeaders.requestIdHeader, id);
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
xhr.ajaxData.requestHeaders[RequestHeaders.requestIdHeader] = id;
|
||||
}
|
||||
}
|
||||
const appId = this._config.appId || this._context.appId();
|
||||
if (appId) {
|
||||
xhr.setRequestHeader(RequestHeaders.requestContextHeader, RequestHeaders.requestContextAppIdFormat + appId);
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
xhr.ajaxData.requestHeaders[RequestHeaders.requestContextHeader] = RequestHeaders.requestContextAppIdFormat + appId;
|
||||
}
|
||||
}
|
||||
if (this._isUsingW3CHeaders) {
|
||||
const traceparent = new Traceparent(xhr.ajaxData.traceID, xhr.ajaxData.spanID);
|
||||
xhr.setRequestHeader(RequestHeaders.traceParentHeader, traceparent.toString());
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
xhr.ajaxData.requestHeaders[RequestHeaders.traceParentHeader] = traceparent.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.trackDependencyDataInternal(dependency);
|
||||
|
||||
xhr.ajaxData = null;
|
||||
return xhr;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private getAjaxCorrelationContext(xhr: XMLHttpRequestInstrumented) {
|
||||
try {
|
||||
var responseHeadersString = xhr.getAllResponseHeaders();
|
||||
if (responseHeadersString !== null) {
|
||||
var index = responseHeadersString.toLowerCase().indexOf(RequestHeaders.requestContextHeaderLowerCase);
|
||||
if (index !== -1) {
|
||||
var responseHeader = xhr.getResponseHeader(RequestHeaders.requestContextHeader);
|
||||
return CorrelationIdHelper.getCorrelationContext(responseHeader);
|
||||
public initialize(config: IConfiguration & IConfig, core: IAppInsightsCore, extensions: IPlugin[]) {
|
||||
if (!this.initialized && !this._fetchInitialized) {
|
||||
this._core = core;
|
||||
const defaultConfig = AjaxMonitor.getDefaultConfig();
|
||||
this._config = AjaxMonitor.getEmptyConfig();
|
||||
for (const field in defaultConfig) {
|
||||
this._config[field] = ConfigurationManager.getConfig(config, field, AjaxMonitor.identifier, defaultConfig[field]);
|
||||
}
|
||||
|
||||
this._isUsingAIHeaders = this._config.distributedTracingMode === DistributedTracingModes.AI || this._config.distributedTracingMode === DistributedTracingModes.AI_AND_W3C;
|
||||
this._isUsingW3CHeaders = this._config.distributedTracingMode === DistributedTracingModes.AI_AND_W3C || this._config.distributedTracingMode === DistributedTracingModes.W3C;
|
||||
|
||||
if (this._config.disableAjaxTracking === false) {
|
||||
this.instrumentXhr();
|
||||
}
|
||||
|
||||
if (this._config.disableFetchTracking === false) {
|
||||
this.instrumentFetch();
|
||||
}
|
||||
|
||||
if (extensions.length > 0 && extensions) {
|
||||
let propExt, extIx = 0;
|
||||
while (!propExt && extIx < extensions.length) {
|
||||
if (extensions[extIx] && extensions[extIx].identifier === PropertiesPluginIdentifier) {
|
||||
propExt = extensions[extIx]
|
||||
}
|
||||
extIx++;
|
||||
}
|
||||
if (propExt) {
|
||||
this._context = propExt.context; // we could move IPropertiesPlugin to common as well
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
this._core.logger.throwInternal(
|
||||
LoggingSeverity.WARNING,
|
||||
_InternalMessageId.FailedMonitorAjaxGetCorrelationHeader,
|
||||
"Failed to get Request-Context correlation header as it may be not included in the response or not accessible.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(xhr),
|
||||
exception: Util.dump(e)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -353,7 +222,7 @@ export class AjaxMonitor implements ITelemetryPlugin, IDependenciesPlugin, IInst
|
|||
*/
|
||||
protected trackDependencyDataInternal(dependency: IDependencyTelemetry, properties?: { [key: string]: any }, systemProperties?: { [key: string]: any }) {
|
||||
if (this._config.maxAjaxCallsPerView === -1 || this._trackAjaxAttempts < this._config.maxAjaxCallsPerView) {
|
||||
let item = TelemetryItemCreator.create<IDependencyTelemetry>(
|
||||
const item = TelemetryItemCreator.create<IDependencyTelemetry>(
|
||||
dependency,
|
||||
RemoteDependencyData.dataType,
|
||||
RemoteDependencyData.envelopeType,
|
||||
|
@ -372,27 +241,6 @@ export class AjaxMonitor implements ITelemetryPlugin, IDependenciesPlugin, IInst
|
|||
++this._trackAjaxAttempts;
|
||||
}
|
||||
|
||||
trackDependencyData(dependency: IDependencyTelemetry, properties?: { [key: string]: any }) {
|
||||
this.trackDependencyDataInternal(dependency, properties);
|
||||
}
|
||||
|
||||
public processTelemetry(item: ITelemetryItem) {
|
||||
if (this._nextPlugin && this._nextPlugin.processTelemetry) {
|
||||
this._nextPlugin.processTelemetry(item);
|
||||
}
|
||||
}
|
||||
|
||||
public static identifier: string = "AjaxDependencyPlugin";
|
||||
public identifier: string = AjaxMonitor.identifier;
|
||||
|
||||
setNextPlugin(next: ITelemetryPlugin) {
|
||||
if (next) {
|
||||
this._nextPlugin = next;
|
||||
}
|
||||
}
|
||||
|
||||
priority: number = 120;
|
||||
|
||||
// Fetch Stuff
|
||||
protected instrumentFetch(): void {
|
||||
if (!this.supportsFetch() || this._fetchInitialized) {
|
||||
|
@ -430,6 +278,305 @@ export class AjaxMonitor implements ITelemetryPlugin, IDependenciesPlugin, IInst
|
|||
this._fetchInitialized = true;
|
||||
}
|
||||
|
||||
protected instrumentXhr() {
|
||||
if (this.supportsAjaxMonitoring() && !this.initialized) {
|
||||
this.instrumentOpen();
|
||||
this.instrumentSend();
|
||||
this.instrumentAbort();
|
||||
this.instrumentSetRequestHeader();
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that particalar instance of XMLHttpRequest needs to be monitored</summary>
|
||||
/// <param name="excludeAjaxDataValidation">Optional parameter. True if ajaxData must be excluded from verification</param>
|
||||
/// <returns type="bool">True if instance needs to be monitored, otherwise false</returns>
|
||||
private isMonitoredInstance(xhr?: XMLHttpRequestInstrumented, excludeAjaxDataValidation?: boolean, request?: Request | string, init?: RequestInit): boolean {
|
||||
|
||||
let disabledProperty = false;
|
||||
let ajaxValidation = true;
|
||||
let initialized = false;
|
||||
if (typeof request !== 'undefined') { // fetch
|
||||
initialized = this._fetchInitialized;
|
||||
// Look for DisabledPropertyName in either Request or RequestInit
|
||||
disabledProperty = (typeof request === 'object' ? request[DisabledPropertyName] === true : false) ||
|
||||
(init ? init[DisabledPropertyName] === true : false);
|
||||
} else if (typeof xhr !== 'undefined') {
|
||||
initialized = this.initialized;
|
||||
disabledProperty = xhr[DisabledPropertyName] === true;
|
||||
ajaxValidation = excludeAjaxDataValidation === true || !CoreUtils.isNullOrUndefined(xhr.ajaxData);
|
||||
}
|
||||
|
||||
// checking to see that all interested functions on xhr were instrumented
|
||||
return initialized
|
||||
|
||||
// checking on ajaxData to see that it was not removed in user code
|
||||
&& ajaxValidation
|
||||
|
||||
// check that this instance is not not used by ajax call performed inside client side monitoring to send data to collector
|
||||
&& !disabledProperty;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Determines whether ajax monitoring can be enabled on this document</summary>
|
||||
/// <returns>True if Ajax monitoring is supported on this page, otherwise false</returns>
|
||||
private supportsAjaxMonitoring(): boolean {
|
||||
let result = true;
|
||||
if (CoreUtils.isNullOrUndefined(XMLHttpRequest) ||
|
||||
CoreUtils.isNullOrUndefined(XMLHttpRequest.prototype) ||
|
||||
CoreUtils.isNullOrUndefined(XMLHttpRequest.prototype.open) ||
|
||||
CoreUtils.isNullOrUndefined(XMLHttpRequest.prototype.send) ||
|
||||
CoreUtils.isNullOrUndefined(XMLHttpRequest.prototype.abort)) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
// disable in IE8 or older (https://www.w3schools.com/jsref/jsref_trim_string.asp)
|
||||
try {
|
||||
" a ".trim();
|
||||
} catch (ex) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private instrumentOpen() {
|
||||
const originalOpen = XMLHttpRequest.prototype.open;
|
||||
const ajaxMonitorInstance = this;
|
||||
XMLHttpRequest.prototype.open = function (method, url, async) {
|
||||
try {
|
||||
if (ajaxMonitorInstance.isMonitoredInstance(this, true) &&
|
||||
(
|
||||
!(this as XMLHttpRequestInstrumented).ajaxData ||
|
||||
!(this as XMLHttpRequestInstrumented).ajaxData.xhrMonitoringState.openDone
|
||||
)) {
|
||||
ajaxMonitorInstance.openHandler(this, method, url, async);
|
||||
}
|
||||
} catch (e) {
|
||||
ajaxMonitorInstance._core.logger.throwInternal(
|
||||
LoggingSeverity.CRITICAL,
|
||||
_InternalMessageId.FailedMonitorAjaxOpen,
|
||||
"Failed to monitor XMLHttpRequest.open, monitoring data for this ajax call may be incorrect.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(this),
|
||||
exception: Util.dump(e)
|
||||
});
|
||||
}
|
||||
|
||||
return originalOpen.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
private openHandler(xhr: XMLHttpRequestInstrumented, method, url, async) {
|
||||
const traceID = (this._context && this._context.telemetryTrace && this._context.telemetryTrace.traceID) || Util.generateW3CId();
|
||||
const spanID = Util.generateW3CId().substr(0, 16);
|
||||
|
||||
const ajaxData = new ajaxRecord(traceID, spanID, this._core.logger);
|
||||
ajaxData.method = method;
|
||||
ajaxData.requestUrl = url;
|
||||
ajaxData.xhrMonitoringState.openDone = true;
|
||||
ajaxData.requestHeaders = {};
|
||||
xhr.ajaxData = ajaxData;
|
||||
|
||||
this.attachToOnReadyStateChange(xhr);
|
||||
}
|
||||
|
||||
private instrumentSend() {
|
||||
const originalSend = XMLHttpRequest.prototype.send;
|
||||
const ajaxMonitorInstance = this;
|
||||
XMLHttpRequest.prototype.send = function (content) {
|
||||
try {
|
||||
if (ajaxMonitorInstance.isMonitoredInstance(this) && !(this as XMLHttpRequestInstrumented).ajaxData.xhrMonitoringState.sendDone) {
|
||||
ajaxMonitorInstance.sendHandler(this, content);
|
||||
}
|
||||
} catch (e) {
|
||||
ajaxMonitorInstance._core.logger.throwInternal(
|
||||
LoggingSeverity.CRITICAL,
|
||||
_InternalMessageId.FailedMonitorAjaxSend,
|
||||
"Failed to monitor XMLHttpRequest, monitoring data for this ajax call may be incorrect.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(this),
|
||||
exception: Util.dump(e)
|
||||
});
|
||||
}
|
||||
|
||||
return originalSend.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
private sendHandler(xhr: XMLHttpRequestInstrumented, content) {
|
||||
xhr.ajaxData.requestSentTime = DateTimeUtils.Now();
|
||||
xhr = this.includeCorrelationHeaders(xhr.ajaxData, undefined, undefined, xhr);
|
||||
xhr.ajaxData.xhrMonitoringState.sendDone = true;
|
||||
}
|
||||
|
||||
private instrumentAbort() {
|
||||
const originalAbort = XMLHttpRequest.prototype.abort;
|
||||
const ajaxMonitorInstance = this;
|
||||
XMLHttpRequest.prototype.abort = function () {
|
||||
try {
|
||||
if (ajaxMonitorInstance.isMonitoredInstance(this) && !(this as XMLHttpRequestInstrumented).ajaxData.xhrMonitoringState.abortDone) {
|
||||
(this as XMLHttpRequestInstrumented).ajaxData.aborted = 1;
|
||||
(this as XMLHttpRequestInstrumented).ajaxData.xhrMonitoringState.abortDone = true;
|
||||
}
|
||||
} catch (e) {
|
||||
ajaxMonitorInstance._core.logger.throwInternal(
|
||||
LoggingSeverity.CRITICAL,
|
||||
_InternalMessageId.FailedMonitorAjaxAbort,
|
||||
"Failed to monitor XMLHttpRequest.abort, monitoring data for this ajax call may be incorrect.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(this),
|
||||
exception: Util.dump(e)
|
||||
});
|
||||
}
|
||||
|
||||
return originalAbort.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
private instrumentSetRequestHeader() {
|
||||
if (!this._config.enableRequestHeaderTracking) {
|
||||
return;
|
||||
}
|
||||
const originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
||||
const ajaxMonitorInstance = this;
|
||||
XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
|
||||
try {
|
||||
if (ajaxMonitorInstance.isMonitoredInstance(this)) {
|
||||
this.ajaxData.requestHeaders[header] = value;
|
||||
}
|
||||
} catch (e) {
|
||||
ajaxMonitorInstance._core.logger.throwInternal(
|
||||
LoggingSeverity.CRITICAL,
|
||||
_InternalMessageId.FailedMonitorAjaxSetRequestHeader,
|
||||
"Failed to monitor XMLHttpRequest.setRequestHeader, monitoring data for this ajax call may be incorrect.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(this),
|
||||
exception: Util.dump(e)
|
||||
});
|
||||
}
|
||||
|
||||
return originalSetRequestHeader.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
private attachToOnReadyStateChange(xhr: XMLHttpRequestInstrumented) {
|
||||
const ajaxMonitorInstance = this;
|
||||
xhr.ajaxData.xhrMonitoringState.onreadystatechangeCallbackAttached = EventHelper.AttachEvent(xhr, "readystatechange", () => {
|
||||
try {
|
||||
if (ajaxMonitorInstance.isMonitoredInstance(xhr)) {
|
||||
if (xhr.readyState === 4) {
|
||||
ajaxMonitorInstance.onAjaxComplete(xhr);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
const exceptionText = Util.dump(e);
|
||||
|
||||
// ignore messages with c00c023f, as this a known IE9 XHR abort issue
|
||||
if (!exceptionText || exceptionText.toLowerCase().indexOf("c00c023f") === -1) {
|
||||
ajaxMonitorInstance._core.logger.throwInternal(
|
||||
LoggingSeverity.CRITICAL,
|
||||
_InternalMessageId.FailedMonitorAjaxRSC,
|
||||
"Failed to monitor XMLHttpRequest 'readystatechange' event handler, monitoring data for this ajax call may be incorrect.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(xhr),
|
||||
exception: Util.dump(e)
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private onAjaxComplete(xhr: XMLHttpRequestInstrumented) {
|
||||
xhr.ajaxData.responseFinishedTime = DateTimeUtils.Now();
|
||||
xhr.ajaxData.status = xhr.status;
|
||||
xhr.ajaxData.CalculateMetrics();
|
||||
|
||||
if (xhr.ajaxData.ajaxTotalDuration < 0) {
|
||||
this._core.logger.throwInternal(
|
||||
LoggingSeverity.WARNING,
|
||||
_InternalMessageId.FailedMonitorAjaxDur,
|
||||
"Failed to calculate the duration of the ajax call, monitoring data for this ajax call won't be sent.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(xhr),
|
||||
requestSentTime: xhr.ajaxData.requestSentTime,
|
||||
responseFinishedTime: xhr.ajaxData.responseFinishedTime
|
||||
});
|
||||
} else {
|
||||
const dependency = {
|
||||
id: "|" + xhr.ajaxData.traceID + "." + xhr.ajaxData.spanID,
|
||||
target: xhr.ajaxData.getAbsoluteUrl(),
|
||||
name: xhr.ajaxData.getPathName(),
|
||||
type: "Ajax",
|
||||
duration: xhr.ajaxData.ajaxTotalDuration,
|
||||
success: (+(xhr.ajaxData.status)) >= 200 && (+(xhr.ajaxData.status)) < 400,
|
||||
responseCode: +xhr.ajaxData.status,
|
||||
method: xhr.ajaxData.method
|
||||
} as IDependencyTelemetry;
|
||||
|
||||
// enrich dependency target with correlation context from the server
|
||||
const correlationContext = this.getAjaxCorrelationContext(xhr);
|
||||
if (correlationContext) {
|
||||
dependency.correlationContext = /* dependency.target + " | " + */ correlationContext;
|
||||
}
|
||||
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
if (Object.keys(xhr.ajaxData.requestHeaders).length > 0) {
|
||||
dependency.properties = dependency.properties || {};
|
||||
dependency.properties.requestHeaders = {};
|
||||
dependency.properties.requestHeaders = xhr.ajaxData.requestHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._config.enableResponseHeaderTracking) {
|
||||
const headers = xhr.getAllResponseHeaders();
|
||||
if (headers) {
|
||||
// xhr.getAllResponseHeaders() method returns all the response headers, separated by CRLF, as a string or null
|
||||
// the regex converts the header string into an array of individual headers
|
||||
const arr = headers.trim().split(/[\r\n]+/);
|
||||
const responseHeaderMap = {};
|
||||
arr.forEach((line) => {
|
||||
const parts = line.split(': ');
|
||||
const header = parts.shift();
|
||||
const value = parts.join(': ');
|
||||
responseHeaderMap[header] = value;
|
||||
});
|
||||
if (Object.keys(responseHeaderMap).length > 0) {
|
||||
dependency.properties = dependency.properties || {};
|
||||
dependency.properties.responseHeaders = {};
|
||||
dependency.properties.responseHeaders = responseHeaderMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.trackDependencyDataInternal(dependency);
|
||||
|
||||
xhr.ajaxData = null;
|
||||
}
|
||||
}
|
||||
|
||||
private getAjaxCorrelationContext(xhr: XMLHttpRequestInstrumented) {
|
||||
try {
|
||||
const responseHeadersString = xhr.getAllResponseHeaders();
|
||||
if (responseHeadersString !== null) {
|
||||
const index = responseHeadersString.toLowerCase().indexOf(RequestHeaders.requestContextHeaderLowerCase);
|
||||
if (index !== -1) {
|
||||
const responseHeader = xhr.getResponseHeader(RequestHeaders.requestContextHeader);
|
||||
return CorrelationIdHelper.getCorrelationContext(responseHeader);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
this._core.logger.throwInternal(
|
||||
LoggingSeverity.WARNING,
|
||||
_InternalMessageId.FailedMonitorAjaxGetCorrelationHeader,
|
||||
"Failed to get Request-Context correlation header as it may be not included in the response or not accessible.",
|
||||
{
|
||||
ajaxDiagnosticsMessage: AjaxMonitor.getFailedAjaxDiagnosticsMessage(xhr),
|
||||
exception: Util.dump(e)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private isFetchInstrumented(input: Request | string): boolean {
|
||||
return this._fetchInitialized && input[DisabledPropertyName] !== true;
|
||||
}
|
||||
|
@ -445,10 +592,10 @@ export class AjaxMonitor implements ITelemetryPlugin, IDependenciesPlugin, IInst
|
|||
}
|
||||
|
||||
private createFetchRecord(input?: Request | string, init?: RequestInit): ajaxRecord {
|
||||
var traceID = (this._context && this._context.telemetryTrace && this._context.telemetryTrace.traceID) || Util.generateW3CId();
|
||||
var spanID = Util.generateW3CId().substr(0, 16);
|
||||
const traceID = (this._context && this._context.telemetryTrace && this._context.telemetryTrace.traceID) || Util.generateW3CId();
|
||||
const spanID = Util.generateW3CId().substr(0, 16);
|
||||
|
||||
var ajaxData = new ajaxRecord(traceID, spanID, this._core.logger);
|
||||
const ajaxData = new ajaxRecord(traceID, spanID, this._core.logger);
|
||||
ajaxData.requestSentTime = DateTimeUtils.Now();
|
||||
|
||||
if (input instanceof Request) {
|
||||
|
@ -473,71 +620,6 @@ export class AjaxMonitor implements ITelemetryPlugin, IDependenciesPlugin, IInst
|
|||
return ajaxData;
|
||||
}
|
||||
|
||||
public includeCorrelationHeaders(ajaxData: ajaxRecord, input?: Request | string, init?: RequestInit, xhr?: XMLHttpRequestInstrumented): any {
|
||||
if (input) { // Fetch
|
||||
if (CorrelationIdHelper.canIncludeCorrelationHeader(this._config, ajaxData.getAbsoluteUrl(), this.currentWindowHost)) {
|
||||
if (!init) {
|
||||
init = {};
|
||||
}
|
||||
// init headers override original request headers
|
||||
// so, if they exist use only them, otherwise use request's because they should have been applied in the first place
|
||||
// not using original request headers will result in them being lost
|
||||
init.headers = new Headers(init.headers || (input instanceof Request ? (input.headers || {}) : {}));
|
||||
if (this._isUsingAIHeaders) {
|
||||
var id = "|" + ajaxData.traceID + "." + ajaxData.spanID;
|
||||
init.headers.set(RequestHeaders.requestIdHeader, id);
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
ajaxData.requestHeaders[RequestHeaders.requestIdHeader] = id;
|
||||
}
|
||||
}
|
||||
let appId: string = this._config.appId || this._context.appId();
|
||||
if (appId) {
|
||||
init.headers.set(RequestHeaders.requestContextHeader, RequestHeaders.requestContextAppIdFormat + appId);
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
ajaxData.requestHeaders[RequestHeaders.requestContextHeader] = RequestHeaders.requestContextAppIdFormat + appId;
|
||||
}
|
||||
}
|
||||
if (this._isUsingW3CHeaders) {
|
||||
var traceparent = new Traceparent(ajaxData.traceID, ajaxData.spanID);
|
||||
init.headers.set(RequestHeaders.traceParentHeader, traceparent.toString());
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
ajaxData.requestHeaders[RequestHeaders.traceParentHeader] = traceparent.toString();
|
||||
}
|
||||
}
|
||||
return init;
|
||||
}
|
||||
return init;
|
||||
} else if (xhr) { // XHR
|
||||
if (this.currentWindowHost && CorrelationIdHelper.canIncludeCorrelationHeader(this._config, xhr.ajaxData.getAbsoluteUrl(),
|
||||
this.currentWindowHost)) {
|
||||
if (this._isUsingAIHeaders) {
|
||||
var id = "|" + xhr.ajaxData.traceID + "." + xhr.ajaxData.spanID;
|
||||
xhr.setRequestHeader(RequestHeaders.requestIdHeader, id);
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
xhr.ajaxData.requestHeaders[RequestHeaders.requestIdHeader] = id;
|
||||
}
|
||||
}
|
||||
var appId = this._config.appId || this._context.appId();
|
||||
if (appId) {
|
||||
xhr.setRequestHeader(RequestHeaders.requestContextHeader, RequestHeaders.requestContextAppIdFormat + appId);
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
xhr.ajaxData.requestHeaders[RequestHeaders.requestContextHeader] = RequestHeaders.requestContextAppIdFormat + appId;
|
||||
}
|
||||
}
|
||||
if (this._isUsingW3CHeaders) {
|
||||
var traceparent = new Traceparent(xhr.ajaxData.traceID, xhr.ajaxData.spanID);
|
||||
xhr.setRequestHeader(RequestHeaders.traceParentHeader, traceparent.toString());
|
||||
if (this._config.enableRequestHeaderTracking) {
|
||||
xhr.ajaxData.requestHeaders[RequestHeaders.traceParentHeader] = traceparent.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return xhr;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private getFailedFetchDiagnosticsMessage(input: Request | Response | string): string {
|
||||
let result: string = "";
|
||||
try {
|
||||
|
@ -590,7 +672,7 @@ export class AjaxMonitor implements ITelemetryPlugin, IDependenciesPlugin, IInst
|
|||
};
|
||||
|
||||
// enrich dependency target with correlation context from the server
|
||||
let correlationContext: string = this.getFetchCorrelationContext(response);
|
||||
const correlationContext: string = this.getFetchCorrelationContext(response);
|
||||
if (correlationContext) {
|
||||
dependency.correlationContext = correlationContext;
|
||||
}
|
||||
|
@ -646,7 +728,7 @@ export class AjaxMonitor implements ITelemetryPlugin, IDependenciesPlugin, IInst
|
|||
responseFinishedTime: ajaxData.responseFinishedTime
|
||||
});
|
||||
} else {
|
||||
let dependency: IDependencyTelemetry = {
|
||||
const dependency: IDependencyTelemetry = {
|
||||
id: "|" + ajaxData.traceID + "." + ajaxData.spanID,
|
||||
target: ajaxData.getAbsoluteUrl(),
|
||||
name: ajaxData.getPathName(),
|
||||
|
@ -673,7 +755,7 @@ export class AjaxMonitor implements ITelemetryPlugin, IDependenciesPlugin, IInst
|
|||
|
||||
private getFetchCorrelationContext(response: Response): string {
|
||||
try {
|
||||
let responseHeader: string = response.headers.get(RequestHeaders.requestContextHeader);
|
||||
const responseHeader: string = response.headers.get(RequestHeaders.requestContextHeader);
|
||||
return CorrelationIdHelper.getCorrelationContext(responseHeader);
|
||||
} catch (e) {
|
||||
this._core.logger.throwInternal(
|
||||
|
@ -686,86 +768,4 @@ export class AjaxMonitor implements ITelemetryPlugin, IDependenciesPlugin, IInst
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected instrumentXhr() {
|
||||
if (this.supportsAjaxMonitoring() && !this.initialized) {
|
||||
this.instrumentOpen();
|
||||
this.instrumentSend();
|
||||
this.instrumentAbort();
|
||||
this.instrumentSetRequestHeader();
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static getDefaultConfig(): ICorrelationConfig {
|
||||
const config: ICorrelationConfig = {
|
||||
maxAjaxCallsPerView: 500,
|
||||
disableAjaxTracking: false,
|
||||
disableFetchTracking: true,
|
||||
disableCorrelationHeaders: false,
|
||||
distributedTracingMode: DistributedTracingModes.AI,
|
||||
correlationHeaderExcludedDomains: [
|
||||
"*.blob.core.windows.net",
|
||||
"*.blob.core.chinacloudapi.cn",
|
||||
"*.blob.core.cloudapi.de",
|
||||
"*.blob.core.usgovcloudapi.net"],
|
||||
correlationHeaderDomains: undefined,
|
||||
appId: undefined,
|
||||
enableCorsCorrelation: false,
|
||||
enableRequestHeaderTracking: false,
|
||||
enableResponseHeaderTracking: false
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public static getEmptyConfig(): ICorrelationConfig {
|
||||
return {
|
||||
maxAjaxCallsPerView: undefined,
|
||||
disableAjaxTracking: undefined,
|
||||
disableFetchTracking: undefined,
|
||||
disableCorrelationHeaders: undefined,
|
||||
distributedTracingMode: undefined,
|
||||
correlationHeaderExcludedDomains: undefined,
|
||||
appId: undefined,
|
||||
enableCorsCorrelation: undefined,
|
||||
correlationHeaderDomains: undefined,
|
||||
enableRequestHeaderTracking: undefined,
|
||||
enableResponseHeaderTracking: undefined
|
||||
}
|
||||
}
|
||||
|
||||
public initialize(config: IConfiguration & IConfig, core: IAppInsightsCore, extensions: IPlugin[]) {
|
||||
if (!this.initialized && !this._fetchInitialized) {
|
||||
this._core = core;
|
||||
const defaultConfig = AjaxMonitor.getDefaultConfig();
|
||||
this._config = AjaxMonitor.getEmptyConfig();
|
||||
for (let field in defaultConfig) {
|
||||
this._config[field] = ConfigurationManager.getConfig(config, field, AjaxMonitor.identifier, defaultConfig[field]);
|
||||
}
|
||||
|
||||
this._isUsingAIHeaders = this._config.distributedTracingMode === DistributedTracingModes.AI || this._config.distributedTracingMode === DistributedTracingModes.AI_AND_W3C;
|
||||
this._isUsingW3CHeaders = this._config.distributedTracingMode === DistributedTracingModes.AI_AND_W3C || this._config.distributedTracingMode === DistributedTracingModes.W3C;
|
||||
|
||||
if (this._config.disableAjaxTracking === false) {
|
||||
this.instrumentXhr();
|
||||
}
|
||||
|
||||
if (this._config.disableFetchTracking === false) {
|
||||
this.instrumentFetch();
|
||||
}
|
||||
|
||||
if (extensions.length > 0 && extensions) {
|
||||
let propExt, extIx = 0;
|
||||
while (!propExt && extIx < extensions.length) {
|
||||
if (extensions[extIx] && extensions[extIx].identifier === PropertiesPluginIdentifier) {
|
||||
propExt = extensions[extIx]
|
||||
}
|
||||
extIx++;
|
||||
}
|
||||
if (propExt) {
|
||||
this._context = propExt.context; // we could move IPropertiesPlugin to common as well
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ export class XHRMonitoringState {
|
|||
public sendDone: boolean = false;
|
||||
public abortDone: boolean = false;
|
||||
|
||||
//<summary>True, if onreadyStateChangeCallback function attached to xhr, otherwise false</summary>
|
||||
// <summary>True, if onreadyStateChangeCallback function attached to xhr, otherwise false</summary>
|
||||
public onreadystatechangeCallbackAttached = false;
|
||||
}
|
||||
|
||||
|
@ -28,38 +28,38 @@ export class ajaxRecord {
|
|||
public requestSize = 0;
|
||||
public method = null;
|
||||
|
||||
///<summary>Returns the HTTP status code.</summary>
|
||||
/// <summary>Returns the HTTP status code.</summary>
|
||||
public status = null;
|
||||
|
||||
//<summary>The timestamp when open method was invoked</summary>
|
||||
// <summary>The timestamp when open method was invoked</summary>
|
||||
public requestSentTime = null;
|
||||
|
||||
//<summary>The timestamps when first byte was received</summary>
|
||||
// <summary>The timestamps when first byte was received</summary>
|
||||
public responseStartedTime = null;
|
||||
|
||||
//<summary>The timestamp when last byte was received</summary>
|
||||
// <summary>The timestamp when last byte was received</summary>
|
||||
public responseFinishedTime = null;
|
||||
|
||||
//<summary>The timestamp when onreadystatechange callback in readyState 4 finished</summary>
|
||||
// <summary>The timestamp when onreadystatechange callback in readyState 4 finished</summary>
|
||||
public callbackFinishedTime = null;
|
||||
|
||||
//<summary>The timestamp at which ajax was ended</summary>
|
||||
// <summary>The timestamp at which ajax was ended</summary>
|
||||
public endTime = null;
|
||||
|
||||
//<summary>The original xhr onreadystatechange event</summary>
|
||||
// <summary>The original xhr onreadystatechange event</summary>
|
||||
public originalOnreadystatechage = null;
|
||||
|
||||
public xhrMonitoringState: XHRMonitoringState = new XHRMonitoringState();
|
||||
|
||||
private _logger: IDiagnosticLogger;
|
||||
|
||||
//<summary>Determines whether or not JavaScript exception occured in xhr.onreadystatechange code. 1 if occured, otherwise 0.</summary>
|
||||
// <summary>Determines whether or not JavaScript exception occured in xhr.onreadystatechange code. 1 if occured, otherwise 0.</summary>
|
||||
public clientFailure = 0;
|
||||
|
||||
|
||||
public traceID: string;
|
||||
public spanID: string;
|
||||
|
||||
private _logger: IDiagnosticLogger;
|
||||
|
||||
constructor(traceID: string, spanID: string, logger: IDiagnosticLogger) {
|
||||
this.traceID = traceID;
|
||||
this.spanID = spanID;
|
||||
|
|
|
@ -5,9 +5,9 @@ import { CoreUtils } from '@microsoft/applicationinsights-core-js';
|
|||
|
||||
export class stringUtils {
|
||||
public static GetLength(strObject) {
|
||||
var res = 0;
|
||||
let res = 0;
|
||||
if (!CoreUtils.isNullOrUndefined(strObject)) {
|
||||
var stringified = "";
|
||||
let stringified = "";
|
||||
try {
|
||||
stringified = strObject.toString();
|
||||
} catch (ex) {
|
||||
|
@ -23,13 +23,13 @@ export class stringUtils {
|
|||
}
|
||||
|
||||
export class EventHelper {
|
||||
///<summary>Binds the specified function to an event, so that the function gets called whenever the event fires on the object</summary>
|
||||
///<param name="obj">Object to which </param>
|
||||
///<param name="eventNameWithoutOn">String that specifies any of the standard DHTML Events without "on" prefix</param>
|
||||
///<param name="handlerRef">Pointer that specifies the function to call when event fires</param>
|
||||
///<returns>True if the function was bound successfully to the event, otherwise false</returns>
|
||||
/// <summary>Binds the specified function to an event, so that the function gets called whenever the event fires on the object</summary>
|
||||
/// <param name="obj">Object to which </param>
|
||||
/// <param name="eventNameWithoutOn">String that specifies any of the standard DHTML Events without "on" prefix</param>
|
||||
/// <param name="handlerRef">Pointer that specifies the function to call when event fires</param>
|
||||
/// <returns>True if the function was bound successfully to the event, otherwise false</returns>
|
||||
public static AttachEvent(obj, eventNameWithoutOn, handlerRef) {
|
||||
var result = false;
|
||||
let result = false;
|
||||
if (!CoreUtils.isNullOrUndefined(obj)) {
|
||||
if (!CoreUtils.isNullOrUndefined(obj.attachEvent)) {
|
||||
// IE before version 9
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint:latest",
|
||||
"tslint-config-prettier"
|
||||
],
|
||||
"rules": {
|
||||
"insecure-random": true,
|
||||
"no-banned-terms": true,
|
||||
"no-cookies": true,
|
||||
"no-delete-expression": true,
|
||||
"no-disable-auto-sanitization": true,
|
||||
"no-document-domain": true,
|
||||
"no-document-write": true,
|
||||
"no-eval": true,
|
||||
"no-exec-script": true,
|
||||
"no-function-constructor-with-string-args": true,
|
||||
"no-http-string": [true, "http://www.example.com/?.*", "http://localhost:?.*"],
|
||||
"no-inner-html": true,
|
||||
"no-octal-literal": true,
|
||||
"no-reserved-keywords": true,
|
||||
"no-string-based-set-immediate": true,
|
||||
"no-string-based-set-interval": true,
|
||||
"no-string-based-set-timeout": true,
|
||||
"non-literal-require": true,
|
||||
"possible-timing-attack": true,
|
||||
"react-anchor-blank-noopener": true,
|
||||
"react-iframe-missing-sandbox": true,
|
||||
"react-no-dangerous-html": true,
|
||||
"ordered-imports": false,
|
||||
"variable-name": false,
|
||||
"member-access": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"no-bitwise": false,
|
||||
"one-variable-per-declaration": false,
|
||||
"max-classes-per-file": false,
|
||||
"no-console": false,
|
||||
"prefer-for-of": false,
|
||||
"class-name": false,
|
||||
"interface-name": false,
|
||||
"no-empty-interface": false,
|
||||
"no-string-literal": false,
|
||||
"no-reference": false,
|
||||
"no-empty": false,
|
||||
"forin": false,
|
||||
"ban-types": false,
|
||||
"no-shadowed-variable": false,
|
||||
"no-implicit-dependencies": false,
|
||||
"no-object-literal-type-assertion": false,
|
||||
"no-this-assignment":false,
|
||||
"prefer-conditional-expression": false,
|
||||
"unified-signatures": false
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче