Skip to content

Commit

Permalink
XHR/Fetch enhancement for office - add additional telemetry from wind…
Browse files Browse the repository at this point in the history
…ow.performance #1194
  • Loading branch information
Nev Wylie committed Feb 26, 2020
1 parent 2019361 commit 404eda5
Show file tree
Hide file tree
Showing 39 changed files with 2,840 additions and 1,171 deletions.
39 changes: 37 additions & 2 deletions AISKU/Tests/TestFramework/TestClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,16 @@ class TestClass {

/** Called when the test is completed. */
private _testCompleted(failed: boolean) {
if (this.useFakeTimers) {
this.clock.restore();
}

if (this.useFakeServer && this.server) {
this.server.restore();
}

this._cleanupAllHooks();

if (failed) {
// Just cleanup the sandbox since the test has already failed.
this.sandbox.restore();
Expand All @@ -194,6 +204,31 @@ class TestClass {
TestClass.currentTestInfo = null;
}

private _removeFuncHooks(fn:any) {
if (typeof fn === "function") {
let aiHook:any = fn["_aiHooks"];

if (aiHook && aiHook.h) {
aiHook.h = [];
}
}
}

private _removeHooks(target:any) {
Object.keys(target).forEach(name => {
try {
this._removeFuncHooks(target[name]);
} catch (e) {
}
});
}

private _cleanupAllHooks() {
this._removeHooks(XMLHttpRequest.prototype);
this._removeHooks(XMLHttpRequest);
this._removeFuncHooks(window.fetch);
}

/**** Sinon methods and properties ***/

// These methods and properties are injected by Sinon and will override the implementation here.
Expand Down Expand Up @@ -240,13 +275,13 @@ class TestClass {
JSON.stringify(data));
}

public getPayloadMessages(spy:SinonSpy, ignoreInit:boolean = false) {
public getPayloadMessages(spy:SinonSpy, includeInit:boolean = false) {
let resultPayload = [];
if (spy.called && spy.args && spy.args.length > 0) {
spy.args.forEach(call => {
call[0].forEach(message => {
// Ignore the internal SendBrowserInfoOnUserInit message (Only occurs when running tests in a browser)
if (ignoreInit || message.indexOf("AI (Internal): 72 ") === -1) {
if (includeInit || message.indexOf("AI (Internal): 72 ") === -1) {
resultPayload.push(message);
}
})
Expand Down
196 changes: 101 additions & 95 deletions AISKU/Tests/applicationinsights.e2e.tests.ts

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions AISKU/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"typescript": "2.5.3"
},
"dependencies": {
"@microsoft/dynamicproto-js": "^0.5.1",
"@microsoft/applicationinsights-analytics-js": "2.4.4",
"@microsoft/applicationinsights-channel-js": "2.4.4",
"@microsoft/applicationinsights-common": "2.4.4",
Expand Down
3 changes: 3 additions & 0 deletions AISKU/rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import nodeResolve from "rollup-plugin-node-resolve";
import {uglify} from "rollup-plugin-uglify";
import replace from "rollup-plugin-replace";
import dynamicRemove from "@microsoft/dynamicproto-js/tools/rollup/node/removedynamic";
import { es3Poly, es3Check, importCheck } from "@microsoft/applicationinsights-rollup-es3";

const version = require("./package.json").version;
Expand All @@ -22,6 +23,7 @@ const browserRollupConfigFactory = (isProduction, libVersion = '2') => {
sourcemap: true
},
plugins: [
dynamicRemove(),
replace({
delimiters: ["", ""],
values: {
Expand Down Expand Up @@ -65,6 +67,7 @@ const nodeUmdRollupConfigFactory = (isProduction) => {
sourcemap: true
},
plugins: [
dynamicRemove(),
replace({
delimiters: ["", ""],
values: {
Expand Down
1 change: 1 addition & 0 deletions AISKULight/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"tslint-config-prettier": "^1.18.0"
},
"dependencies": {
"@microsoft/dynamicproto-js": "^0.5.1",
"@microsoft/applicationinsights-common": "2.4.4",
"@microsoft/applicationinsights-channel-js": "2.4.4",
"@microsoft/applicationinsights-core-js": "2.4.4"
Expand Down
3 changes: 3 additions & 0 deletions AISKULight/rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import nodeResolve from "rollup-plugin-node-resolve";
import {uglify} from "rollup-plugin-uglify";
import replace from "rollup-plugin-replace";
import dynamicRemove from "@microsoft/dynamicproto-js/tools/rollup/node/removedynamic";
import { es3Poly, es3Check, importCheck } from "@microsoft/applicationinsights-rollup-es3";

const version = require("./package.json").version;
Expand All @@ -22,6 +23,7 @@ const browserRollupConfigFactory = (isProduction, libV = '2') => {
sourcemap: true
},
plugins: [
dynamicRemove(),
replace({
delimiters: ["", ""],
values: {
Expand Down Expand Up @@ -65,6 +67,7 @@ const nodeUmdRollupConfigFactory = (isProduction) => {
sourcemap: true
},
plugins: [
dynamicRemove(),
replace({
delimiters: ["", ""],
values: {
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ Most configuration fields are named such that they can be defaulted to falsey. A
| enableRequestHeaderTracking | false | If true, AJAX & Fetch request headers is tracked, default is false.
| enableResponseHeaderTracking | false | If true, AJAX & Fetch request's response headers is tracked, default is false.
| enableAjaxErrorStatusText | false | Default false. If true, include response error data text in dependency event on failed AJAX requests.
| enableAjaxPerfTracking | false | Default false. Flag to enable looking up and including additional browser window.performance timings in the reported ajax (XHR and fetch) reported metrics.
| maxAjaxPerfLookupAttempts | 3 | Defaults to 3. The maximum number of times to look for the window.performance timings (if available), this is required as not all browsers populate the window.performance before reporting the end of the XHR request and for fetch requests this is added after its complete.
| ajaxPerfLookupDelay | 25 | Defaults to 25ms. The amount of time to wait before re-attempting to find the windows.performance timings for an ajax request, time is in milliseconds and is passed directly to setTimeout().
| distributedTracingMode | `DistributedTracingModes.AI` | Sets the distributed tracing mode. If AI_AND_W3C mode or W3C mode is set, W3C trace context headers (traceparent/tracestate) will be generated and included in all outgoing requests. AI_AND_W3C is provided for back-compatibility with any legacy Application Insights instrumented services.
| enableUnhandledPromiseRejectionTracking | false | If true, unhandled promise rejections will be autocollected and reported as a javascript error. When disableExceptionTracking is true (dont track exceptions) the config value will be ignored and unhandled promise rejections will not be reported.
## Single Page Applications
Expand Down
64 changes: 64 additions & 0 deletions common/Tests/Selenium/ModuleLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
function ModuleLoader(config) {

if (config) {
require.config(config);
}

var requiredModules = [];

function processModules() {
if (requiredModules.length > 0) {
var module = requiredModules.shift();
if (module.prepare) {
module.prepare();
}

if (module.config) {
require.config(config);
}

require([module.path], function (theModule) {
if (module.run) {
module.run(theModule);
} else {
console && console.log("Loaded module - " + module.name + " from " + module.path);
define(module.name, function() {
if (module.asDefault) {
console && console.log("Returning default module - " + module.name);
return {
"default": theModule
}
}
console && console.log("Returning module - " + module.name);
return theModule
});
}

processModules();
},
function (err) {
console && console.log("Failed to load [" + module.name + "] from [" + module.path + "]\n - Require ERROR: " + err.toString());
if (module.path.toLowercase() !== module.path) {
console && console.log(" ** Validate the path it may need to be all lowercase -- " + module.path);
}
});
}
}

function addModule(name, path, asDefault) {
var moduleDef = {
name: name,
path: !path ? name : path,
asDefault: asDefault
}

requiredModules.push(moduleDef);

return moduleDef;
}

return {
add: addModule,
run: processModules
}
}
5 changes: 5 additions & 0 deletions common/config/rush/npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -8,44 +8,42 @@
<link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-1.23.1.css">
<script src="http://sinonjs.org/releases/sinon-2.3.8.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.js"></script>
<script src="../../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="appinsights-analytics.tests.js"></script>

<script>
require.config({
var modules = new ModuleLoader({
baseUrl: '../../',
paths: {
core: 'node_modules/@microsoft/applicationinsights-core-js/browser/',
common: 'node_modules/@microsoft/applicationinsights-common/browser/',
qunit: "Tests/External/qunit-1.23.1"
qunit: "Tests/External/qunit-1.23.1",
dynamicProto: 'node_modules/@microsoft/dynamicproto-js/lib/dist/umd/'
}
});

// Load core & common before channel tests
require([
'qunit', // Load qunit here instead of with tests, otherwise will not work
'core/applicationinsights-core-js',
], function (QUnit, aicore) {
// aichannel is expecting to import from i.e '@microsoft/applicationinsights-core-js', so create a temp module
define('@microsoft/applicationinsights-core-js', function () { return aicore; });

require(['common/applicationinsights-common'], function (aicommon) {
define('@microsoft/applicationinsights-common', function () { return aicommon; });
require.config({
baseUrl: './'
});

require([
"Tests/Selenium/appinsights-analytics.tests"
], function (tests) {
QUnit.start();
tests.runTests();
}, function (err) {
console.log('REQUIRE ERROR:', err.toString());
});
})

// Load qunit here instead of with tests, otherwise will not work
modules.add("qunit");
// Load dynamicProto
modules.add("@microsoft/dynamicproto-js", "dynamicProto/dynamicproto-js", true);
// Load Core
modules.add("@microsoft/applicationinsights-core-js", "core/applicationinsights-core-js");
// aichannel is expecting to import from i.e '@microsoft/applicationinsights-core-js', so create a temp module
modules.add("@microsoft/applicationinsights-common", "common/applicationinsights-common");

});
var testModule = modules.add("Tests/Selenium/appinsights-analytics.tests")
testModule.config = {
baseUrl: './'
};

testModule.run = function (tests) {
console && console.log("Starting tests");
QUnit.start();
tests.runTests();
};

modules.run();
</script>
</head>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,20 @@ class TestClass {

config.injectInto = config.injectIntoThis && this || config.injectInto;
this.sandbox = sinon.sandbox.create(config);
this.server = this.sandbox.server;
if (config.useFakeServer) {
this.server = this.sandbox.server;
} else {
this.server = null;
}

// Allow the derived class to perform test initialization.
this.testInitialize();
}

/** Called when the test is completed. */
private _testCompleted(failed?: boolean) {
this._cleanupAllHooks();

if (failed) {
// Just cleanup the sandbox since the test has already failed.
this.sandbox.restore();
Expand All @@ -231,6 +237,31 @@ class TestClass {
// Clear the instance of the currently running suite.
TestClass.currentTestClass = null;
}

private _removeFuncHooks(fn:any) {
if (typeof fn === "function") {
let aiHook:any = fn["_aiHooks"];

if (aiHook && aiHook.h) {
aiHook.h = [];
}
}
}

private _removeHooks(target:any) {
Object.keys(target).forEach(name => {
try {
this._removeFuncHooks(target[name]);
} catch (e) {
}
});
}

private _cleanupAllHooks() {
this._removeHooks(XMLHttpRequest.prototype);
this._removeHooks(XMLHttpRequest);
this._removeFuncHooks(window.fetch);
}
}

// Configure Sinon
Expand Down
1 change: 1 addition & 0 deletions extensions/applicationinsights-analytics-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"tslint-config-prettier": "^1.18.0"
},
"dependencies": {
"@microsoft/dynamicproto-js": "^0.5.1",
"@microsoft/applicationinsights-core-js": "2.4.4",
"@microsoft/applicationinsights-common": "2.4.4",
"tslib": "1.10.0"
Expand Down
3 changes: 3 additions & 0 deletions extensions/applicationinsights-analytics-js/rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import nodeResolve from "rollup-plugin-node-resolve";
import {uglify} from "rollup-plugin-uglify";
import replace from "rollup-plugin-replace";
import dynamicRemove from "@microsoft/dynamicproto-js/tools/rollup/node/removedynamic";
import { es3Poly, es3Check, importCheck } from "@microsoft/applicationinsights-rollup-es3";

const version = require("./package.json").version;
Expand All @@ -23,6 +24,7 @@ const browserRollupConfigFactory = isProduction => {
sourcemap: true
},
plugins: [
dynamicRemove(),
replace({
delimiters: ["", ""],
values: {
Expand Down Expand Up @@ -66,6 +68,7 @@ const nodeUmdRollupConfigFactory = (isProduction) => {
sourcemap: true
},
plugins: [
dynamicRemove(),
replace({
delimiters: ["", ""],
values: {
Expand Down
Loading

0 comments on commit 404eda5

Please sign in to comment.