Add the Aborted flag to the dependency initializer / listeners #1970 (#1982)

- Remove SimpleSyncPromise as it's no longer needed
This commit is contained in:
Nev 2023-02-01 16:58:24 -08:00 коммит произвёл GitHub
Родитель 9141ebf151
Коммит c920fb782a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
24 изменённых файлов: 312 добавлений и 155 удалений

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

@ -13,8 +13,6 @@
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({

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

@ -9,8 +9,7 @@
<script src="../../../common/Tests/External/sinon-2.3.8.js"></script>
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({
baseUrl: '../',

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

@ -9,7 +9,6 @@
<script src="../../../common/Tests/External/sinon-2.3.8.js"></script>
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({

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

@ -9,7 +9,6 @@
<!-- <script src="../../../common/Tests/External/sinon-7.3.1.js"></script> -->
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({
baseUrl: '../',

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

@ -34,6 +34,7 @@
"grunt": "^1.5.3",
"grunt-contrib-qunit": "^6.2.1",
"@nevware21/grunt-ts-plugin": "^0.4.3",
"@nevware21/ts-async": "^0.1.0",
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-replace": "^2.3.3",

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

@ -15,7 +15,6 @@ function loadFetchModule(moduleLoader, name) {
window.Headers = window.Headers || polyFetch.Headers;
window.Response = window.Response || polyFetch.Response;
window.Request = window.Request || polyFetch.Request;
window.Promise = window.Promise || SimpleSyncPromise;
var usePolyFetch = getParameterByName("polyFetch");
if (usePolyFetch) {

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

@ -1,88 +0,0 @@
/**
* Implementing a simple synchronous promise interface as PhantomJS doesn't support / implement the es6 Promise as used by fetch
*/
function SimpleSyncPromise(executor) {
var _self = this;
var _state = "pending";
var _settledValue = null;
var _queue = [];
_self.then = function (onResolved, onRejected) {
return new SimpleSyncPromise(function (resolve, reject) {
// Queue the new promise returned to be resolved or rejected
// when this promise settles.
_enqueue(onResolved, onRejected, resolve, reject);
});
};
_self["catch"] = function (onRejected) {
// Just return an empty promise as this doesn't support rejection
return _self.then(null, onRejected);
}
function _enqueue(onResolved, onRejected, resolve, reject) {
_queue.push(function () {
var value;
try {
if (_state === "resolved") {
value = typeof onResolved === "function" ? onResolved(_settledValue) : _settledValue;
} else {
value = typeof onRejected === "function" ? onRejected(_settledValue) : _settledValue;
}
if (value instanceof SimpleSyncPromise) {
// The called handlers returned a new promise, so the chained promise
// will follow the state of this promise.
value.then(resolve, reject);
} else if (_state === "rejected" && typeof onRejected !== "function") {
// If there wasn't an onRejected handler and this promise is rejected, then
// the chained promise also rejects with the same reason.
reject(value);
} else {
// If this promise is fulfilled, then the chained promise is also fulfilled
// with either the settled value of this promise (if no onFulfilled handler
// was available) or the return value of the handler. If this promise is
// rejected and there was an onRejected handler, then the chained promise is
// fulfilled with the return value of the handler.
resolve(value);
}
} catch (e) {
reject(e);
}
});
if (_state !== "pending") {
_processQueue();
}
}
function _processQueue() {
if (_queue.length > 0) {
var pending = _queue.slice();
_queue = [];
for (var i = 0, len = pending.length; i < len; ++i) {
pending[i]();
}
}
}
function _resolve(value) {
if (_state === "pending") {
_settledValue = value;
_state = "resolved";
_processQueue();
}
}
function _reject(reason) {
if (_state === "pending") {
_settledValue = reason;
_state = "rejected";
_processQueue();
}
}
(function _initialize() {
try {
executor(_resolve, _reject);
} catch (e) {
_reject(e);
}
})();
}

63
common/config/rush/npm-shrinkwrap.json сгенерированный
Просмотреть файл

@ -12,6 +12,7 @@
"@microsoft/dynamicproto-js": "^1.1.7",
"@nevware21/grunt-eslint-ts": "^0.2.2",
"@nevware21/grunt-ts-plugin": "^0.4.3",
"@nevware21/ts-async": "^0.1.0",
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-replace": "^2.3.3",
@ -413,6 +414,24 @@
"typescript": ">=1"
}
},
"node_modules/@nevware21/ts-async": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.1.0.tgz",
"integrity": "sha512-O8tHb7bl7+QBaPNPQFaFONAxAdPhuAmE4rvjWKxb2VKikAl6h/WlNuXd0F/4jwZx57LdWflUokh8G2cWTRf24Q==",
"peerDependencies": {
"@nevware21/ts-utils": ">= 0.7 < 2.x",
"typescript": ">=1"
}
},
"node_modules/@nevware21/ts-utils": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.7.0.tgz",
"integrity": "sha512-IUAjuaIekPVnLlzprfOnppE8vPTtstI4ESHz6JEzboX+BlI8WLc76PhNtxAzhkBs1glGQ5zot4L8sA8tMNgmTQ==",
"peer": true,
"peerDependencies": {
"typescript": ">=1"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -520,10 +539,11 @@
"node_modules/@rush-temp/ai-test-framework": {
"version": "0.0.0",
"resolved": "file:projects/ai-test-framework.tgz",
"integrity": "sha512-dkn8ZUzeaneGbu8n+QIShhtR5nB+YQaDdOPOIrHNEEtwqfV7zHHwNQ7SvBaVCd+dbqS6gwn4/uViVj+gIZzyrA==",
"integrity": "sha512-/46+N6gn1YXvalhor8Js+EEHD3ufhqYVwwHIEJVeSRAHMtWZZC36yHWPC4ihKXsJsGo4T6UuU2lgi5AK+SjalQ==",
"dependencies": {
"@microsoft/dynamicproto-js": "^1.1.7",
"@nevware21/grunt-ts-plugin": "^0.4.3",
"@nevware21/ts-async": "^0.1.0",
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-replace": "^2.3.3",
@ -722,12 +742,13 @@
"node_modules/@rush-temp/applicationinsights-dependencies-js": {
"version": "0.0.0",
"resolved": "file:projects/applicationinsights-dependencies-js.tgz",
"integrity": "sha512-UlxsHu42JM4o2VgtqN4zQjn2UP9o21oCRQUnOx4biuaeBt6mXmm5XTFjacEEBAUzOGWAZ97daSRzxtalIL3zIw==",
"integrity": "sha512-GqQkRS6fd2dkwogUPMVrv8MzUzhnR+VyUuJCmujommkykbmlF65Z5v+VJfuOjS5SwMRyg+CShqpOvvHIaQXxBg==",
"dependencies": {
"@microsoft/api-extractor": "^7.18.1",
"@microsoft/dynamicproto-js": "^1.1.7",
"@nevware21/grunt-eslint-ts": "^0.2.2",
"@nevware21/grunt-ts-plugin": "^0.4.3",
"@nevware21/ts-async": "^0.1.0",
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-replace": "^2.3.3",
@ -2726,19 +2747,6 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@ -5721,6 +5729,19 @@
"integrity": "sha512-4ALgho4uvEBMFu5jiN+bVzGMbpCGPrKjb47jus8T3zlBpi+k/swe6lKb5j6HGQ7EBSN7C41mLU02GMo9ZSgX3Q==",
"requires": {}
},
"@nevware21/ts-async": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.1.0.tgz",
"integrity": "sha512-O8tHb7bl7+QBaPNPQFaFONAxAdPhuAmE4rvjWKxb2VKikAl6h/WlNuXd0F/4jwZx57LdWflUokh8G2cWTRf24Q==",
"requires": {}
},
"@nevware21/ts-utils": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.7.0.tgz",
"integrity": "sha512-IUAjuaIekPVnLlzprfOnppE8vPTtstI4ESHz6JEzboX+BlI8WLc76PhNtxAzhkBs1glGQ5zot4L8sA8tMNgmTQ==",
"peer": true,
"requires": {}
},
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -5799,10 +5820,11 @@
},
"@rush-temp/ai-test-framework": {
"version": "file:projects\\ai-test-framework.tgz",
"integrity": "sha512-dkn8ZUzeaneGbu8n+QIShhtR5nB+YQaDdOPOIrHNEEtwqfV7zHHwNQ7SvBaVCd+dbqS6gwn4/uViVj+gIZzyrA==",
"integrity": "sha512-/46+N6gn1YXvalhor8Js+EEHD3ufhqYVwwHIEJVeSRAHMtWZZC36yHWPC4ihKXsJsGo4T6UuU2lgi5AK+SjalQ==",
"requires": {
"@microsoft/dynamicproto-js": "^1.1.7",
"@nevware21/grunt-ts-plugin": "^0.4.3",
"@nevware21/ts-async": "^0.1.0",
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-replace": "^2.3.3",
@ -5993,12 +6015,13 @@
},
"@rush-temp/applicationinsights-dependencies-js": {
"version": "file:projects\\applicationinsights-dependencies-js.tgz",
"integrity": "sha512-UlxsHu42JM4o2VgtqN4zQjn2UP9o21oCRQUnOx4biuaeBt6mXmm5XTFjacEEBAUzOGWAZ97daSRzxtalIL3zIw==",
"integrity": "sha512-GqQkRS6fd2dkwogUPMVrv8MzUzhnR+VyUuJCmujommkykbmlF65Z5v+VJfuOjS5SwMRyg+CShqpOvvHIaQXxBg==",
"requires": {
"@microsoft/api-extractor": "^7.18.1",
"@microsoft/dynamicproto-js": "^1.1.7",
"@nevware21/grunt-eslint-ts": "^0.2.2",
"@nevware21/grunt-ts-plugin": "^0.4.3",
"@nevware21/ts-async": "^0.1.0",
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-replace": "^2.3.3",
@ -7552,12 +7575,6 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"optional": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",

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

@ -12,7 +12,6 @@
<!-- <script src="../../../common/Tests/External/require-2.3.6.js" crossorigin="anonymous"></script> -->
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({

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

@ -9,7 +9,6 @@
<script src="../../../common/Tests/External/sinon-2.3.8.js"></script>
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({

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

@ -1,5 +1,6 @@
import { SinonStub } from "sinon";
import { Assert, AITestClass, PollingAssert } from "@microsoft/ai-test-framework";
import { createSyncPromise } from "@nevware21/ts-async";
import { AjaxMonitor } from "../../../src/ajax";
import { DisabledPropertyName, IConfig, DistributedTracingModes, RequestHeaders, IDependencyTelemetry, IRequestContext, formatTraceParent, createTraceParent } from "@microsoft/applicationinsights-common";
import {
@ -22,7 +23,7 @@ function hookFetch<T>(executor: (resolve: (value?: T | PromiseLike<T>) => void,
input,
init
});
return new window["SimpleSyncPromise"](executor);
return createSyncPromise(executor);
}
return calls;
@ -641,6 +642,7 @@ export class AjaxTests extends AITestClass {
data = trackStub.args[1][0].baseData;
Assert.equal("Ajax", data.type, "request is Ajax type");
Assert.equal(undefined, data.properties.responseText, "xhr request's reponse error is not stored in part C");
Assert.equal(undefined, data.properties.aborted, "The aborted flag should not be set");
}
});
@ -686,6 +688,116 @@ export class AjaxTests extends AITestClass {
}
});
this.testCase({
name: "Ajax: xhr abort is tracked as part C data when enableAjaxErrorStatusText flag is true",
test: () => {
this._ajax = new AjaxMonitor();
let appInsightsCore = new AppInsightsCore();
let coreConfig: IConfiguration & IConfig = { instrumentationKey: "abc", disableAjaxTracking: false, enableAjaxErrorStatusText: true };
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
var trackStub = this.sandbox.stub(appInsightsCore, "track");
// act
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://microsoft.com");
xhr.send();
xhr.abort();
// assert
Assert.ok(trackStub.calledOnce, "track is called");
let data = trackStub.args[0][0].baseData;
Assert.equal("Ajax", data.type, "request is Ajax type");
Assert.equal(0, data.responseCode, "Check the response code");
Assert.equal(true, data.properties.aborted, "The aborted flag should be set");
Assert.notEqual(undefined, data.properties.responseText, "xhr request's reponse error is stored in part C");
Assert.strictEqual("", data.properties.responseText, "Check the status Text");
}
});
this.testCase({
name: "Ajax: xhr abort is tracked as part C data when enableAjaxErrorStatusText flag is false",
test: () => {
this._ajax = new AjaxMonitor();
let appInsightsCore = new AppInsightsCore();
let coreConfig: IConfiguration & IConfig = { instrumentationKey: "abc", disableAjaxTracking: false, enableAjaxErrorStatusText: false };
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
var trackStub = this.sandbox.stub(appInsightsCore, "track");
// act
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://microsoft.com");
xhr.send();
xhr.abort();
// assert
Assert.ok(trackStub.calledOnce, "track is called");
let data = trackStub.args[0][0].baseData;
Assert.equal("Ajax", data.type, "request is Ajax type");
Assert.equal(0, data.responseCode, "Check the response code");
Assert.equal(true, data.properties.aborted, "The aborted flag should be set");
Assert.equal(undefined, data.properties.responseText, "xhr request's reponse error is stored in part C");
}
});
this.testCase({
name: "Ajax: xhr respond with status code zero is tracked as part C data when enableAjaxErrorStatusText flag is true",
test: () => {
this._ajax = new AjaxMonitor();
let appInsightsCore = new AppInsightsCore();
let coreConfig: IConfiguration & IConfig = { instrumentationKey: "abc", disableAjaxTracking: false, enableAjaxErrorStatusText: true };
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
var trackStub = this.sandbox.stub(appInsightsCore, "track");
// act
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://microsoft.com");
xhr.send();
(<any>xhr).respond(0);
// assert
Assert.ok(trackStub.calledOnce, "track is called");
let data = trackStub.args[0][0].baseData;
Assert.equal("Ajax", data.type, "request is Ajax type");
Assert.equal(0, data.responseCode, "Check the response code");
Assert.equal(undefined, data.properties.aborted, "The aborted flag should be set");
Assert.notEqual(undefined, data.properties.responseText, "xhr request's reponse error is stored in part C");
Assert.strictEqual("", data.properties.responseText, "Check the status Text");
}
});
this.testCase({
name: "Ajax: xhr respond with status code zero is tracked as part C data when enableAjaxErrorStatusText flag is false",
test: () => {
this._ajax = new AjaxMonitor();
let appInsightsCore = new AppInsightsCore();
let coreConfig: IConfiguration & IConfig = { instrumentationKey: "abc", disableAjaxTracking: false, enableAjaxErrorStatusText: false };
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
var trackStub = this.sandbox.stub(appInsightsCore, "track");
// act
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://microsoft.com");
xhr.send();
(<any>xhr).respond(0);
// assert
Assert.ok(trackStub.calledOnce, "track is called");
let data = trackStub.args[0][0].baseData;
Assert.equal("Ajax", data.type, "request is Ajax type");
Assert.equal(0, data.responseCode, "Check the response code");
Assert.equal(undefined, data.properties.aborted, "The aborted flag should be set");
Assert.equal(undefined, data.properties.responseText, "xhr request's reponse error is stored in part C");
}
});
this.testCaseAsync({
name: "Fetch: fetch with disabled flag isn't tracked",
stepDelay: 10,
@ -1042,6 +1154,108 @@ export class AjaxTests extends AITestClass {
}]
});
this.testCaseAsync({
name: "Fetch: Respond with status 0 and no status text",
stepDelay: 10,
autoComplete: false,
timeOut: 10000,
steps: [ (testContext) => {
hookFetch((resolve) => {
AITestClass.orgSetTimeout(function() {
resolve({
headers: new Headers(),
ok: true,
body: null,
bodyUsed: false,
redirected: false,
status: 0,
statusText: "Blocked",
trailer: null,
type: "basic",
url: "https://httpbin.org/status/200"
});
}, 0);
});
this._ajax = new AjaxMonitor();
let dependencyFields = hookTrackDependencyInternal(this._ajax);
let appInsightsCore = new AppInsightsCore();
let coreConfig = { instrumentationKey: "", disableFetchTracking: false };
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
let fetchSpy = this.sandbox.spy(appInsightsCore, "track")
let throwSpy = this.sandbox.spy(appInsightsCore.logger, "throwInternal");
// Act
Assert.ok(fetchSpy.notCalled, "No fetch called yet");
fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => {
// Assert
Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch");
let data = fetchSpy.args[0][0].baseData;
Assert.equal("Fetch", data.type, "request is Fetch type");
Assert.ok(throwSpy.notCalled, "Make sure we didn't fail internally");
Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called");
Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called");
Assert.equal(0, dependencyFields[0].dependency.responseCode, "Check the response code");
Assert.equal(undefined, dependencyFields[0].dependency.properties.responseText);
testContext.testDone();
}, () => {
Assert.ok(false, "fetch failed!");
testContext.testDone();
});
}]
});
this.testCaseAsync({
name: "Fetch: Respond with status 0 and no status text",
stepDelay: 10,
autoComplete: false,
timeOut: 10000,
steps: [ (testContext) => {
hookFetch((resolve) => {
AITestClass.orgSetTimeout(function() {
resolve({
headers: new Headers(),
ok: true,
body: null,
bodyUsed: false,
redirected: false,
status: 0,
statusText: "Blocked",
trailer: null,
type: "basic",
url: "https://httpbin.org/status/200"
});
}, 0);
});
this._ajax = new AjaxMonitor();
let dependencyFields = hookTrackDependencyInternal(this._ajax);
let appInsightsCore = new AppInsightsCore();
let coreConfig = { instrumentationKey: "", disableFetchTracking: false, enableAjaxErrorStatusText: true };
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
let fetchSpy = this.sandbox.spy(appInsightsCore, "track")
let throwSpy = this.sandbox.spy(appInsightsCore.logger, "throwInternal");
// Act
Assert.ok(fetchSpy.notCalled, "No fetch called yet");
fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => {
// Assert
Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch");
let data = fetchSpy.args[0][0].baseData;
Assert.equal("Fetch", data.type, "request is Fetch type");
Assert.ok(throwSpy.notCalled, "Make sure we didn't fail internally");
Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called");
Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called");
Assert.equal(0, dependencyFields[0].dependency.responseCode, "Check the response code");
Assert.equal("Blocked", dependencyFields[0].dependency!.properties.responseText);
testContext.testDone();
}, () => {
Assert.ok(false, "fetch failed!");
testContext.testDone();
});
}]
});
this.testCaseAsync({
name: "Fetch: fetch addDependencyInitializer adding context",
stepDelay: 10,
@ -2864,7 +3078,12 @@ export class AjaxPerfTrackTests extends AITestClass {
.concat(PollingAssert.createPollingAssert(() => {
let trackStub = this._context["trackStub"] as SinonStub;
if (this._context["fetchComplete"]) {
Assert.ok(trackStub.notCalled, "No fetch called yet");
Assert.ok(trackStub.calledOnce, "track is called");
let data = trackStub.args[0][0].baseData;
Assert.equal("Fetch", data.type, "request is Fetch type");
let props = data.properties;
Assert.notEqual(undefined, props, "Should contain properties");
Assert.equal(undefined, props.ajaxPerf, "No performance data should exist");
return true;
}
@ -3200,7 +3419,7 @@ export class AjaxFrozenTests extends AITestClass {
return false;
}, 'response received', 60, 1000) as any)
});
// This is currently a manual test as we don't have hooks / mocks defined to automated this today
// this.testCaseAsync({
// name: "AjaxFrozenTests: check frozen prototype",

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

@ -9,7 +9,6 @@
<script src="../../../common/Tests/External/sinon-2.3.8.js"></script>
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({
@ -32,6 +31,9 @@
// Load Common
modules.add("@microsoft/applicationinsights-common", "./node_modules/@microsoft/applicationinsights-common/browser/applicationinsights-common");
// Load Common
modules.add("@nevware21/ts-async", "./node_modules/@nevware21/ts-async/dist/es5/umd/ts-async");
var testModule = modules.add("Tests/Unit/src/dependencies.tests", "./Unit/dist/dependencies.tests.js")
testModule.run = function (tests) {
console && console.log("Starting tests");

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

@ -26,6 +26,7 @@
"ai-restore": "grunt deps-restore"
},
"devDependencies": {
"@nevware21/ts-async": "^0.1.0",
"@microsoft/ai-test-framework": "0.0.1",
"@microsoft/applicationinsights-rollup-plugin-uglify3-js": "1.0.0",
"@microsoft/applicationinsights-rollup-es3": "1.1.3",

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

@ -23,6 +23,11 @@ export interface IDependencyInitializerDetails {
* The context that the application can assigned via the dependency listener(s)
*/
context?: { [key: string]: any };
/**
* [Optional] A flag that indicates whether the client request was manually aborted by the `abort()`
*/
aborted?: boolean;
}
/**

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

@ -51,6 +51,13 @@ export interface IDependencyListenerDetails {
* [Optional] Context that the application can assign that will also be passed to any dependency initializer
*/
context?: { [key: string]: any };
/**
* [Optional] A flag that indicates whether the client request was manually aborted by the `abort()`,
* as listeners are called just before the request is sent it is unlikely that an application would have
* called `abort` before `send` this is also available in the dependency initializer.
*/
aborted?: boolean;
}
/**

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

@ -23,7 +23,7 @@ import { IAjaxRecordResponse, ajaxRecord } from "./ajaxRecord";
const AJAX_MONITOR_PREFIX = "ai.ajxmn.";
const strDiagLog = "diagLog";
const strAjaxData = "ajaxData";
const strFetch = "fetch";
const STR_FETCH = "fetch";
const ERROR_HEADER = "Failed to monitor XMLHttpRequest";
const ERROR_PREFIX = ", monitoring data for this ajax call ";
@ -47,11 +47,11 @@ function _supportsFetch(): (input: RequestInfo, init?: RequestInit) => Promise<R
if (!_global ||
isNullOrUndefined((_global as any).Request) ||
isNullOrUndefined((_global as any).Request[strPrototype]) ||
isNullOrUndefined(_global[strFetch])) {
isNullOrUndefined(_global[STR_FETCH])) {
return null;
}
return _global[strFetch];
return _global[STR_FETCH];
}
/**
@ -196,7 +196,8 @@ function _processDependencyListeners(listeners: _IInternalDependencyHandler<Depe
traceId: ajaxData.traceID,
spanId: ajaxData.spanID,
traceFlags: ajaxData.traceFlags,
context: ajaxData.context || {}
context: ajaxData.context || {},
aborted: !!ajaxData.aborted
};
_processDependencyContainer(core, listeners, details, "listener");
@ -559,7 +560,7 @@ export class AjaxMonitor extends BaseTelemetryPlugin implements IDependenciesPlu
let global = getGlobal();
let isPolyfill = (fetch as any).polyfill;
if (!_disableFetchTracking && !_fetchInitialized) {
_addHook(InstrumentFunc(global, strFetch, {
_addHook(InstrumentFunc(global, STR_FETCH, {
ns: _evtNamespace,
// Add request hook
req: (callDetails: IInstrumentCallDetails, input, init) => {
@ -585,12 +586,12 @@ export class AjaxMonitor extends BaseTelemetryPlugin implements IDependenciesPlu
callDetails.rslt = callDetails.rslt.then((response: any) => {
_reportFetchMetrics(callDetails, (response||{}).status, input, response, fetchData, () => {
let ajaxResponse:IAjaxRecordResponse = {
statusText: response.statusText,
statusText: (response||{}).statusText,
headerMap: null,
correlationContext: _getFetchCorrelationContext(response)
};
if (_enableResponseHeaderTracking) {
if (_enableResponseHeaderTracking && response) {
const responseHeaderMap = {};
response.headers.forEach((value: string, name: string) => { // @skip-minify
if (_canIncludeHeaders(name)) {
@ -607,7 +608,7 @@ export class AjaxMonitor extends BaseTelemetryPlugin implements IDependenciesPlu
return response;
})
.catch((reason: any) => {
_reportFetchMetrics(callDetails, 0, input, null, fetchData, null, { error: reason.message });
_reportFetchMetrics(callDetails, 0, input, null, fetchData, null, { error: reason.message || dumpObj(reason) });
throw reason;
});
}
@ -626,7 +627,7 @@ export class AjaxMonitor extends BaseTelemetryPlugin implements IDependenciesPlu
// Note: Polyfill implementations that don't support the "poyyfill" tag are not supported
// the workaround is to add a polyfill property to your fetch implementation before initializing
// App Insights
_addHook(InstrumentFunc(global, strFetch, {
_addHook(InstrumentFunc(global, STR_FETCH, {
ns: _evtNamespace,
req: (callDetails: IInstrumentCallDetails, input, init) => {
// Just call so that we record any disabled URL
@ -638,7 +639,7 @@ export class AjaxMonitor extends BaseTelemetryPlugin implements IDependenciesPlu
if (isPolyfill) {
// retag the instrumented fetch with the same polyfill settings this is mostly for testing
// But also supports multiple App Insights usages
(global[strFetch] as any).polyfill = isPolyfill;
(global[STR_FETCH] as any).polyfill = isPolyfill;
}
}
@ -1097,7 +1098,7 @@ export class AjaxMonitor extends BaseTelemetryPlugin implements IDependenciesPlu
ajaxData.requestHeaders = requestHeaders;
_createMarkId("fetch", ajaxData);
_createMarkId(STR_FETCH, ajaxData);
return ajaxData;
}
@ -1143,7 +1144,7 @@ export class AjaxMonitor extends BaseTelemetryPlugin implements IDependenciesPlu
ajaxData.responseFinishedTime = dateTimeUtilsNow();
ajaxData.status = status;
_findPerfResourceEntry("fetch", ajaxData, () => {
_findPerfResourceEntry(STR_FETCH, ajaxData, () => {
const dependency = ajaxData.CreateTrackItem("Fetch", _enableRequestHeaderTracking, getResponse);
let properties;
@ -1209,7 +1210,8 @@ export class AjaxMonitor extends BaseTelemetryPlugin implements IDependenciesPlu
item: dependency,
properties: properties,
sysProperties: systemProperties,
context: ajaxData ? ajaxData.context : null
context: ajaxData ? ajaxData.context : null,
aborted: ajaxData ? !!ajaxData.aborted : false
};
result = _processDependencyContainer(core, initializers, details, "initializer");

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

@ -240,7 +240,7 @@ export class ajaxRecord {
public xhrMonitoringState: XHRMonitoringState;
// <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 occurred in xhr.onreadystatechange code. 1 if occurred, otherwise 0.</summary>
public clientFailure: number;
/**
@ -339,6 +339,11 @@ export class ajaxRecord {
[STR_PROPERTIES]: { HttpMethod: self.method }
} as IDependencyTelemetry;
let props = dependency[STR_PROPERTIES];
if (self.aborted) {
props.aborted = true;
}
if (self.requestSentTime) {
// Set the correct dependency start time
dependency.startTime = new Date();
@ -350,7 +355,6 @@ export class ajaxRecord {
if (enableRequestHeaderTracking) {
if (objKeys(self.requestHeaders).length > 0) {
let props = dependency.properties = dependency.properties || {};
props.requestHeaders = self.requestHeaders;
}
}
@ -367,19 +371,21 @@ export class ajaxRecord {
if (response.headerMap) {
if (objKeys(response.headerMap).length > 0) {
let props = dependency.properties = dependency.properties || {};
props.responseHeaders = response.headerMap;
}
}
if (self.errorStatusText && self.status >= 400) {
const responseType = response.type;
let props = dependency.properties = dependency.properties || {};
if (responseType === "" || responseType === "text") {
props.responseText = response.responseText ? response.statusText + " - " + response[strResponseText] : response.statusText;
}
if (responseType === "json") {
props.responseText = response.response ? response.statusText + " - " + JSON.stringify(response.response) : response.statusText;
if (self.errorStatusText) {
if (self.status >= 400) {
const responseType = response.type;
if (responseType === "" || responseType === "text") {
props.responseText = response.responseText ? response.statusText + " - " + response[strResponseText] : response.statusText;
}
if (responseType === "json") {
props.responseText = response.response ? response.statusText + " - " + JSON.stringify(response.response) : response.statusText;
}
} else if (self.status === 0) {
props.responseText = response.statusText || "";
}
}
}

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

@ -9,7 +9,6 @@
<script src="../../../common/Tests/External/sinon-2.3.8.js"></script>
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({

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

@ -9,7 +9,6 @@
<script src="../../../common/Tests/External/sinon-2.3.8.js"></script>
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({

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

@ -9,7 +9,6 @@
<script src="../../../common/Tests/External/sinon-2.3.8.js"></script>
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({

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

@ -9,7 +9,6 @@
<script src="../../../common/Tests/External/sinon-2.3.8.js"></script>
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({

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

@ -10,7 +10,6 @@
<!-- <script src="../../../common/Tests/External/require-2.3.6.js"></script> -->
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({

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

@ -9,7 +9,6 @@
<script src="../../../common/Tests/External/sinon-2.3.8.js"></script>
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({

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

@ -9,7 +9,6 @@
<script src="../../../common/Tests/External/sinon-2.3.8.js"></script>
<script src="../../../common/Tests/External/require-2.3.6.js"></script>
<script src="../../../common/Tests/Selenium/ModuleLoader.js"></script>
<script src="../../../common/Tests/Selenium/SimpleSyncPromise.js"></script>
<script>
var modules = new ModuleLoader({