fix lint error for analytics and dependencies plugin

This commit is contained in:
Xiao Li 2019-09-06 12:00:13 -07:00
Родитель 156c25f928
Коммит 60da0f7506
22 изменённых файлов: 1170 добавлений и 1059 удалений

Просмотреть файл

@ -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
}
}