@microsoft/applicationinsights-dependencies-js 2.7.2-nightly.2111-05 → 2.7.2-nightly.2111-09
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Tests/Unit/src/ajax.tests.ts +2708 -0
- package/Tests/Unit/src/dependencies.tests.ts +7 -0
- package/Tests/UnitTests.html +52 -0
- package/Tests/tsconfig.json +13 -0
- package/api-extractor.json +361 -0
- package/applicationinsights-dependencies-js.build.error.log +20 -0
- package/applicationinsights-dependencies-js.build.log +249 -0
- package/browser/applicationinsights-dependencies-js.integrity.json +9 -9
- package/browser/applicationinsights-dependencies-js.js +1 -1
- package/browser/applicationinsights-dependencies-js.js.map +1 -1
- package/browser/applicationinsights-dependencies-js.min.js +1 -1
- package/browser/applicationinsights-dependencies-js.min.js.map +1 -1
- package/dist/applicationinsights-dependencies-js.api.json +1 -1
- package/dist/applicationinsights-dependencies-js.d.ts +1 -1
- package/dist/applicationinsights-dependencies-js.js +1 -1
- package/dist/applicationinsights-dependencies-js.js.map +1 -1
- package/dist/applicationinsights-dependencies-js.min.js +1 -1
- package/dist/applicationinsights-dependencies-js.min.js.map +1 -1
- package/dist/applicationinsights-dependencies-js.rollup.d.ts +1 -1
- package/dist-esm/TraceParent.js +1 -1
- package/dist-esm/ajax.js +1 -1
- package/dist-esm/ajaxRecord.js +1 -1
- package/dist-esm/ajaxUtils.js +1 -1
- package/dist-esm/applicationinsights-dependencies-js.js +1 -1
- package/microsoft-applicationinsights-dependencies-js-2.7.2-nightly.2111-09.tgz +0 -0
- package/package.json +3 -3
- package/rollup.config.js +139 -0
- package/temp/applicationinsights-dependencies-js.api.md +136 -0
- package/tslint.json +8 -0
- package/types/tsdoc-metadata.json +1 -1
|
@@ -0,0 +1,2708 @@
|
|
|
1
|
+
import { SinonStub } from "sinon";
|
|
2
|
+
import { Assert, AITestClass, PollingAssert } from "@microsoft/ai-test-framework";
|
|
3
|
+
import { AjaxMonitor } from "../../../src/ajax";
|
|
4
|
+
import { DisabledPropertyName, IConfig, DistributedTracingModes, RequestHeaders, IDependencyTelemetry, IRequestContext } from "@microsoft/applicationinsights-common";
|
|
5
|
+
import {
|
|
6
|
+
AppInsightsCore, IConfiguration, ITelemetryItem, ITelemetryPlugin, IChannelControls, _InternalMessageId,
|
|
7
|
+
getPerformance, getGlobalInst, getGlobal
|
|
8
|
+
} from "@microsoft/applicationinsights-core-js";
|
|
9
|
+
|
|
10
|
+
interface IFetchArgs {
|
|
11
|
+
input: RequestInfo,
|
|
12
|
+
init: RequestInit
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function hookFetch<T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): IFetchArgs[] {
|
|
16
|
+
let calls:IFetchArgs[] = [];
|
|
17
|
+
let global = getGlobal() as any;
|
|
18
|
+
global.fetch = function(input: RequestInfo, init?: RequestInit) {
|
|
19
|
+
calls.push({
|
|
20
|
+
input,
|
|
21
|
+
init
|
|
22
|
+
});
|
|
23
|
+
return new window["SimpleSyncPromise"](executor);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return calls;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function hookTrackDependencyInternal(ajaxMonitor: AjaxMonitor) {
|
|
30
|
+
let orgInternalDependency: (dependency: IDependencyTelemetry, properties?: { [key: string]: any }) => void = ajaxMonitor["trackDependencyDataInternal"];
|
|
31
|
+
let dependencyArgs: IDependencyTelemetry[] = [];
|
|
32
|
+
|
|
33
|
+
ajaxMonitor["trackDependencyDataInternal"] = function (dependency: IDependencyTelemetry, properties?: { [key: string]: any }) {
|
|
34
|
+
let orgArguments = arguments;
|
|
35
|
+
dependencyArgs.push({ ...dependency});
|
|
36
|
+
orgInternalDependency.apply(ajaxMonitor, orgArguments);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return dependencyArgs;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class AjaxTests extends AITestClass {
|
|
43
|
+
private _fetch;
|
|
44
|
+
private _ajax:AjaxMonitor = null;
|
|
45
|
+
private _context:any = {};
|
|
46
|
+
|
|
47
|
+
public testInitialize() {
|
|
48
|
+
this._context = {};
|
|
49
|
+
this.useFakeServer = true;
|
|
50
|
+
this._fetch = getGlobalInst("fetch");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public testCleanup() {
|
|
54
|
+
this._context = {};
|
|
55
|
+
if (this._ajax !== null) {
|
|
56
|
+
this._ajax.teardown();
|
|
57
|
+
this._ajax = null;
|
|
58
|
+
}
|
|
59
|
+
getGlobal().fetch = this._fetch;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public registerTests() {
|
|
63
|
+
this.testCase({
|
|
64
|
+
name: "Dependencies Configuration: Config can be set from root config",
|
|
65
|
+
test: () => {
|
|
66
|
+
this._ajax = new AjaxMonitor();
|
|
67
|
+
let dependencyFields = hookTrackDependencyInternal(this._ajax);
|
|
68
|
+
let appInsightsCore = new AppInsightsCore();
|
|
69
|
+
let coreConfig = {
|
|
70
|
+
instrumentationKey: "instrumentation_key",
|
|
71
|
+
maxAjaxCallsPerView: 5,
|
|
72
|
+
};
|
|
73
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
74
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
75
|
+
let throwSpy = this.sandbox.spy(appInsightsCore.logger, "throwInternal");
|
|
76
|
+
|
|
77
|
+
for (let lp = 0; lp < 5; lp++) {
|
|
78
|
+
var xhr = new XMLHttpRequest();
|
|
79
|
+
xhr.open("GET", "http://microsoft.com");
|
|
80
|
+
xhr.setRequestHeader("header name", "header value");
|
|
81
|
+
xhr.send();
|
|
82
|
+
// Emulate response
|
|
83
|
+
(<any>xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*"}, "");
|
|
84
|
+
}
|
|
85
|
+
Assert.equal(5, trackSpy.callCount, "Track has been called 5 times");
|
|
86
|
+
Assert.equal(false, throwSpy.called, "We should not have thrown an internal error -- yet");
|
|
87
|
+
|
|
88
|
+
var xhr = new XMLHttpRequest();
|
|
89
|
+
xhr.open("GET", "http://microsoft.com");
|
|
90
|
+
xhr.setRequestHeader("header name", "header value");
|
|
91
|
+
xhr.send();
|
|
92
|
+
// Emulate response
|
|
93
|
+
(<any>xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*"}, "");
|
|
94
|
+
|
|
95
|
+
Assert.equal(5, trackSpy.callCount, "Track has still only been called 5 times");
|
|
96
|
+
Assert.equal(true, throwSpy.called, "We should have thrown an internal error");
|
|
97
|
+
Assert.equal(_InternalMessageId.MaxAjaxPerPVExceeded, throwSpy.args[0][1], "Reported error should be max exceeded");
|
|
98
|
+
Assert.equal(true, throwSpy.args[0][2].indexOf("ajax per page view limit") !== -1, "Reported error should be contain text describing the issue");
|
|
99
|
+
|
|
100
|
+
Assert.equal(6, dependencyFields.length, "trackDependencyDataInternal should have been called");
|
|
101
|
+
for (let lp = 0; lp < 6; lp++) {
|
|
102
|
+
Assert.ok(dependencyFields[lp].startTime, `startTime ${lp} was specified before trackDependencyDataInternal was called`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
this.testCase({
|
|
108
|
+
name: "Dependencies Configuration: Make sure we don't fail for invalid arguments",
|
|
109
|
+
test: () => {
|
|
110
|
+
this._ajax = new AjaxMonitor();
|
|
111
|
+
let dependencyFields = hookTrackDependencyInternal(this._ajax);
|
|
112
|
+
let appInsightsCore = new AppInsightsCore();
|
|
113
|
+
let coreConfig = {
|
|
114
|
+
instrumentationKey: "instrumentation_key",
|
|
115
|
+
maxAjaxCallsPerView: 5,
|
|
116
|
+
};
|
|
117
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
118
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
119
|
+
let throwSpy = this.sandbox.spy(appInsightsCore.logger, "throwInternal");
|
|
120
|
+
|
|
121
|
+
var xhr = new XMLHttpRequest();
|
|
122
|
+
xhr.open("GET", null);
|
|
123
|
+
xhr.setRequestHeader("header name", "header value");
|
|
124
|
+
xhr.send();
|
|
125
|
+
// Emulate response
|
|
126
|
+
(<any>xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*"}, "");
|
|
127
|
+
|
|
128
|
+
var xhr = new XMLHttpRequest();
|
|
129
|
+
xhr.open("GET", undefined);
|
|
130
|
+
xhr.setRequestHeader("header name", "header value");
|
|
131
|
+
xhr.send();
|
|
132
|
+
// Emulate response
|
|
133
|
+
(<any>xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*"}, "");
|
|
134
|
+
|
|
135
|
+
Assert.equal(2, trackSpy.callCount, "Track has been called 2 times");
|
|
136
|
+
Assert.equal(2, dependencyFields.length, "trackDependencyDataInternal should have been called");
|
|
137
|
+
Assert.ok(dependencyFields[0].startTime, "startTime 0 was specified before trackDependencyDataInternal was called")
|
|
138
|
+
Assert.ok(dependencyFields[1].startTime, "startTime 1 was specified before trackDependencyDataInternal was called")
|
|
139
|
+
|
|
140
|
+
Assert.equal(false, throwSpy.called, "We should not have thrown an internal error -- yet");
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
this.testCase({
|
|
145
|
+
name: "Ajax: xhr.open gets instrumented",
|
|
146
|
+
test: () => {
|
|
147
|
+
this._ajax = new AjaxMonitor();
|
|
148
|
+
let appInsightsCore = new AppInsightsCore();
|
|
149
|
+
let coreConfig = { instrumentationKey: "instrumentationKey", extensionConfig: {"AjaxPlugin": {}}};
|
|
150
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
151
|
+
|
|
152
|
+
// act
|
|
153
|
+
var xhr = new XMLHttpRequest();
|
|
154
|
+
xhr.open("GET", "http://microsoft.com");
|
|
155
|
+
|
|
156
|
+
// assert
|
|
157
|
+
Assert.ok((<any>xhr).ajaxData)
|
|
158
|
+
var ajaxData = (<any>xhr).ajaxData;
|
|
159
|
+
Assert.equal("http://microsoft.com", ajaxData.requestUrl, "RequestUrl is collected correctly");
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
this.testCase({
|
|
164
|
+
name: "Ajax: xhr with disabled flag isn't tracked",
|
|
165
|
+
test: () => {
|
|
166
|
+
this._ajax = new AjaxMonitor();
|
|
167
|
+
let appInsightsCore = new AppInsightsCore();
|
|
168
|
+
let coreConfig: IConfiguration & IConfig = { instrumentationKey: "", disableAjaxTracking: false };
|
|
169
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
170
|
+
|
|
171
|
+
// act
|
|
172
|
+
var xhr = new XMLHttpRequest();
|
|
173
|
+
xhr[DisabledPropertyName] = true;
|
|
174
|
+
xhr.open("GET", "http://microsoft.com");
|
|
175
|
+
|
|
176
|
+
// assert
|
|
177
|
+
Assert.equal(undefined, (<any>xhr).ajaxData, "RequestUrl is collected correctly");
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
this.testCase({
|
|
182
|
+
name: "Ajax: xhr with disabled flag isn't tracked and any followup request to the same URL event without the disabled flag are also not tracked",
|
|
183
|
+
test: () => {
|
|
184
|
+
this._ajax = new AjaxMonitor();
|
|
185
|
+
let appInsightsCore = new AppInsightsCore();
|
|
186
|
+
let coreConfig: IConfiguration & IConfig = { instrumentationKey: "", disableAjaxTracking: false };
|
|
187
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
188
|
+
|
|
189
|
+
// act
|
|
190
|
+
var xhr = new XMLHttpRequest();
|
|
191
|
+
xhr[DisabledPropertyName] = true;
|
|
192
|
+
xhr.open("GET", "http://microsoft.com");
|
|
193
|
+
|
|
194
|
+
// assert
|
|
195
|
+
Assert.equal(undefined, (<any>xhr).ajaxData, "RequestUrl is collected correctly");
|
|
196
|
+
|
|
197
|
+
xhr = new XMLHttpRequest();
|
|
198
|
+
xhr.open("GET", "http://microsoft.com");
|
|
199
|
+
|
|
200
|
+
// assert
|
|
201
|
+
Assert.equal(undefined, (<any>xhr).ajaxData, "Follow up GET Request was not instrumented");
|
|
202
|
+
|
|
203
|
+
xhr = new XMLHttpRequest();
|
|
204
|
+
xhr.open("POST", "http://microsoft.com");
|
|
205
|
+
|
|
206
|
+
// assert
|
|
207
|
+
Assert.equal(undefined, (<any>xhr).ajaxData, "Follow up POST Request was not instrumented");
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
this.testCase({
|
|
212
|
+
name: "Ajax: xhr without disabled flag but with exclude string configured are not tracked",
|
|
213
|
+
test: () => {
|
|
214
|
+
this._ajax = new AjaxMonitor();
|
|
215
|
+
let appInsightsCore = new AppInsightsCore();
|
|
216
|
+
const ExcludeRequestRegex = ["microsoft"];
|
|
217
|
+
let coreConfig: IConfiguration & IConfig = { instrumentationKey: "", disableAjaxTracking: true, excludeRequestFromAutoTrackingPatterns: ExcludeRequestRegex };
|
|
218
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
219
|
+
|
|
220
|
+
// act
|
|
221
|
+
var xhr = new XMLHttpRequest();
|
|
222
|
+
xhr.open("GET", "http://microsoft.com");
|
|
223
|
+
|
|
224
|
+
// assert
|
|
225
|
+
Assert.equal(undefined, (<any>xhr).ajaxData, "RequestUrl is collected correctly");
|
|
226
|
+
|
|
227
|
+
xhr = new XMLHttpRequest();
|
|
228
|
+
xhr.open("GET", "http://microsoft.com");
|
|
229
|
+
|
|
230
|
+
// assert
|
|
231
|
+
Assert.equal(undefined, (<any>xhr).ajaxData, "Follow up GET Request was not instrumented");
|
|
232
|
+
|
|
233
|
+
xhr = new XMLHttpRequest();
|
|
234
|
+
xhr.open("POST", "http://microsoft.com");
|
|
235
|
+
|
|
236
|
+
// assert
|
|
237
|
+
Assert.equal(undefined, (<any>xhr).ajaxData, "Follow up POST Request was not instrumented");
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
this.testCase({
|
|
242
|
+
name: "Ajax: add context into custom dimension with call back configuration on AI initialization.",
|
|
243
|
+
test: () => {
|
|
244
|
+
this._ajax = new AjaxMonitor();
|
|
245
|
+
let dependencyFields = hookTrackDependencyInternal(this._ajax);
|
|
246
|
+
let appInsightsCore = new AppInsightsCore();
|
|
247
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
248
|
+
|
|
249
|
+
let coreConfig: IConfiguration & IConfig = {
|
|
250
|
+
instrumentationKey: "",
|
|
251
|
+
disableAjaxTracking: false,
|
|
252
|
+
addRequestContext: (requestContext: IRequestContext) => {
|
|
253
|
+
return {
|
|
254
|
+
test: "ajax context",
|
|
255
|
+
xhrStatus: requestContext.status
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
260
|
+
|
|
261
|
+
// act
|
|
262
|
+
var xhr = new XMLHttpRequest();
|
|
263
|
+
xhr.open("GET", "http://microsoft.com");
|
|
264
|
+
xhr.send();
|
|
265
|
+
|
|
266
|
+
// Emulate response
|
|
267
|
+
(<any>xhr).respond(200, {}, "");
|
|
268
|
+
|
|
269
|
+
Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called");
|
|
270
|
+
|
|
271
|
+
// assert
|
|
272
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
273
|
+
let data = trackStub.args[0][0].baseData;
|
|
274
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
275
|
+
Assert.equal("ajax context", data.properties.test, "xhr request's request context is added when customer configures addRequestContext.");
|
|
276
|
+
Assert.equal(200, data.properties.xhrStatus, "xhr object properties are captured");
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
this.testCase({
|
|
281
|
+
name: "Ajax: xhr request header is tracked as part C data when enableRequestHeaderTracking flag is true",
|
|
282
|
+
test: () => {
|
|
283
|
+
this._ajax = new AjaxMonitor();
|
|
284
|
+
let appInsightsCore = new AppInsightsCore();
|
|
285
|
+
let coreConfig: IConfiguration & IConfig = { instrumentationKey: "abc", disableAjaxTracking: false, enableRequestHeaderTracking: true };
|
|
286
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
287
|
+
|
|
288
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
289
|
+
|
|
290
|
+
// act
|
|
291
|
+
var xhr = new XMLHttpRequest();
|
|
292
|
+
xhr.open("GET", "http://microsoft.com");
|
|
293
|
+
xhr.setRequestHeader("header name", "header value");
|
|
294
|
+
xhr.setRequestHeader("Authorization", "Authorization");
|
|
295
|
+
xhr.send();
|
|
296
|
+
|
|
297
|
+
// Emulate response
|
|
298
|
+
(<any>xhr).respond(200, {}, "");
|
|
299
|
+
|
|
300
|
+
// assert
|
|
301
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
302
|
+
let data = trackStub.args[0][0].baseData;
|
|
303
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
304
|
+
Assert.notEqual(undefined, data.properties.requestHeaders, "xhr request's request header is stored");
|
|
305
|
+
Assert.equal(undefined, data.properties.requestHeaders["Authorization"], "xhr request's request header is not ignored when the header is in ignoreHeaders");
|
|
306
|
+
Assert.equal(undefined, data.properties.responseHeaders, "xhr request's reponse header is not stored when enableResponseHeaderTracking flag is not set, default is false");
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
this.testCase({
|
|
311
|
+
name: "Ajax: xhr request header is tracked as part C data when enableResponseHeaderTracking flag is true",
|
|
312
|
+
test: () => {
|
|
313
|
+
this._ajax = new AjaxMonitor();
|
|
314
|
+
let appInsightsCore = new AppInsightsCore();
|
|
315
|
+
let coreConfig: IConfiguration & IConfig = { instrumentationKey: "abc", disableAjaxTracking: false, enableResponseHeaderTracking: true };
|
|
316
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
317
|
+
|
|
318
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
319
|
+
|
|
320
|
+
// act
|
|
321
|
+
var xhr = new XMLHttpRequest();
|
|
322
|
+
xhr.open("GET", "http://microsoft.com");
|
|
323
|
+
xhr.send();
|
|
324
|
+
|
|
325
|
+
// Emulate response
|
|
326
|
+
(<any>xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*","Authorization":"Authorization"}, "");
|
|
327
|
+
|
|
328
|
+
// assert
|
|
329
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
330
|
+
let data = trackStub.args[0][0].baseData;
|
|
331
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
332
|
+
Assert.equal(undefined, data.properties.requestHeaders, "xhr request's request header is not stored when enableRequestHeaderTracking flag is not set, default is false");
|
|
333
|
+
Assert.notEqual(undefined, data.properties.responseHeaders, "xhr request's reponse header is stored");
|
|
334
|
+
Assert.equal(undefined, data.properties.responseHeaders["Authorization"], "xhr request's reponse header is not ignored when the header is in ignoreHeader");
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
this.testCase({
|
|
339
|
+
name: "Ajax: xhr respond error data is tracked as part C data when enableAjaxErrorStatusText flag is true",
|
|
340
|
+
test: () => {
|
|
341
|
+
this._ajax = new AjaxMonitor();
|
|
342
|
+
let appInsightsCore = new AppInsightsCore();
|
|
343
|
+
let coreConfig: IConfiguration & IConfig = { instrumentationKey: "abc", disableAjaxTracking: false, enableAjaxErrorStatusText: true };
|
|
344
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
345
|
+
|
|
346
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
347
|
+
|
|
348
|
+
// act
|
|
349
|
+
var xhr = new XMLHttpRequest();
|
|
350
|
+
xhr.open("GET", "http://microsoft.com");
|
|
351
|
+
xhr.send();
|
|
352
|
+
|
|
353
|
+
// Emulate response
|
|
354
|
+
(<any>xhr).respond(403, {}, "error data with status code 403");
|
|
355
|
+
|
|
356
|
+
// assert
|
|
357
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
358
|
+
let data = trackStub.args[0][0].baseData;
|
|
359
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
360
|
+
Assert.notEqual(undefined, data.properties.responseText, "xhr request's reponse error is stored in part C");
|
|
361
|
+
Assert.strictEqual("Forbidden - error data with status code 403", data.properties.responseText, "xhr responseType is \"\"");
|
|
362
|
+
|
|
363
|
+
// act
|
|
364
|
+
var xhr2 = new XMLHttpRequest();
|
|
365
|
+
xhr2.open("GET", "http://microsoft.com");
|
|
366
|
+
xhr2.responseType = "json";
|
|
367
|
+
xhr2.send();
|
|
368
|
+
|
|
369
|
+
// Emulate response
|
|
370
|
+
let responseJSON = '{ "error":"error data with status code 403" }';
|
|
371
|
+
(<any>xhr2).respond(403, {}, responseJSON);
|
|
372
|
+
|
|
373
|
+
// assert
|
|
374
|
+
Assert.ok(trackStub.calledTwice, "track is called");
|
|
375
|
+
data = trackStub.args[1][0].baseData;
|
|
376
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
377
|
+
Assert.notEqual(undefined, data.properties.responseText, "xhr request's reponse error is stored in part C");
|
|
378
|
+
Assert.strictEqual("Forbidden - {\"error\":\"error data with status code 403\"}", data.properties.responseText, "xhr responseType is JSON, response got parsed");
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
this.testCase({
|
|
383
|
+
name: "Ajax: xhr respond error data is not tracked as part C data when enableAjaxErrorStatusText flag is false",
|
|
384
|
+
test: () => {
|
|
385
|
+
this._ajax = new AjaxMonitor();
|
|
386
|
+
let appInsightsCore = new AppInsightsCore();
|
|
387
|
+
let coreConfig: IConfiguration & IConfig = { instrumentationKey: "abc", disableAjaxTracking: false, enableAjaxErrorStatusText: false };
|
|
388
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
389
|
+
|
|
390
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
391
|
+
|
|
392
|
+
// act
|
|
393
|
+
var xhr = new XMLHttpRequest();
|
|
394
|
+
xhr.open("GET", "http://microsoft.com");
|
|
395
|
+
xhr.send();
|
|
396
|
+
|
|
397
|
+
// Emulate response
|
|
398
|
+
(<any>xhr).respond(403, {}, "error data with status code 403");
|
|
399
|
+
|
|
400
|
+
// assert
|
|
401
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
402
|
+
let data = trackStub.args[0][0].baseData;
|
|
403
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
404
|
+
Assert.equal(undefined, data.properties.responseText, "xhr request's reponse error is not stored in part C");
|
|
405
|
+
|
|
406
|
+
// act
|
|
407
|
+
var xhr2 = new XMLHttpRequest();
|
|
408
|
+
xhr2.open("GET", "http://microsoft.com");
|
|
409
|
+
xhr2.responseType = "json";
|
|
410
|
+
xhr2.send();
|
|
411
|
+
|
|
412
|
+
// Emulate response
|
|
413
|
+
let responseJSON = '{ "error":"error data with status code 403" }';
|
|
414
|
+
(<any>xhr2).respond(403, {}, responseJSON);
|
|
415
|
+
|
|
416
|
+
// assert
|
|
417
|
+
Assert.ok(trackStub.calledTwice, "track is called");
|
|
418
|
+
data = trackStub.args[1][0].baseData;
|
|
419
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
420
|
+
Assert.equal(undefined, data.properties.responseText, "xhr request's reponse error is not stored in part C");
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
this.testCase({
|
|
425
|
+
name: "Ajax: xhr respond error data is not tracked as part C data when enableAjaxErrorStatusText flag is default",
|
|
426
|
+
test: () => {
|
|
427
|
+
this._ajax = new AjaxMonitor();
|
|
428
|
+
let appInsightsCore = new AppInsightsCore();
|
|
429
|
+
let coreConfig: IConfiguration & IConfig = { instrumentationKey: "abc", disableAjaxTracking: false };
|
|
430
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
431
|
+
|
|
432
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
433
|
+
|
|
434
|
+
// act
|
|
435
|
+
var xhr = new XMLHttpRequest();
|
|
436
|
+
xhr.open("GET", "http://microsoft.com");
|
|
437
|
+
xhr.send();
|
|
438
|
+
|
|
439
|
+
// Emulate response
|
|
440
|
+
(<any>xhr).respond(403, {}, "error data with status code 403");
|
|
441
|
+
|
|
442
|
+
// assert
|
|
443
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
444
|
+
let data = trackStub.args[0][0].baseData;
|
|
445
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
446
|
+
Assert.equal(undefined, data.properties.responseText, "xhr request's reponse error is not stored in part C");
|
|
447
|
+
|
|
448
|
+
// act
|
|
449
|
+
var xhr2 = new XMLHttpRequest();
|
|
450
|
+
xhr2.open("GET", "http://microsoft.com");
|
|
451
|
+
xhr2.responseType = "json";
|
|
452
|
+
xhr2.send();
|
|
453
|
+
|
|
454
|
+
// Emulate response
|
|
455
|
+
let responseJSON = '{ "error":"error data with status code 403" }';
|
|
456
|
+
(<any>xhr2).respond(403, {}, responseJSON);
|
|
457
|
+
|
|
458
|
+
// assert
|
|
459
|
+
Assert.ok(trackStub.calledTwice, "track is called");
|
|
460
|
+
data = trackStub.args[1][0].baseData;
|
|
461
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
462
|
+
Assert.equal(undefined, data.properties.responseText, "xhr request's reponse error is not stored in part C");
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
this.testCaseAsync({
|
|
467
|
+
name: "Fetch: fetch with disabled flag isn't tracked",
|
|
468
|
+
stepDelay: 10,
|
|
469
|
+
autoComplete: false,
|
|
470
|
+
timeOut: 10000,
|
|
471
|
+
steps: [ (testContext) => {
|
|
472
|
+
hookFetch((resolve) => {
|
|
473
|
+
AITestClass.orgSetTimeout(function() {
|
|
474
|
+
resolve({
|
|
475
|
+
headers: new Headers(),
|
|
476
|
+
ok: true,
|
|
477
|
+
body: null,
|
|
478
|
+
bodyUsed: false,
|
|
479
|
+
redirected: false,
|
|
480
|
+
status: 200,
|
|
481
|
+
statusText: "Hello",
|
|
482
|
+
trailer: null,
|
|
483
|
+
type: "basic",
|
|
484
|
+
url: "https://httpbin.org/status/200"
|
|
485
|
+
});
|
|
486
|
+
}, 0);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
this._ajax = new AjaxMonitor();
|
|
490
|
+
let appInsightsCore = new AppInsightsCore();
|
|
491
|
+
let coreConfig = { instrumentationKey: "", disableFetchTracking: false };
|
|
492
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
493
|
+
let fetchSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
494
|
+
|
|
495
|
+
// Act
|
|
496
|
+
Assert.ok(fetchSpy.notCalled, "No fetch called yet");
|
|
497
|
+
fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: true}).then(() => {
|
|
498
|
+
// Assert
|
|
499
|
+
Assert.ok(fetchSpy.notCalled, "The request was not tracked");
|
|
500
|
+
testContext.testDone();
|
|
501
|
+
}, () => {
|
|
502
|
+
Assert.ok(false, "fetch failed!");
|
|
503
|
+
testContext.testDone();
|
|
504
|
+
});
|
|
505
|
+
}]
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
this.testCaseAsync({
|
|
509
|
+
name: "Fetch: fetch with disabled flag isn't tracked and any followup request to the same URL event without the disabled flag are also not tracked",
|
|
510
|
+
stepDelay: 10,
|
|
511
|
+
autoComplete: false,
|
|
512
|
+
timeOut: 10000,
|
|
513
|
+
steps: [ (testContext) => {
|
|
514
|
+
hookFetch((resolve) => {
|
|
515
|
+
AITestClass.orgSetTimeout(function() {
|
|
516
|
+
resolve({
|
|
517
|
+
headers: new Headers(),
|
|
518
|
+
ok: true,
|
|
519
|
+
body: null,
|
|
520
|
+
bodyUsed: false,
|
|
521
|
+
redirected: false,
|
|
522
|
+
status: 200,
|
|
523
|
+
statusText: "Hello",
|
|
524
|
+
trailer: null,
|
|
525
|
+
type: "basic",
|
|
526
|
+
url: "https://httpbin.org/status/200"
|
|
527
|
+
});
|
|
528
|
+
}, 0);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
this._ajax = new AjaxMonitor();
|
|
532
|
+
let appInsightsCore = new AppInsightsCore();
|
|
533
|
+
let coreConfig = { instrumentationKey: "", disableFetchTracking: false };
|
|
534
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
535
|
+
let fetchSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
536
|
+
|
|
537
|
+
// Act
|
|
538
|
+
Assert.ok(fetchSpy.notCalled, "No fetch called yet");
|
|
539
|
+
fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: true}).then(() => {
|
|
540
|
+
// Assert
|
|
541
|
+
Assert.ok(fetchSpy.notCalled, "The initial request was not tracked");
|
|
542
|
+
|
|
543
|
+
fetch("https://httpbin.org/status/200", {method: "post" }).then(() => {
|
|
544
|
+
// Assert
|
|
545
|
+
Assert.ok(fetchSpy.notCalled, "The follow up request should also not have been tracked");
|
|
546
|
+
testContext.testDone();
|
|
547
|
+
}, () => {
|
|
548
|
+
Assert.ok(false, "fetch failed!");
|
|
549
|
+
testContext.testDone();
|
|
550
|
+
});
|
|
551
|
+
}, () => {
|
|
552
|
+
Assert.ok(false, "fetch failed!");
|
|
553
|
+
testContext.testDone();
|
|
554
|
+
});
|
|
555
|
+
}]
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
this.testCaseAsync({
|
|
559
|
+
name: "Fetch: fetch with disabled flag false and with exclude request regex pattern isn't tracked and any followup request to the same URL event without the disabled flag are also not tracked",
|
|
560
|
+
stepDelay: 10,
|
|
561
|
+
autoComplete: false,
|
|
562
|
+
timeOut: 10000,
|
|
563
|
+
steps: [ (testContext) => {
|
|
564
|
+
hookFetch((resolve) => {
|
|
565
|
+
AITestClass.orgSetTimeout(function() {
|
|
566
|
+
resolve({
|
|
567
|
+
headers: new Headers(),
|
|
568
|
+
ok: true,
|
|
569
|
+
body: null,
|
|
570
|
+
bodyUsed: false,
|
|
571
|
+
redirected: false,
|
|
572
|
+
status: 200,
|
|
573
|
+
statusText: "Hello",
|
|
574
|
+
trailer: null,
|
|
575
|
+
type: "basic",
|
|
576
|
+
url: "https://httpbin.org/status/200"
|
|
577
|
+
});
|
|
578
|
+
}, 0);
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
this._ajax = new AjaxMonitor();
|
|
582
|
+
let appInsightsCore = new AppInsightsCore();
|
|
583
|
+
const ExcludeRequestRegex = ["bin"];
|
|
584
|
+
let coreConfig = { instrumentationKey: "", disableFetchTracking: false, excludeRequestFromAutoTrackingPatterns: ExcludeRequestRegex };
|
|
585
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
586
|
+
let fetchSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
587
|
+
|
|
588
|
+
// Act
|
|
589
|
+
Assert.ok(fetchSpy.notCalled, "No fetch called yet");
|
|
590
|
+
fetch("https://httpbin.org/status/200", {method: "post"}).then(() => {
|
|
591
|
+
// Assert
|
|
592
|
+
Assert.ok(fetchSpy.notCalled, "The initial request was not tracked");
|
|
593
|
+
|
|
594
|
+
fetch("https://httpbin.org/status/200", {method: "post" }).then(() => {
|
|
595
|
+
// Assert
|
|
596
|
+
Assert.ok(fetchSpy.notCalled, "The follow up request should also not have been tracked");
|
|
597
|
+
testContext.testDone();
|
|
598
|
+
}, () => {
|
|
599
|
+
Assert.ok(false, "fetch failed!");
|
|
600
|
+
testContext.testDone();
|
|
601
|
+
});
|
|
602
|
+
}, () => {
|
|
603
|
+
Assert.ok(false, "fetch failed!");
|
|
604
|
+
testContext.testDone();
|
|
605
|
+
});
|
|
606
|
+
}]
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
this.testCaseAsync({
|
|
610
|
+
name: "Fetch: add context into custom dimension with call back configuration on AI initialization.",
|
|
611
|
+
stepDelay: 10,
|
|
612
|
+
autoComplete: false,
|
|
613
|
+
timeOut: 10000,
|
|
614
|
+
steps: [ (testContext) => {
|
|
615
|
+
hookFetch((resolve) => {
|
|
616
|
+
AITestClass.orgSetTimeout(function() {
|
|
617
|
+
resolve({
|
|
618
|
+
headers: new Headers(),
|
|
619
|
+
ok: true,
|
|
620
|
+
body: null,
|
|
621
|
+
bodyUsed: false,
|
|
622
|
+
redirected: false,
|
|
623
|
+
status: 200,
|
|
624
|
+
statusText: "Hello",
|
|
625
|
+
trailer: null,
|
|
626
|
+
type: "basic",
|
|
627
|
+
url: "https://httpbin.org/status/200"
|
|
628
|
+
});
|
|
629
|
+
}, 0);
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
this._ajax = new AjaxMonitor();
|
|
633
|
+
let dependencyFields = hookTrackDependencyInternal(this._ajax);
|
|
634
|
+
let appInsightsCore = new AppInsightsCore();
|
|
635
|
+
let coreConfig = {
|
|
636
|
+
instrumentationKey: "",
|
|
637
|
+
disableFetchTracking: false,
|
|
638
|
+
addRequestContext: (requestContext: IRequestContext) => {
|
|
639
|
+
return {
|
|
640
|
+
test: "Fetch context",
|
|
641
|
+
fetchRequestUrl: requestContext.request,
|
|
642
|
+
fetchResponseType: (requestContext.response as Response).type
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
647
|
+
let fetchSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
648
|
+
|
|
649
|
+
// Act
|
|
650
|
+
Assert.ok(fetchSpy.notCalled, "No fetch called yet");
|
|
651
|
+
fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => {
|
|
652
|
+
// assert
|
|
653
|
+
Assert.ok(fetchSpy.calledOnce, "track is called");
|
|
654
|
+
let data = fetchSpy.args[0][0].baseData;
|
|
655
|
+
Assert.equal("Fetch", data.type, "request is Fetch type");
|
|
656
|
+
Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called");
|
|
657
|
+
Assert.equal("Fetch context", data.properties.test, "Fetch request's request context is added when customer configures addRequestContext.");
|
|
658
|
+
Assert.equal("https://httpbin.org/status/200", data.properties.fetchRequestUrl, "Fetch request is captured.");
|
|
659
|
+
Assert.equal("basic", data.properties.fetchResponseType, "Fetch response is captured.");
|
|
660
|
+
|
|
661
|
+
testContext.testDone();
|
|
662
|
+
}, () => {
|
|
663
|
+
Assert.ok(false, "fetch failed!");
|
|
664
|
+
testContext.testDone();
|
|
665
|
+
});
|
|
666
|
+
}]
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
this.testCaseAsync({
|
|
670
|
+
name: "Fetch: fetch gets instrumented",
|
|
671
|
+
stepDelay: 10,
|
|
672
|
+
autoComplete: false,
|
|
673
|
+
timeOut: 10000,
|
|
674
|
+
steps: [ (testContext) => {
|
|
675
|
+
hookFetch((resolve) => {
|
|
676
|
+
AITestClass.orgSetTimeout(function() {
|
|
677
|
+
resolve({
|
|
678
|
+
headers: new Headers(),
|
|
679
|
+
ok: true,
|
|
680
|
+
body: null,
|
|
681
|
+
bodyUsed: false,
|
|
682
|
+
redirected: false,
|
|
683
|
+
status: 200,
|
|
684
|
+
statusText: "Hello",
|
|
685
|
+
trailer: null,
|
|
686
|
+
type: "basic",
|
|
687
|
+
url: "https://httpbin.org/status/200"
|
|
688
|
+
});
|
|
689
|
+
}, 0);
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
this._ajax = new AjaxMonitor();
|
|
693
|
+
let dependencyFields = hookTrackDependencyInternal(this._ajax);
|
|
694
|
+
let appInsightsCore = new AppInsightsCore();
|
|
695
|
+
let coreConfig = { instrumentationKey: "", disableFetchTracking: false };
|
|
696
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
697
|
+
let fetchSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
698
|
+
let throwSpy = this.sandbox.spy(appInsightsCore.logger, "throwInternal");
|
|
699
|
+
|
|
700
|
+
// Act
|
|
701
|
+
Assert.ok(fetchSpy.notCalled, "No fetch called yet");
|
|
702
|
+
fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => {
|
|
703
|
+
// Assert
|
|
704
|
+
Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch");
|
|
705
|
+
let data = fetchSpy.args[0][0].baseData;
|
|
706
|
+
Assert.equal("Fetch", data.type, "request is Fetch type");
|
|
707
|
+
Assert.ok(throwSpy.notCalled, "Make sure we didn't fail internally");
|
|
708
|
+
Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called");
|
|
709
|
+
Assert.ok(dependencyFields[0].startTime, "startTime was specified before trackDependencyDataInternal was called")
|
|
710
|
+
testContext.testDone();
|
|
711
|
+
}, () => {
|
|
712
|
+
Assert.ok(false, "fetch failed!");
|
|
713
|
+
testContext.testDone();
|
|
714
|
+
});
|
|
715
|
+
}]
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
this.testCaseAsync({
|
|
719
|
+
name: "Fetch: instrumentation handles invalid / missing request or url",
|
|
720
|
+
stepDelay: 10,
|
|
721
|
+
autoComplete: false,
|
|
722
|
+
timeOut: 10000,
|
|
723
|
+
steps: [ (testContext) => {
|
|
724
|
+
hookFetch((resolve) => {
|
|
725
|
+
AITestClass.orgSetTimeout(function() {
|
|
726
|
+
resolve({
|
|
727
|
+
headers: new Headers(),
|
|
728
|
+
ok: true,
|
|
729
|
+
body: null,
|
|
730
|
+
bodyUsed: false,
|
|
731
|
+
redirected: false,
|
|
732
|
+
status: 200,
|
|
733
|
+
statusText: "Hello",
|
|
734
|
+
trailer: null,
|
|
735
|
+
type: "basic",
|
|
736
|
+
url: "https://httpbin.org/status/200"
|
|
737
|
+
});
|
|
738
|
+
}, 0);
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
this._ajax = new AjaxMonitor();
|
|
742
|
+
let dependencyFields = hookTrackDependencyInternal(this._ajax);
|
|
743
|
+
let appInsightsCore = new AppInsightsCore();
|
|
744
|
+
let coreConfig = { instrumentationKey: "", disableFetchTracking: false };
|
|
745
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
746
|
+
let fetchSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
747
|
+
let throwSpy = this.sandbox.spy(appInsightsCore.logger, "throwInternal");
|
|
748
|
+
|
|
749
|
+
// Act
|
|
750
|
+
Assert.ok(fetchSpy.notCalled, "No fetch called yet");
|
|
751
|
+
fetch(null, {method: "post", [DisabledPropertyName]: false}).then(() => {
|
|
752
|
+
// Assert
|
|
753
|
+
Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch");
|
|
754
|
+
let data = fetchSpy.args[0][0].baseData;
|
|
755
|
+
Assert.equal("Fetch", data.type, "request is Fetch type");
|
|
756
|
+
Assert.equal(false, throwSpy.called, "We should not have failed internally");
|
|
757
|
+
Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called");
|
|
758
|
+
Assert.ok(dependencyFields[0].startTime, "startTime was specified before trackDependencyDataInternal was called")
|
|
759
|
+
|
|
760
|
+
fetch(undefined, null).then(() => {
|
|
761
|
+
// Assert
|
|
762
|
+
Assert.ok(fetchSpy.calledTwice, "createFetchRecord called once after using fetch");
|
|
763
|
+
Assert.equal(false, throwSpy.called, "We should still not have failed internally");
|
|
764
|
+
Assert.equal(2, dependencyFields.length, "trackDependencyDataInternal was called");
|
|
765
|
+
Assert.ok(dependencyFields[1].startTime, "startTime was specified before trackDependencyDataInternal was called");
|
|
766
|
+
testContext.testDone();
|
|
767
|
+
}, () => {
|
|
768
|
+
Assert.ok(false, "fetch failed!");
|
|
769
|
+
testContext.testDone();
|
|
770
|
+
});
|
|
771
|
+
}, () => {
|
|
772
|
+
Assert.ok(false, "fetch failed!");
|
|
773
|
+
testContext.testDone();
|
|
774
|
+
});
|
|
775
|
+
}]
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
this.testCase({
|
|
779
|
+
name: "Fetch: fetch keeps custom headers",
|
|
780
|
+
test: () => {
|
|
781
|
+
hookFetch((resolve) => {
|
|
782
|
+
AITestClass.orgSetTimeout(function() {
|
|
783
|
+
resolve();
|
|
784
|
+
}, 0);
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
try {
|
|
788
|
+
this._ajax = new AjaxMonitor();
|
|
789
|
+
let appInsightsCore = new AppInsightsCore();
|
|
790
|
+
let coreConfig = {
|
|
791
|
+
instrumentationKey: "",
|
|
792
|
+
disableFetchTracking: false,
|
|
793
|
+
disableAjaxTracking: true
|
|
794
|
+
};
|
|
795
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
796
|
+
let fetchSpy = this.sandbox.spy(window, "fetch");
|
|
797
|
+
|
|
798
|
+
// Setup
|
|
799
|
+
let headers = new Headers();
|
|
800
|
+
headers.append('My-Header', 'Header field');
|
|
801
|
+
let init = {
|
|
802
|
+
method: 'get',
|
|
803
|
+
headers: headers
|
|
804
|
+
};
|
|
805
|
+
const url = 'https://httpbin.org/status/200';
|
|
806
|
+
|
|
807
|
+
let headerSpy = this.sandbox.spy(this._ajax, "includeCorrelationHeaders");
|
|
808
|
+
|
|
809
|
+
// Act
|
|
810
|
+
Assert.ok(fetchSpy.notCalled);
|
|
811
|
+
fetch(url, init);
|
|
812
|
+
|
|
813
|
+
// Assert
|
|
814
|
+
Assert.ok(fetchSpy.calledOnce);
|
|
815
|
+
Assert.ok(headerSpy.calledOnce);
|
|
816
|
+
Assert.deepEqual(init, headerSpy.returnValue || headerSpy.returnValues[0]);
|
|
817
|
+
} catch (e) {
|
|
818
|
+
console && console.warn("Exception: " + e);
|
|
819
|
+
Assert.ok(false, e);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
this.testCaseAsync({
|
|
825
|
+
name: "Fetch: should create and pass a traceparent header if ai and w3c is enabled with custom headers",
|
|
826
|
+
stepDelay: 10,
|
|
827
|
+
timeOut: 10000,
|
|
828
|
+
steps: [ (testContext) => {
|
|
829
|
+
let fetchCalls = hookFetch((resolve) => {
|
|
830
|
+
AITestClass.orgSetTimeout(function() {
|
|
831
|
+
resolve({
|
|
832
|
+
headers: new Headers(),
|
|
833
|
+
ok: true,
|
|
834
|
+
body: null,
|
|
835
|
+
bodyUsed: false,
|
|
836
|
+
redirected: false,
|
|
837
|
+
status: 200,
|
|
838
|
+
statusText: "Hello",
|
|
839
|
+
trailer: null,
|
|
840
|
+
type: "basic",
|
|
841
|
+
url: "https://httpbin.org/status/200"
|
|
842
|
+
});
|
|
843
|
+
}, 0);
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
this._ajax = new AjaxMonitor();
|
|
847
|
+
let appInsightsCore = new AppInsightsCore();
|
|
848
|
+
let coreConfig = {
|
|
849
|
+
instrumentationKey: "instrumentationKey",
|
|
850
|
+
disableFetchTracking: false,
|
|
851
|
+
disableAjaxTracking: false,
|
|
852
|
+
extensionConfig: {
|
|
853
|
+
"AjaxDependencyPlugin": {
|
|
854
|
+
appId: "appId",
|
|
855
|
+
distributedTracingMode: DistributedTracingModes.AI_AND_W3C
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
860
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
861
|
+
this._context["trackStub"] = trackSpy;
|
|
862
|
+
|
|
863
|
+
// Use test hook to simulate the correct url location
|
|
864
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
865
|
+
|
|
866
|
+
// Setup
|
|
867
|
+
let headers = new Headers();
|
|
868
|
+
headers.append('My-Header', 'Header field');
|
|
869
|
+
let init = {
|
|
870
|
+
method: 'get',
|
|
871
|
+
headers: headers
|
|
872
|
+
};
|
|
873
|
+
const url = 'https://httpbin.org/status/200';
|
|
874
|
+
|
|
875
|
+
// Act
|
|
876
|
+
Assert.ok(trackSpy.notCalled, "No fetch called yet");
|
|
877
|
+
fetch(url, init).then(() => {
|
|
878
|
+
// Assert
|
|
879
|
+
Assert.ok(trackSpy.called, "The request was not tracked");
|
|
880
|
+
// Assert that both headers are sent
|
|
881
|
+
Assert.equal(1, fetchCalls.length);
|
|
882
|
+
Assert.notEqual(undefined, fetchCalls[0].init, "Has init param");
|
|
883
|
+
let headers:Headers = fetchCalls[0].init.headers as Headers;
|
|
884
|
+
Assert.notEqual(undefined, headers, "has headers");
|
|
885
|
+
Assert.equal(true, headers.has("My-Header"), "My-Header should be present");
|
|
886
|
+
Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI
|
|
887
|
+
Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C
|
|
888
|
+
}, () => {
|
|
889
|
+
Assert.ok(false, "fetch failed!");
|
|
890
|
+
testContext.testDone();
|
|
891
|
+
});
|
|
892
|
+
}]
|
|
893
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
894
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
895
|
+
if (trackStub.called) {
|
|
896
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
897
|
+
let data = trackStub.args[0][0].baseData;
|
|
898
|
+
Assert.equal("Fetch", data.type, "request is Fatch type");
|
|
899
|
+
var id = data.id;
|
|
900
|
+
Assert.equal("|", id[0]);
|
|
901
|
+
Assert.equal(".", id[id.length - 1]);
|
|
902
|
+
return true;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
return false;
|
|
906
|
+
}, 'response received', 60, 1000) as any)
|
|
907
|
+
})
|
|
908
|
+
|
|
909
|
+
this.testCaseAsync({
|
|
910
|
+
name: "Fetch: should create and pass a traceparent header if ai and w3c is enabled with no init param",
|
|
911
|
+
stepDelay: 10,
|
|
912
|
+
timeOut: 10000,
|
|
913
|
+
steps: [ (testContext) => {
|
|
914
|
+
let fetchCalls = hookFetch((resolve) => {
|
|
915
|
+
AITestClass.orgSetTimeout(function() {
|
|
916
|
+
resolve({
|
|
917
|
+
headers: new Headers(),
|
|
918
|
+
ok: true,
|
|
919
|
+
body: null,
|
|
920
|
+
bodyUsed: false,
|
|
921
|
+
redirected: false,
|
|
922
|
+
status: 200,
|
|
923
|
+
statusText: "Hello",
|
|
924
|
+
trailer: null,
|
|
925
|
+
type: "basic",
|
|
926
|
+
url: "https://httpbin.org/status/200"
|
|
927
|
+
});
|
|
928
|
+
}, 0);
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
this._ajax = new AjaxMonitor();
|
|
932
|
+
let appInsightsCore = new AppInsightsCore();
|
|
933
|
+
let coreConfig = {
|
|
934
|
+
instrumentationKey: "instrumentationKey",
|
|
935
|
+
disableFetchTracking: false,
|
|
936
|
+
disableAjaxTracking: false,
|
|
937
|
+
extensionConfig: {
|
|
938
|
+
"AjaxDependencyPlugin": {
|
|
939
|
+
appId: "appId",
|
|
940
|
+
distributedTracingMode: DistributedTracingModes.AI_AND_W3C
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
945
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
946
|
+
this._context["trackStub"] = trackSpy;
|
|
947
|
+
|
|
948
|
+
// Use test hook to simulate the correct url location
|
|
949
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
950
|
+
|
|
951
|
+
// Setup
|
|
952
|
+
const url = 'https://httpbin.org/status/200';
|
|
953
|
+
|
|
954
|
+
// Act
|
|
955
|
+
Assert.ok(trackSpy.notCalled, "No fetch called yet");
|
|
956
|
+
fetch(url).then(() => {
|
|
957
|
+
// Assert
|
|
958
|
+
Assert.ok(trackSpy.called, "The request was not tracked");
|
|
959
|
+
// Assert that both headers are sent
|
|
960
|
+
Assert.equal(1, fetchCalls.length);
|
|
961
|
+
Assert.notEqual(undefined, fetchCalls[0].init, "Has init param");
|
|
962
|
+
let headers:Headers = fetchCalls[0].init.headers as Headers;
|
|
963
|
+
Assert.notEqual(undefined, headers, "has headers");
|
|
964
|
+
Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI
|
|
965
|
+
Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C
|
|
966
|
+
}, () => {
|
|
967
|
+
Assert.ok(false, "fetch failed!");
|
|
968
|
+
testContext.testDone();
|
|
969
|
+
});
|
|
970
|
+
}]
|
|
971
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
972
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
973
|
+
if (trackStub.called) {
|
|
974
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
975
|
+
let data = trackStub.args[0][0].baseData;
|
|
976
|
+
Assert.equal("Fetch", data.type, "request is Fatch type");
|
|
977
|
+
var id = data.id;
|
|
978
|
+
Assert.equal("|", id[0]);
|
|
979
|
+
Assert.equal(".", id[id.length - 1]);
|
|
980
|
+
return true;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
return false;
|
|
984
|
+
}, 'response received', 60, 1000) as any)
|
|
985
|
+
})
|
|
986
|
+
|
|
987
|
+
this.testCaseAsync({
|
|
988
|
+
name: "Fetch: should create and pass a traceparent header if w3c only is enabled with custom headers",
|
|
989
|
+
stepDelay: 10,
|
|
990
|
+
timeOut: 10000,
|
|
991
|
+
steps: [ (testContext) => {
|
|
992
|
+
let fetchCalls = hookFetch((resolve) => {
|
|
993
|
+
AITestClass.orgSetTimeout(function() {
|
|
994
|
+
resolve({
|
|
995
|
+
headers: new Headers(),
|
|
996
|
+
ok: true,
|
|
997
|
+
body: null,
|
|
998
|
+
bodyUsed: false,
|
|
999
|
+
redirected: false,
|
|
1000
|
+
status: 200,
|
|
1001
|
+
statusText: "Hello",
|
|
1002
|
+
trailer: null,
|
|
1003
|
+
type: "basic",
|
|
1004
|
+
url: "https://httpbin.org/status/200"
|
|
1005
|
+
});
|
|
1006
|
+
}, 0);
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
this._ajax = new AjaxMonitor();
|
|
1010
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1011
|
+
let coreConfig = {
|
|
1012
|
+
instrumentationKey: "instrumentationKey",
|
|
1013
|
+
disableFetchTracking: false,
|
|
1014
|
+
disableAjaxTracking: false,
|
|
1015
|
+
extensionConfig: {
|
|
1016
|
+
"AjaxDependencyPlugin": {
|
|
1017
|
+
appId: "appId",
|
|
1018
|
+
distributedTracingMode: DistributedTracingModes.W3C
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1023
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
1024
|
+
this._context["trackStub"] = trackSpy;
|
|
1025
|
+
|
|
1026
|
+
// Use test hook to simulate the correct url location
|
|
1027
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
1028
|
+
|
|
1029
|
+
// Setup
|
|
1030
|
+
let headers = new Headers();
|
|
1031
|
+
headers.append('My-Header', 'Header field');
|
|
1032
|
+
let init = {
|
|
1033
|
+
method: 'get',
|
|
1034
|
+
headers: headers
|
|
1035
|
+
};
|
|
1036
|
+
const url = 'https://httpbin.org/status/200';
|
|
1037
|
+
|
|
1038
|
+
// Act
|
|
1039
|
+
Assert.ok(trackSpy.notCalled, "No fetch called yet");
|
|
1040
|
+
fetch(url, init).then(() => {
|
|
1041
|
+
// Assert
|
|
1042
|
+
Assert.ok(trackSpy.called, "The request was not tracked");
|
|
1043
|
+
// Assert that both headers are sent
|
|
1044
|
+
Assert.equal(1, fetchCalls.length);
|
|
1045
|
+
Assert.notEqual(undefined, fetchCalls[0].init, "Has init param");
|
|
1046
|
+
let headers:Headers = fetchCalls[0].init.headers as Headers;
|
|
1047
|
+
Assert.notEqual(undefined, headers, "has headers");
|
|
1048
|
+
Assert.equal(true, headers.has("My-Header"), "My-Header should be present");
|
|
1049
|
+
Assert.equal(false, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI
|
|
1050
|
+
Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C
|
|
1051
|
+
}, () => {
|
|
1052
|
+
Assert.ok(false, "fetch failed!");
|
|
1053
|
+
testContext.testDone();
|
|
1054
|
+
});
|
|
1055
|
+
}]
|
|
1056
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
1057
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
1058
|
+
if (trackStub.called) {
|
|
1059
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
1060
|
+
let data = trackStub.args[0][0].baseData;
|
|
1061
|
+
Assert.equal("Fetch", data.type, "request is Fatch type");
|
|
1062
|
+
var id = data.id;
|
|
1063
|
+
Assert.equal("|", id[0]);
|
|
1064
|
+
Assert.equal(".", id[id.length - 1]);
|
|
1065
|
+
return true;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
return false;
|
|
1069
|
+
}, 'response received', 60, 1000) as any)
|
|
1070
|
+
})
|
|
1071
|
+
|
|
1072
|
+
this.testCaseAsync({
|
|
1073
|
+
name: "Fetch: should create and pass a traceparent header if w3c only is enabled with no init param",
|
|
1074
|
+
stepDelay: 10,
|
|
1075
|
+
timeOut: 10000,
|
|
1076
|
+
steps: [ (testContext) => {
|
|
1077
|
+
let fetchCalls = hookFetch((resolve) => {
|
|
1078
|
+
AITestClass.orgSetTimeout(function() {
|
|
1079
|
+
resolve({
|
|
1080
|
+
headers: new Headers(),
|
|
1081
|
+
ok: true,
|
|
1082
|
+
body: null,
|
|
1083
|
+
bodyUsed: false,
|
|
1084
|
+
redirected: false,
|
|
1085
|
+
status: 200,
|
|
1086
|
+
statusText: "Hello",
|
|
1087
|
+
trailer: null,
|
|
1088
|
+
type: "basic",
|
|
1089
|
+
url: "https://httpbin.org/status/200"
|
|
1090
|
+
});
|
|
1091
|
+
}, 0);
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
this._ajax = new AjaxMonitor();
|
|
1095
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1096
|
+
let coreConfig = {
|
|
1097
|
+
instrumentationKey: "instrumentationKey",
|
|
1098
|
+
disableFetchTracking: false,
|
|
1099
|
+
disableAjaxTracking: false,
|
|
1100
|
+
extensionConfig: {
|
|
1101
|
+
"AjaxDependencyPlugin": {
|
|
1102
|
+
appId: "appId",
|
|
1103
|
+
distributedTracingMode: DistributedTracingModes.W3C
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1108
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
1109
|
+
this._context["trackStub"] = trackSpy;
|
|
1110
|
+
|
|
1111
|
+
// Use test hook to simulate the correct url location
|
|
1112
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
1113
|
+
|
|
1114
|
+
// Setup
|
|
1115
|
+
const url = 'https://httpbin.org/status/200';
|
|
1116
|
+
|
|
1117
|
+
// Act
|
|
1118
|
+
Assert.ok(trackSpy.notCalled, "No fetch called yet");
|
|
1119
|
+
fetch(url).then(() => {
|
|
1120
|
+
// Assert
|
|
1121
|
+
Assert.ok(trackSpy.called, "The request was not tracked");
|
|
1122
|
+
// Assert that both headers are sent
|
|
1123
|
+
Assert.equal(1, fetchCalls.length);
|
|
1124
|
+
Assert.notEqual(undefined, fetchCalls[0].init, "Has init param");
|
|
1125
|
+
let headers:Headers = fetchCalls[0].init.headers as Headers;
|
|
1126
|
+
Assert.notEqual(undefined, headers, "has headers");
|
|
1127
|
+
Assert.equal(false, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI
|
|
1128
|
+
Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C
|
|
1129
|
+
}, () => {
|
|
1130
|
+
Assert.ok(false, "fetch failed!");
|
|
1131
|
+
testContext.testDone();
|
|
1132
|
+
});
|
|
1133
|
+
}]
|
|
1134
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
1135
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
1136
|
+
if (trackStub.called) {
|
|
1137
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
1138
|
+
let data = trackStub.args[0][0].baseData;
|
|
1139
|
+
Assert.equal("Fetch", data.type, "request is Fatch type");
|
|
1140
|
+
var id = data.id;
|
|
1141
|
+
Assert.equal("|", id[0]);
|
|
1142
|
+
Assert.equal(".", id[id.length - 1]);
|
|
1143
|
+
return true;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
return false;
|
|
1147
|
+
}, 'response received', 60, 1000) as any)
|
|
1148
|
+
})
|
|
1149
|
+
|
|
1150
|
+
this.testCaseAsync({
|
|
1151
|
+
name: "Fetch: should create and pass a request header if AI only is enabled with custom headers",
|
|
1152
|
+
stepDelay: 10,
|
|
1153
|
+
timeOut: 10000,
|
|
1154
|
+
steps: [ (testContext) => {
|
|
1155
|
+
let fetchCalls = hookFetch((resolve) => {
|
|
1156
|
+
AITestClass.orgSetTimeout(function() {
|
|
1157
|
+
resolve({
|
|
1158
|
+
headers: new Headers(),
|
|
1159
|
+
ok: true,
|
|
1160
|
+
body: null,
|
|
1161
|
+
bodyUsed: false,
|
|
1162
|
+
redirected: false,
|
|
1163
|
+
status: 200,
|
|
1164
|
+
statusText: "Hello",
|
|
1165
|
+
trailer: null,
|
|
1166
|
+
type: "basic",
|
|
1167
|
+
url: "https://httpbin.org/status/200"
|
|
1168
|
+
});
|
|
1169
|
+
}, 0);
|
|
1170
|
+
});
|
|
1171
|
+
|
|
1172
|
+
this._ajax = new AjaxMonitor();
|
|
1173
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1174
|
+
let coreConfig = {
|
|
1175
|
+
instrumentationKey: "instrumentationKey",
|
|
1176
|
+
disableFetchTracking: false,
|
|
1177
|
+
disableAjaxTracking: false,
|
|
1178
|
+
extensionConfig: {
|
|
1179
|
+
"AjaxDependencyPlugin": {
|
|
1180
|
+
appId: "appId",
|
|
1181
|
+
distributedTracingMode: DistributedTracingModes.AI
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1186
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
1187
|
+
this._context["trackStub"] = trackSpy;
|
|
1188
|
+
|
|
1189
|
+
// Use test hook to simulate the correct url location
|
|
1190
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
1191
|
+
|
|
1192
|
+
// Setup
|
|
1193
|
+
let headers = new Headers();
|
|
1194
|
+
headers.append('My-Header', 'Header field');
|
|
1195
|
+
let init = {
|
|
1196
|
+
method: 'get',
|
|
1197
|
+
headers: headers
|
|
1198
|
+
};
|
|
1199
|
+
const url = 'https://httpbin.org/status/200';
|
|
1200
|
+
|
|
1201
|
+
// Act
|
|
1202
|
+
Assert.ok(trackSpy.notCalled, "No fetch called yet");
|
|
1203
|
+
fetch(url, init).then(() => {
|
|
1204
|
+
// Assert
|
|
1205
|
+
Assert.ok(trackSpy.called, "The request was not tracked");
|
|
1206
|
+
// Assert that both headers are sent
|
|
1207
|
+
Assert.equal(1, fetchCalls.length);
|
|
1208
|
+
Assert.notEqual(undefined, fetchCalls[0].init, "Has init param");
|
|
1209
|
+
let headers:Headers = fetchCalls[0].init.headers as Headers;
|
|
1210
|
+
Assert.notEqual(undefined, headers, "has headers");
|
|
1211
|
+
Assert.equal(true, headers.has("My-Header"), "My-Header should be present");
|
|
1212
|
+
Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI
|
|
1213
|
+
Assert.equal(false, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C
|
|
1214
|
+
}, () => {
|
|
1215
|
+
Assert.ok(false, "fetch failed!");
|
|
1216
|
+
testContext.testDone();
|
|
1217
|
+
});
|
|
1218
|
+
}]
|
|
1219
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
1220
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
1221
|
+
if (trackStub.called) {
|
|
1222
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
1223
|
+
let data = trackStub.args[0][0].baseData;
|
|
1224
|
+
Assert.equal("Fetch", data.type, "request is Fatch type");
|
|
1225
|
+
var id = data.id;
|
|
1226
|
+
Assert.equal("|", id[0]);
|
|
1227
|
+
return true;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
return false;
|
|
1231
|
+
}, 'response received', 60, 1000) as any)
|
|
1232
|
+
})
|
|
1233
|
+
|
|
1234
|
+
this.testCaseAsync({
|
|
1235
|
+
name: "Fetch: should create and pass a request header if AI only is enabled with no init param",
|
|
1236
|
+
stepDelay: 10,
|
|
1237
|
+
timeOut: 10000,
|
|
1238
|
+
steps: [ (testContext) => {
|
|
1239
|
+
let fetchCalls = hookFetch((resolve) => {
|
|
1240
|
+
AITestClass.orgSetTimeout(function() {
|
|
1241
|
+
resolve({
|
|
1242
|
+
headers: new Headers(),
|
|
1243
|
+
ok: true,
|
|
1244
|
+
body: null,
|
|
1245
|
+
bodyUsed: false,
|
|
1246
|
+
redirected: false,
|
|
1247
|
+
status: 200,
|
|
1248
|
+
statusText: "Hello",
|
|
1249
|
+
trailer: null,
|
|
1250
|
+
type: "basic",
|
|
1251
|
+
url: "https://httpbin.org/status/200"
|
|
1252
|
+
});
|
|
1253
|
+
}, 0);
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
this._ajax = new AjaxMonitor();
|
|
1257
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1258
|
+
let coreConfig = {
|
|
1259
|
+
instrumentationKey: "instrumentationKey",
|
|
1260
|
+
disableFetchTracking: false,
|
|
1261
|
+
disableAjaxTracking: false,
|
|
1262
|
+
extensionConfig: {
|
|
1263
|
+
"AjaxDependencyPlugin": {
|
|
1264
|
+
appId: "appId",
|
|
1265
|
+
distributedTracingMode: DistributedTracingModes.AI
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1270
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
1271
|
+
this._context["trackStub"] = trackSpy;
|
|
1272
|
+
|
|
1273
|
+
// Use test hook to simulate the correct url location
|
|
1274
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
1275
|
+
|
|
1276
|
+
// Setup
|
|
1277
|
+
const url = 'https://httpbin.org/status/200';
|
|
1278
|
+
|
|
1279
|
+
// Act
|
|
1280
|
+
Assert.ok(trackSpy.notCalled, "No fetch called yet");
|
|
1281
|
+
fetch(url).then(() => {
|
|
1282
|
+
// Assert
|
|
1283
|
+
Assert.ok(trackSpy.called, "The request was not tracked");
|
|
1284
|
+
// Assert that both headers are sent
|
|
1285
|
+
Assert.equal(1, fetchCalls.length);
|
|
1286
|
+
Assert.notEqual(undefined, fetchCalls[0].init, "Has init param");
|
|
1287
|
+
let headers:Headers = fetchCalls[0].init.headers as Headers;
|
|
1288
|
+
Assert.notEqual(undefined, headers, "has headers");
|
|
1289
|
+
Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI
|
|
1290
|
+
Assert.equal(false, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C
|
|
1291
|
+
}, () => {
|
|
1292
|
+
Assert.ok(false, "fetch failed!");
|
|
1293
|
+
testContext.testDone();
|
|
1294
|
+
});
|
|
1295
|
+
}]
|
|
1296
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
1297
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
1298
|
+
if (trackStub.called) {
|
|
1299
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
1300
|
+
let data = trackStub.args[0][0].baseData;
|
|
1301
|
+
Assert.equal("Fetch", data.type, "request is Fatch type");
|
|
1302
|
+
var id = data.id;
|
|
1303
|
+
Assert.equal("|", id[0]);
|
|
1304
|
+
return true;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
return false;
|
|
1308
|
+
}, 'response received', 60, 1000) as any)
|
|
1309
|
+
})
|
|
1310
|
+
|
|
1311
|
+
this.testCaseAsync({
|
|
1312
|
+
name: "Fetch: should add request headers to all valid argument variants",
|
|
1313
|
+
stepDelay: 10,
|
|
1314
|
+
timeOut: 10000,
|
|
1315
|
+
useFakeTimers: true,
|
|
1316
|
+
steps: [ (testContext) => {
|
|
1317
|
+
this._context["fetchCalls"] = hookFetch((resolve) => {
|
|
1318
|
+
AITestClass.orgSetTimeout(function() {
|
|
1319
|
+
resolve({
|
|
1320
|
+
headers: new Headers(),
|
|
1321
|
+
ok: true,
|
|
1322
|
+
body: null,
|
|
1323
|
+
bodyUsed: false,
|
|
1324
|
+
redirected: false,
|
|
1325
|
+
status: 200,
|
|
1326
|
+
statusText: "Hello",
|
|
1327
|
+
trailer: null,
|
|
1328
|
+
type: "basic",
|
|
1329
|
+
url: "https://httpbin.org/status/200"
|
|
1330
|
+
});
|
|
1331
|
+
}, 0);
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
this._ajax = new AjaxMonitor();
|
|
1335
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1336
|
+
let coreConfig = {
|
|
1337
|
+
instrumentationKey: "instrumentationKey",
|
|
1338
|
+
disableFetchTracking: false,
|
|
1339
|
+
disableAjaxTracking: false,
|
|
1340
|
+
enableRequestHeaderTracking: true,
|
|
1341
|
+
extensionConfig: {
|
|
1342
|
+
"AjaxDependencyPlugin": {
|
|
1343
|
+
appId: "appId",
|
|
1344
|
+
distributedTracingMode: DistributedTracingModes.AI_AND_W3C
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
};
|
|
1348
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1349
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
1350
|
+
this._context["trackStub"] = trackSpy;
|
|
1351
|
+
|
|
1352
|
+
// Use test hook to simulate the correct url location
|
|
1353
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
1354
|
+
|
|
1355
|
+
// Setup
|
|
1356
|
+
let headers = new Headers();
|
|
1357
|
+
headers.append('My-Header', 'Header field');
|
|
1358
|
+
headers.append("Authorization","Authorization");
|
|
1359
|
+
let init = {
|
|
1360
|
+
method: 'get',
|
|
1361
|
+
headers: headers
|
|
1362
|
+
};
|
|
1363
|
+
const url = 'https://httpbin.org/status/200';
|
|
1364
|
+
|
|
1365
|
+
Assert.ok(trackSpy.notCalled, "No fetch called yet");
|
|
1366
|
+
fetch(url);
|
|
1367
|
+
fetch(url, {});
|
|
1368
|
+
fetch(url, { headers: {} });
|
|
1369
|
+
fetch(url, { headers: new Headers() });
|
|
1370
|
+
fetch(url, { headers });
|
|
1371
|
+
fetch(url, init);
|
|
1372
|
+
fetch(new Request(url));
|
|
1373
|
+
fetch(new Request(url, {}));
|
|
1374
|
+
fetch(new Request(url, { headers: {} }));
|
|
1375
|
+
fetch(new Request(url, { headers: new Headers() }));
|
|
1376
|
+
fetch(new Request(url, { headers }));
|
|
1377
|
+
fetch(new Request(url, init));
|
|
1378
|
+
}]
|
|
1379
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
1380
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
1381
|
+
let fetchCalls = this._context["fetchCalls"] as IFetchArgs[];
|
|
1382
|
+
Assert.ok(true, "Track: " + trackStub.args.length + " Fetch Calls: " + fetchCalls.length);
|
|
1383
|
+
if (trackStub.called && trackStub.args.length === 12 && fetchCalls.length === 12) {
|
|
1384
|
+
for (let lp = 0; lp < trackStub.args.length; lp++) {
|
|
1385
|
+
let evtData = trackStub.args[lp][0];
|
|
1386
|
+
this._checkFetchTraceId(evtData, "Fetch " + lp);
|
|
1387
|
+
let properties = evtData.baseData.properties || {};
|
|
1388
|
+
let trackHeaders = properties.requestHeaders || {};
|
|
1389
|
+
|
|
1390
|
+
Assert.notEqual(undefined, fetchCalls[lp].init, "Has init param");
|
|
1391
|
+
let headers:Headers = fetchCalls[lp].init.headers as Headers;
|
|
1392
|
+
Assert.notEqual(undefined, headers, "has headers");
|
|
1393
|
+
switch (lp) {
|
|
1394
|
+
case 4:
|
|
1395
|
+
case 5:
|
|
1396
|
+
case 10:
|
|
1397
|
+
case 11:
|
|
1398
|
+
// All headers should be added to the init (2nd param) as this overrides
|
|
1399
|
+
// any headers on any request object
|
|
1400
|
+
Assert.equal(true, headers.has("My-Header"), "My-Header should be present");
|
|
1401
|
+
Assert.equal(true, headers.has("Authorization"), "Authorization should be present");
|
|
1402
|
+
Assert.equal("Header field", trackHeaders["my-header"], "my-header present in outbound event");
|
|
1403
|
+
Assert.equal(undefined, trackHeaders["Authorization"],"Authorization header should be ignored")
|
|
1404
|
+
break;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
Assert.equal(true, headers.has(RequestHeaders.requestContextHeader), "requestContext header shoud be present");
|
|
1408
|
+
Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI
|
|
1409
|
+
Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C
|
|
1410
|
+
|
|
1411
|
+
Assert.notEqual(undefined, trackHeaders[RequestHeaders.requestIdHeader], "RequestId present in outbound event");
|
|
1412
|
+
Assert.notEqual(undefined, trackHeaders[RequestHeaders.requestContextHeader], "RequestContext present in outbound event");
|
|
1413
|
+
Assert.notEqual(undefined, trackHeaders[RequestHeaders.traceParentHeader], "traceParent present in outbound event");
|
|
1414
|
+
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
return true;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
this.clock.tick(1000);
|
|
1421
|
+
return false;
|
|
1422
|
+
}, 'response received', 60, 1000) as any)
|
|
1423
|
+
})
|
|
1424
|
+
|
|
1425
|
+
this.testCase({
|
|
1426
|
+
name: "Ajax: successful request, ajax monitor doesn't change payload",
|
|
1427
|
+
test: () => {
|
|
1428
|
+
var callback = this.sandbox.spy();
|
|
1429
|
+
this._ajax = new AjaxMonitor();
|
|
1430
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1431
|
+
let coreConfig = { instrumentationKey: "instrumentationKey", extensionConfig: {"AjaxPlugin": {}}};
|
|
1432
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1433
|
+
|
|
1434
|
+
// Act
|
|
1435
|
+
var xhr = new XMLHttpRequest();
|
|
1436
|
+
xhr.onload = callback;
|
|
1437
|
+
xhr.open("GET", "example.com/bla");
|
|
1438
|
+
xhr.send();
|
|
1439
|
+
|
|
1440
|
+
|
|
1441
|
+
// Emulate response
|
|
1442
|
+
(<any>xhr).respond(200, { "Content-Type": "application/json" }, "bla");
|
|
1443
|
+
//Assert.ok((<any>ajaxMonitor)._trackAjaxAttempts === 1, "TrackAjax is called");
|
|
1444
|
+
|
|
1445
|
+
// Assert
|
|
1446
|
+
var result = callback.args[0][0].target;
|
|
1447
|
+
Assert.ok(callback.called, "Ajax callback is called");
|
|
1448
|
+
Assert.equal("bla", result.responseText, "Expected result match");
|
|
1449
|
+
Assert.equal(200, result.status, "Expected 200 response code");
|
|
1450
|
+
Assert.equal(4, xhr.readyState, "Expected readyState is 4 after request is finished");
|
|
1451
|
+
}
|
|
1452
|
+
});
|
|
1453
|
+
|
|
1454
|
+
this.testCase({
|
|
1455
|
+
name: "Ajax: custom onreadystatechange gets called",
|
|
1456
|
+
useFakeServer: true,
|
|
1457
|
+
useFakeTimers: true,
|
|
1458
|
+
test: () => {
|
|
1459
|
+
var onreadystatechangeSpy = this.sandbox.spy();
|
|
1460
|
+
this._ajax = new AjaxMonitor();
|
|
1461
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1462
|
+
let coreConfig = { instrumentationKey: "instrumentationKey", extensionConfig: {"AjaxPlugin": {}}};
|
|
1463
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1464
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
1465
|
+
|
|
1466
|
+
// Act
|
|
1467
|
+
var xhr = new XMLHttpRequest();
|
|
1468
|
+
xhr.onreadystatechange = onreadystatechangeSpy;
|
|
1469
|
+
xhr.open("GET", "example.com/bla");
|
|
1470
|
+
xhr.send();
|
|
1471
|
+
|
|
1472
|
+
Assert.ok(trackStub.notCalled, "track should not be called yet");
|
|
1473
|
+
|
|
1474
|
+
// Emulate response
|
|
1475
|
+
(<any>xhr).respond(200, {}, "");
|
|
1476
|
+
|
|
1477
|
+
// Assert
|
|
1478
|
+
Assert.ok(trackStub.called, "TrackAjax is called");
|
|
1479
|
+
Assert.ok(onreadystatechangeSpy.callCount >= 4, "custom onreadystatechange should be called");
|
|
1480
|
+
}
|
|
1481
|
+
});
|
|
1482
|
+
|
|
1483
|
+
this.testCase({
|
|
1484
|
+
name: "Ajax: 200 means success",
|
|
1485
|
+
test: () => {
|
|
1486
|
+
this._ajax = new AjaxMonitor();
|
|
1487
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1488
|
+
let coreConfig = { instrumentationKey: "instrumentationKey", extensionConfig: {"AjaxPlugin": {}}};
|
|
1489
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1490
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
1491
|
+
|
|
1492
|
+
// Act
|
|
1493
|
+
var xhr = new XMLHttpRequest();
|
|
1494
|
+
xhr.open("GET", "example.com/bla");
|
|
1495
|
+
xhr.send();
|
|
1496
|
+
|
|
1497
|
+
// Emulate response
|
|
1498
|
+
(<any>xhr).respond(200, {}, "");
|
|
1499
|
+
|
|
1500
|
+
// Assert
|
|
1501
|
+
let data = trackStub.args[0][0].baseData;
|
|
1502
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
1503
|
+
Assert.equal(true, data.success, "TrackAjax should receive true as a 'success' argument");
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
|
|
1507
|
+
this.testCase({
|
|
1508
|
+
name: "Ajax: non 200 means failure",
|
|
1509
|
+
test: () => {
|
|
1510
|
+
this._ajax = new AjaxMonitor();
|
|
1511
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1512
|
+
let coreConfig = { instrumentationKey: "instrumentationKey", extensionConfig: {"AjaxPlugin": {}}};
|
|
1513
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1514
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
1515
|
+
|
|
1516
|
+
// Act
|
|
1517
|
+
var xhr = new XMLHttpRequest();
|
|
1518
|
+
xhr.open("GET", "example.com/bla");
|
|
1519
|
+
xhr.send();
|
|
1520
|
+
|
|
1521
|
+
// Emulate response
|
|
1522
|
+
(<any>xhr).respond(404, {}, "");
|
|
1523
|
+
|
|
1524
|
+
// Assert
|
|
1525
|
+
let data = trackStub.args[0][0].baseData;
|
|
1526
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
1527
|
+
Assert.equal(false, data.success, "TrackAjax should receive false as a 'success' argument");
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
|
|
1531
|
+
[200, 201, 202, 203, 204, 301, 302, 303, 304].forEach((responseCode) => {
|
|
1532
|
+
this.testCase({
|
|
1533
|
+
name: "Ajax: test success http response code: " + responseCode,
|
|
1534
|
+
test: () => {
|
|
1535
|
+
this.testAjaxSuccess(responseCode, true);
|
|
1536
|
+
}
|
|
1537
|
+
})
|
|
1538
|
+
});
|
|
1539
|
+
|
|
1540
|
+
[400, 401, 402, 403, 404, 500, 501].forEach((responseCode) => {
|
|
1541
|
+
this.testCase({
|
|
1542
|
+
name: "Ajax: test failure http response code: " + responseCode,
|
|
1543
|
+
test: () => {
|
|
1544
|
+
this.testAjaxSuccess(responseCode, false);
|
|
1545
|
+
}
|
|
1546
|
+
})
|
|
1547
|
+
});
|
|
1548
|
+
|
|
1549
|
+
this.testCase({
|
|
1550
|
+
name: "Ajax: overriding ready state change handlers in all possible ways",
|
|
1551
|
+
test: () => {
|
|
1552
|
+
this._ajax = new AjaxMonitor();
|
|
1553
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1554
|
+
let coreConfig = { instrumentationKey: "instrumentationKey", extensionConfig: {"AjaxPlugin": {}}};
|
|
1555
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1556
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
1557
|
+
var cb1 = this.sandbox.spy();
|
|
1558
|
+
var cb2 = this.sandbox.spy();
|
|
1559
|
+
var cb3 = this.sandbox.spy();
|
|
1560
|
+
var cb4 = this.sandbox.spy();
|
|
1561
|
+
var cb5 = this.sandbox.spy();
|
|
1562
|
+
var cb6 = this.sandbox.spy();
|
|
1563
|
+
var cb7 = this.sandbox.spy();
|
|
1564
|
+
|
|
1565
|
+
// Act
|
|
1566
|
+
var xhr = new XMLHttpRequest();
|
|
1567
|
+
xhr.addEventListener("readystatechange", cb1);
|
|
1568
|
+
xhr.addEventListener("readystatechange", cb2);
|
|
1569
|
+
xhr.open("GET", "example.com/bla");
|
|
1570
|
+
xhr.onreadystatechange = cb3;
|
|
1571
|
+
xhr.addEventListener("readystatechange", cb4);
|
|
1572
|
+
xhr.addEventListener("readystatechange", cb5);
|
|
1573
|
+
xhr.send();
|
|
1574
|
+
xhr.addEventListener("readystatechange", cb6);
|
|
1575
|
+
xhr.addEventListener("readystatechange", cb7);
|
|
1576
|
+
|
|
1577
|
+
Assert.ok(!trackStub.called, "TrackAjax should not be called yet");
|
|
1578
|
+
|
|
1579
|
+
// Emulate response
|
|
1580
|
+
(<any>xhr).respond(404, {}, "");
|
|
1581
|
+
|
|
1582
|
+
// Assert
|
|
1583
|
+
Assert.ok(trackStub.calledOnce, "TrackAjax should be called");
|
|
1584
|
+
Assert.ok(cb1.called, "callback 1 should be called");
|
|
1585
|
+
Assert.ok(cb2.called, "callback 2 should be called");
|
|
1586
|
+
Assert.ok(cb3.called, "callback 3 should be called");
|
|
1587
|
+
Assert.ok(cb4.called, "callback 4 should be called");
|
|
1588
|
+
Assert.ok(cb5.called, "callback 5 should be called");
|
|
1589
|
+
Assert.ok(cb6.called, "callback 6 should be called");
|
|
1590
|
+
Assert.ok(cb7.called, "callback 7 should be called");
|
|
1591
|
+
}
|
|
1592
|
+
});
|
|
1593
|
+
|
|
1594
|
+
this.testCase({
|
|
1595
|
+
name: "Ajax: test ajax duration is calculated correctly",
|
|
1596
|
+
test: () => {
|
|
1597
|
+
var initialPerformance = window.performance;
|
|
1598
|
+
this._ajax = new AjaxMonitor();
|
|
1599
|
+
try {
|
|
1600
|
+
// Mocking window performance (sinon doesn't have it).
|
|
1601
|
+
// tick() is similar to sinon's clock.tick()
|
|
1602
|
+
(<any>window).performance = <any>{
|
|
1603
|
+
current: 0,
|
|
1604
|
+
|
|
1605
|
+
now: function () {
|
|
1606
|
+
return this.current;
|
|
1607
|
+
},
|
|
1608
|
+
|
|
1609
|
+
tick: function (ms: number) {
|
|
1610
|
+
this.current += ms;
|
|
1611
|
+
},
|
|
1612
|
+
|
|
1613
|
+
timing: initialPerformance.timing
|
|
1614
|
+
};
|
|
1615
|
+
|
|
1616
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1617
|
+
let coreConfig = { instrumentationKey: "instrumentationKey", extensionConfig: {"AjaxPlugin": {}}};
|
|
1618
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1619
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
1620
|
+
// tick to set the initial time be non zero
|
|
1621
|
+
(<any>window.performance).tick(23);
|
|
1622
|
+
|
|
1623
|
+
// Act
|
|
1624
|
+
var xhr = new XMLHttpRequest();
|
|
1625
|
+
var clock = this.clock;
|
|
1626
|
+
var expectedResponseDuration = 50;
|
|
1627
|
+
xhr.onreadystatechange = () => {
|
|
1628
|
+
if (xhr.readyState == 3) {
|
|
1629
|
+
(<any>window.performance).tick(expectedResponseDuration);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
xhr.open("GET", "example.com/bla");
|
|
1633
|
+
xhr.send();
|
|
1634
|
+
// Emulate response
|
|
1635
|
+
(<any>xhr).respond(404, {}, "");
|
|
1636
|
+
|
|
1637
|
+
// Assert
|
|
1638
|
+
Assert.ok(trackStub.calledOnce, "TrackAjax should be called");
|
|
1639
|
+
let data = trackStub.args[0][0].baseData;
|
|
1640
|
+
Assert.equal("Ajax", data.type, "It should be an XHR request");
|
|
1641
|
+
Assert.ok(data.startTime);
|
|
1642
|
+
Assert.equal(expectedResponseDuration, data.duration, "Ajax duration should match expected duration");
|
|
1643
|
+
} finally {
|
|
1644
|
+
(<any>window.performance) = initialPerformance;
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
});
|
|
1648
|
+
|
|
1649
|
+
this.testCase({
|
|
1650
|
+
name: "Ajax: 2nd invokation of xhr.send doesn't cause send wrapper to execute 2nd time",
|
|
1651
|
+
test: () => {
|
|
1652
|
+
this._ajax = new AjaxMonitor();
|
|
1653
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1654
|
+
let coreConfig = { instrumentationKey: "instrumentationKey", extensionConfig: {"AjaxPlugin": {}}};
|
|
1655
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1656
|
+
var spy = this.sandbox.spy(this._ajax, "includeCorrelationHeaders");
|
|
1657
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
1658
|
+
this._context["trackStub"] = trackSpy;
|
|
1659
|
+
|
|
1660
|
+
// Act
|
|
1661
|
+
var xhr = new XMLHttpRequest();
|
|
1662
|
+
xhr.open("GET", "example.com/bla");
|
|
1663
|
+
xhr.send();
|
|
1664
|
+
|
|
1665
|
+
try {
|
|
1666
|
+
xhr.send();
|
|
1667
|
+
} catch (e) { }
|
|
1668
|
+
|
|
1669
|
+
// Emulate response
|
|
1670
|
+
(<any>xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*"}, "");
|
|
1671
|
+
|
|
1672
|
+
// Assert
|
|
1673
|
+
Assert.ok(spy.calledOnce, "sendPrefixInstrumentor/includeCorrelationHeaders should be called only once");
|
|
1674
|
+
}
|
|
1675
|
+
});
|
|
1676
|
+
|
|
1677
|
+
this.testCase({
|
|
1678
|
+
name: "Ajax: 2nd invokation of xhr.send doesn't cause send wrapper to execute 2nd time even if after response",
|
|
1679
|
+
test: () => {
|
|
1680
|
+
this._ajax = new AjaxMonitor();
|
|
1681
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1682
|
+
let coreConfig = { instrumentationKey: "instrumentationKey", extensionConfig: {"AjaxPlugin": {}}};
|
|
1683
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1684
|
+
var spy = this.sandbox.spy(this._ajax, "includeCorrelationHeaders");
|
|
1685
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
1686
|
+
this._context["trackStub"] = trackSpy;
|
|
1687
|
+
|
|
1688
|
+
// Act
|
|
1689
|
+
var xhr = new XMLHttpRequest();
|
|
1690
|
+
xhr.open("GET", "example.com/bla");
|
|
1691
|
+
xhr.send();
|
|
1692
|
+
|
|
1693
|
+
// Emulate response
|
|
1694
|
+
(<any>xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*"}, "");
|
|
1695
|
+
|
|
1696
|
+
try {
|
|
1697
|
+
xhr.send();
|
|
1698
|
+
} catch (e) { }
|
|
1699
|
+
|
|
1700
|
+
// Assert
|
|
1701
|
+
Assert.ok(spy.calledOnce, "sendPrefixInstrumentor/includeCorrelationHeaders should be called only once");
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1704
|
+
|
|
1705
|
+
this.testCase({
|
|
1706
|
+
name: "Ajax: 2 invokation of xhr.open() doesn't cause send wrapper to execute 2nd time",
|
|
1707
|
+
test: () => {
|
|
1708
|
+
this._ajax = new AjaxMonitor();
|
|
1709
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1710
|
+
let coreConfig = { instrumentationKey: "instrumentationKey", extensionConfig: {"AjaxPlugin": {}}};
|
|
1711
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1712
|
+
|
|
1713
|
+
// Act
|
|
1714
|
+
var xhr = new XMLHttpRequest();
|
|
1715
|
+
xhr.open("GET", "example.com/bla");
|
|
1716
|
+
|
|
1717
|
+
|
|
1718
|
+
Assert.equal("GET", xhr["ajaxData"].method, "Expecting the ajax data set the method");
|
|
1719
|
+
// Reset the method to something else
|
|
1720
|
+
xhr["ajaxData"].method = "TEST";
|
|
1721
|
+
|
|
1722
|
+
try {
|
|
1723
|
+
xhr.open("GET", "example.com/bla");
|
|
1724
|
+
} catch (e) { }
|
|
1725
|
+
|
|
1726
|
+
Assert.equal("TEST", xhr["ajaxData"].method, "sendPrefixInstrumentor should be called only once");
|
|
1727
|
+
}
|
|
1728
|
+
});
|
|
1729
|
+
|
|
1730
|
+
this.testCase({
|
|
1731
|
+
name: "Ajax: should create and pass a traceparent header if w3c is enabled",
|
|
1732
|
+
test: () => {
|
|
1733
|
+
this._ajax = new AjaxMonitor();
|
|
1734
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1735
|
+
let coreConfig = {
|
|
1736
|
+
instrumentationKey: "instrumentationKey",
|
|
1737
|
+
extensionConfig: {
|
|
1738
|
+
"AjaxDependencyPlugin": {
|
|
1739
|
+
appId: "appId",
|
|
1740
|
+
distributedTracingMode: DistributedTracingModes.AI_AND_W3C
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
};
|
|
1744
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1745
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
1746
|
+
|
|
1747
|
+
// Use test hook to simulate the correct url location
|
|
1748
|
+
this._ajax["_currentWindowHost"] = "www.example.com";
|
|
1749
|
+
|
|
1750
|
+
// Act
|
|
1751
|
+
var xhr = new XMLHttpRequest();
|
|
1752
|
+
var stub = this.sandbox.stub(xhr, "setRequestHeader");
|
|
1753
|
+
xhr.open("GET", "http://www.example.com");
|
|
1754
|
+
xhr.send();
|
|
1755
|
+
|
|
1756
|
+
// Assert that both headers are sent
|
|
1757
|
+
Assert.equal(true, stub.calledWith(RequestHeaders.requestIdHeader)); // AI
|
|
1758
|
+
Assert.equal(true, stub.calledWith(RequestHeaders.traceParentHeader)); // W3C
|
|
1759
|
+
|
|
1760
|
+
// Emulate response so perf monitoring is cleaned up
|
|
1761
|
+
(<any>xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*"}, "");
|
|
1762
|
+
var id = trackStub.args[0][0].baseData.id;
|
|
1763
|
+
Assert.equal("|", id[0]);
|
|
1764
|
+
Assert.equal(".", id[id.length - 1]);
|
|
1765
|
+
}
|
|
1766
|
+
});
|
|
1767
|
+
|
|
1768
|
+
this.testCase({
|
|
1769
|
+
name: "Ajax: should not create and pass a traceparent header if correlationHeaderExcludePatterns set to exclude all",
|
|
1770
|
+
test: () => {
|
|
1771
|
+
this._ajax = new AjaxMonitor();
|
|
1772
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1773
|
+
let coreConfig = {
|
|
1774
|
+
instrumentationKey: "instrumentationKey",
|
|
1775
|
+
correlationHeaderExcludePatterns: [/.*/],
|
|
1776
|
+
extensionConfig: {
|
|
1777
|
+
"AjaxDependencyPlugin": {
|
|
1778
|
+
appId: "appId",
|
|
1779
|
+
distributedTracingMode: DistributedTracingModes.AI_AND_W3C
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1784
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
1785
|
+
|
|
1786
|
+
// Use test hook to simulate the correct url location
|
|
1787
|
+
this._ajax["_currentWindowHost"] = "www.example.com";
|
|
1788
|
+
|
|
1789
|
+
// Act
|
|
1790
|
+
var xhr = new XMLHttpRequest();
|
|
1791
|
+
var stub = this.sandbox.stub(xhr, "setRequestHeader");
|
|
1792
|
+
xhr.open("GET", "http://www.example.com");
|
|
1793
|
+
xhr.send();
|
|
1794
|
+
|
|
1795
|
+
// Assert that both headers are not sent
|
|
1796
|
+
Assert.equal(false, stub.calledWith(RequestHeaders.requestIdHeader)); // AI
|
|
1797
|
+
Assert.equal(false, stub.calledWith(RequestHeaders.traceParentHeader)); // W3C
|
|
1798
|
+
|
|
1799
|
+
// Emulate response so perf monitoring is cleaned up
|
|
1800
|
+
(<any>xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*"}, "");
|
|
1801
|
+
var id = trackStub.args[0][0].baseData.id;
|
|
1802
|
+
Assert.equal("|", id[0]);
|
|
1803
|
+
Assert.equal(".", id[id.length - 1]);
|
|
1804
|
+
}
|
|
1805
|
+
});
|
|
1806
|
+
|
|
1807
|
+
this.testCase({
|
|
1808
|
+
name: "Ajax: should create and only pass a traceparent header if w3c is enabled",
|
|
1809
|
+
test: () => {
|
|
1810
|
+
this._ajax = new AjaxMonitor();
|
|
1811
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1812
|
+
let coreConfig = {
|
|
1813
|
+
instrumentationKey: "instrumentationKey",
|
|
1814
|
+
extensionConfig: {
|
|
1815
|
+
"AjaxDependencyPlugin": {
|
|
1816
|
+
appId: "appId",
|
|
1817
|
+
distributedTracingMode: DistributedTracingModes.W3C
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
};
|
|
1821
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1822
|
+
this._ajax["_currentWindowHost"] = "www.example.com";
|
|
1823
|
+
|
|
1824
|
+
// Act
|
|
1825
|
+
var xhr = new XMLHttpRequest();
|
|
1826
|
+
var stub = this.sandbox.stub(xhr, "setRequestHeader");
|
|
1827
|
+
xhr.open("GET", "http://www.example.com");
|
|
1828
|
+
xhr.send();
|
|
1829
|
+
|
|
1830
|
+
// Assert that the AI header was not included
|
|
1831
|
+
Assert.equal(false, stub.calledWith(RequestHeaders.requestIdHeader)); // AI
|
|
1832
|
+
// Assert that the W3C header is included
|
|
1833
|
+
Assert.equal(true, stub.calledWith(RequestHeaders.traceParentHeader)); // W3C
|
|
1834
|
+
|
|
1835
|
+
// Emulate response
|
|
1836
|
+
(<any>xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*"}, "");
|
|
1837
|
+
}
|
|
1838
|
+
})
|
|
1839
|
+
|
|
1840
|
+
this.testCase({
|
|
1841
|
+
name: "Ajax: should create and only pass AI is enabled",
|
|
1842
|
+
test: () => {
|
|
1843
|
+
this._ajax = new AjaxMonitor();
|
|
1844
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1845
|
+
let coreConfig = {
|
|
1846
|
+
instrumentationKey: "instrumentationKey",
|
|
1847
|
+
extensionConfig: {
|
|
1848
|
+
"AjaxDependencyPlugin": {
|
|
1849
|
+
appId: "appId",
|
|
1850
|
+
distributedTracingMode: DistributedTracingModes.AI
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
};
|
|
1854
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1855
|
+
this._ajax["_currentWindowHost"] = "www.example.com";
|
|
1856
|
+
|
|
1857
|
+
// Act
|
|
1858
|
+
var xhr = new XMLHttpRequest();
|
|
1859
|
+
var stub = this.sandbox.stub(xhr, "setRequestHeader");
|
|
1860
|
+
xhr.open("GET", "http://www.example.com");
|
|
1861
|
+
xhr.send();
|
|
1862
|
+
|
|
1863
|
+
// Assert that the AI header was not included
|
|
1864
|
+
Assert.equal(true, stub.calledWith(RequestHeaders.requestIdHeader)); // AI
|
|
1865
|
+
// Assert that the W3C header is included
|
|
1866
|
+
Assert.equal(false, stub.calledWith(RequestHeaders.traceParentHeader)); // W3C
|
|
1867
|
+
|
|
1868
|
+
// Emulate response
|
|
1869
|
+
(<any>xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*"}, "");
|
|
1870
|
+
}
|
|
1871
|
+
})
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
private testAjaxSuccess(responseCode: number, success: boolean) {
|
|
1875
|
+
this._ajax = new AjaxMonitor();
|
|
1876
|
+
let appInsightsCore = new AppInsightsCore();
|
|
1877
|
+
let coreConfig = { instrumentationKey: "instrumentationKey", extensionConfig: {"AjaxPlugin": {}}};
|
|
1878
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
1879
|
+
var trackStub = this.sandbox.stub(appInsightsCore, "track");
|
|
1880
|
+
|
|
1881
|
+
// Act
|
|
1882
|
+
var xhr = new XMLHttpRequest();
|
|
1883
|
+
xhr.open("GET", "example.com/bla");
|
|
1884
|
+
xhr.send();
|
|
1885
|
+
|
|
1886
|
+
// Emulate response
|
|
1887
|
+
(<any>xhr).respond(responseCode, {}, "");
|
|
1888
|
+
|
|
1889
|
+
// Assert
|
|
1890
|
+
let data = trackStub.args[0][0].baseData;
|
|
1891
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
1892
|
+
Assert.equal(success, data.success, "TrackAjax should receive " + success + " as a 'success' argument");
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
private _checkFetchTraceId(evtData:any, message:string) {
|
|
1896
|
+
Assert.notEqual(undefined, evtData, message + " - Must have track data");
|
|
1897
|
+
if (evtData) {
|
|
1898
|
+
let data = evtData.baseData;
|
|
1899
|
+
Assert.equal("Fetch", data.type, message + " - request is Fatch type");
|
|
1900
|
+
var id = data.id;
|
|
1901
|
+
Assert.equal("|", id[0], message + " - check id starts with |");
|
|
1902
|
+
Assert.equal(".", id[id.length - 1], message + " - check id ends with .");
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
export class AjaxPerfTrackTests extends AITestClass {
|
|
1908
|
+
|
|
1909
|
+
private _fetch:any;
|
|
1910
|
+
private _ajax:AjaxMonitor;
|
|
1911
|
+
private _initialPerformance: Performance;
|
|
1912
|
+
private _perfEntries: PerformanceEntry[];
|
|
1913
|
+
private _context:any;
|
|
1914
|
+
|
|
1915
|
+
constructor(name?: string) {
|
|
1916
|
+
super(name);
|
|
1917
|
+
|
|
1918
|
+
this.useFakeServer = false;
|
|
1919
|
+
this._perfEntries = [];
|
|
1920
|
+
this._context = {};
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
public addPerfEntry(entry:any) {
|
|
1924
|
+
this._perfEntries.push(entry);
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
public testInitialize() {
|
|
1928
|
+
this._context = {};
|
|
1929
|
+
this._fetch = getGlobalInst("fetch");
|
|
1930
|
+
|
|
1931
|
+
let performance = getPerformance();
|
|
1932
|
+
if (performance && performance.clearResourceTimings) {
|
|
1933
|
+
// Make sure we don't pick up some elses value
|
|
1934
|
+
performance.clearResourceTimings();
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
let testThis = this;
|
|
1938
|
+
testThis._initialPerformance = performance;
|
|
1939
|
+
|
|
1940
|
+
testThis._perfEntries = [];
|
|
1941
|
+
|
|
1942
|
+
// Add polyfil / mock
|
|
1943
|
+
(<any>window).performance = {
|
|
1944
|
+
_current: 0,
|
|
1945
|
+
_tick: function (ms: number) {
|
|
1946
|
+
this._current += ms;
|
|
1947
|
+
},
|
|
1948
|
+
now: function() {
|
|
1949
|
+
return this._current;
|
|
1950
|
+
},
|
|
1951
|
+
getEntries: function() {
|
|
1952
|
+
return testThis._perfEntries;
|
|
1953
|
+
},
|
|
1954
|
+
getEntriesByName: function (name) {
|
|
1955
|
+
let result = [];
|
|
1956
|
+
testThis._perfEntries.forEach((entry) => {
|
|
1957
|
+
if (entry.name === name) {
|
|
1958
|
+
result.push(entry);
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
return result;
|
|
1962
|
+
},
|
|
1963
|
+
mark: function (name) {
|
|
1964
|
+
testThis.addPerfEntry({
|
|
1965
|
+
entryType: "mark",
|
|
1966
|
+
name: name,
|
|
1967
|
+
startTime: this._current,
|
|
1968
|
+
duration: 0
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
public testCleanup() {
|
|
1975
|
+
this._context = {};
|
|
1976
|
+
if (this._ajax) {
|
|
1977
|
+
this._ajax.teardown();
|
|
1978
|
+
this._ajax = null;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
if (this._initialPerformance) {
|
|
1982
|
+
(<any>window).performance = this._initialPerformance;
|
|
1983
|
+
this._initialPerformance = null;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
// Restore the real fetch
|
|
1987
|
+
window.fetch = this._fetch;
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
public registerTests() {
|
|
1991
|
+
|
|
1992
|
+
this.testCaseAsync({
|
|
1993
|
+
name: "AjaxPerf: check that performance tracking is disabled for xhr requests by default",
|
|
1994
|
+
stepDelay: 10,
|
|
1995
|
+
steps: [ () => {
|
|
1996
|
+
let performance = getPerformance();
|
|
1997
|
+
let markSpy = this.sandbox.spy(performance, "mark");
|
|
1998
|
+
|
|
1999
|
+
this._ajax = new AjaxMonitor();
|
|
2000
|
+
let appInsightsCore = new AppInsightsCore();
|
|
2001
|
+
let coreConfig = {
|
|
2002
|
+
instrumentationKey: "instrumentationKey",
|
|
2003
|
+
extensionConfig: {
|
|
2004
|
+
"AjaxDependencyPlugin": {
|
|
2005
|
+
appId: "appId"
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
};
|
|
2009
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
2010
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
2011
|
+
|
|
2012
|
+
// Used to "wait" for App Insights to finish initializing which should complete after the XHR request
|
|
2013
|
+
this._context["trackStub"] = this.sandbox.stub(appInsightsCore, "track");
|
|
2014
|
+
|
|
2015
|
+
|
|
2016
|
+
// Act
|
|
2017
|
+
var xhr = new XMLHttpRequest();
|
|
2018
|
+
|
|
2019
|
+
// trigger the request that should cause a track event once the xhr request is complete
|
|
2020
|
+
xhr.open("GET", "https://httpbin.org/status/200");
|
|
2021
|
+
xhr.send();
|
|
2022
|
+
Assert.equal(false, markSpy.called, "The code should not have called mark()");
|
|
2023
|
+
}]
|
|
2024
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
2025
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
2026
|
+
if (trackStub.called) {
|
|
2027
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
2028
|
+
let data = trackStub.args[0][0].baseData;
|
|
2029
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
2030
|
+
let props = data.properties || {};
|
|
2031
|
+
Assert.equal(undefined, props.ajaxPerf, "Should contain properties perf object");
|
|
2032
|
+
return true;
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
return false;
|
|
2036
|
+
}, 'response received', 600, 1000) as any)
|
|
2037
|
+
});
|
|
2038
|
+
|
|
2039
|
+
this.testCaseAsync({
|
|
2040
|
+
name: "AjaxPerf: check that performance tracking is included when enabled for xhr requests",
|
|
2041
|
+
stepDelay: 10,
|
|
2042
|
+
steps: [ (testContext) => {
|
|
2043
|
+
let performance = getPerformance();
|
|
2044
|
+
let markSpy = this.sandbox.spy(performance, "mark");
|
|
2045
|
+
|
|
2046
|
+
this._ajax = new AjaxMonitor();
|
|
2047
|
+
let appInsightsCore = new AppInsightsCore();
|
|
2048
|
+
let coreConfig = {
|
|
2049
|
+
instrumentationKey: "instrumentationKey",
|
|
2050
|
+
extensionConfig: {
|
|
2051
|
+
"AjaxDependencyPlugin": {
|
|
2052
|
+
appId: "appId",
|
|
2053
|
+
enableAjaxPerfTracking: true
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
};
|
|
2057
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
2058
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
2059
|
+
|
|
2060
|
+
// Used to "wait" for App Insights to finish initializing which should complete after the XHR request
|
|
2061
|
+
this._context["trackStub"] = this.sandbox.stub(appInsightsCore, "track");
|
|
2062
|
+
|
|
2063
|
+
// Act
|
|
2064
|
+
var xhr = new XMLHttpRequest();
|
|
2065
|
+
|
|
2066
|
+
// trigger the request that should cause a track event once the xhr request is complete
|
|
2067
|
+
xhr.open("GET", "https://httpbin.org/status/200");
|
|
2068
|
+
xhr.send();
|
|
2069
|
+
Assert.equal(true, markSpy.called, "The code should have called been mark()");
|
|
2070
|
+
this.addPerfEntry({
|
|
2071
|
+
entryType: "resource",
|
|
2072
|
+
initiatorType: "xmlhttprequest",
|
|
2073
|
+
name: "https://httpbin.org/status/200",
|
|
2074
|
+
startTime: getPerformance().now(),
|
|
2075
|
+
duration: 10
|
|
2076
|
+
});
|
|
2077
|
+
}]
|
|
2078
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
2079
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
2080
|
+
if (trackStub.called) {
|
|
2081
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
2082
|
+
let data = trackStub.args[0][0].baseData;
|
|
2083
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
2084
|
+
let props = data.properties;
|
|
2085
|
+
Assert.notEqual(undefined, props, "Should contain properties");
|
|
2086
|
+
if (props) {
|
|
2087
|
+
let perf = props.ajaxPerf || {};
|
|
2088
|
+
Assert.equal(10, perf.duration, "Duration exists - " + JSON.stringify(data));
|
|
2089
|
+
}
|
|
2090
|
+
return true;
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
return false;
|
|
2094
|
+
}, 'response received', 600, 1000) as any)
|
|
2095
|
+
});
|
|
2096
|
+
|
|
2097
|
+
this.testCaseAsync({
|
|
2098
|
+
name: "AjaxPerf: check that performance tracking is included when enabled for xhr requests with server timing",
|
|
2099
|
+
stepDelay: 10,
|
|
2100
|
+
steps: [ (testContext) => {
|
|
2101
|
+
let performance = getPerformance();
|
|
2102
|
+
let markSpy = this.sandbox.spy(performance, "mark");
|
|
2103
|
+
|
|
2104
|
+
this._ajax = new AjaxMonitor();
|
|
2105
|
+
let appInsightsCore = new AppInsightsCore();
|
|
2106
|
+
let coreConfig = {
|
|
2107
|
+
instrumentationKey: "instrumentationKey",
|
|
2108
|
+
extensionConfig: {
|
|
2109
|
+
"AjaxDependencyPlugin": {
|
|
2110
|
+
appId: "appId",
|
|
2111
|
+
enableAjaxPerfTracking: true
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
};
|
|
2115
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
2116
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
2117
|
+
|
|
2118
|
+
// Used to "wait" for App Insights to finish initializing which should complete after the XHR request
|
|
2119
|
+
this._context["trackStub"] = this.sandbox.stub(appInsightsCore, "track");
|
|
2120
|
+
|
|
2121
|
+
// Act
|
|
2122
|
+
var xhr = new XMLHttpRequest();
|
|
2123
|
+
|
|
2124
|
+
// trigger the request that should cause a track event once the xhr request is complete
|
|
2125
|
+
xhr.open("GET", "https://httpbin.org/status/200");
|
|
2126
|
+
xhr.send();
|
|
2127
|
+
Assert.equal(true, markSpy.called, "The code should have called been mark()");
|
|
2128
|
+
this.addPerfEntry({
|
|
2129
|
+
entryType: "resource",
|
|
2130
|
+
initiatorType: "xmlhttprequest",
|
|
2131
|
+
name: "https://httpbin.org/status/200",
|
|
2132
|
+
startTime: getPerformance().now(),
|
|
2133
|
+
duration: 10,
|
|
2134
|
+
serverTiming: [
|
|
2135
|
+
{ name: "cache", duration: 23.2, description: "Cache Read"},
|
|
2136
|
+
{ name: "db", duration: 53, description: ""},
|
|
2137
|
+
{ name: "app", duration: 47.2, description: ""},
|
|
2138
|
+
{ name: "dup", description: "dup1"},
|
|
2139
|
+
{ name: "dup", description: "dup2"},
|
|
2140
|
+
]
|
|
2141
|
+
});
|
|
2142
|
+
}]
|
|
2143
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
2144
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
2145
|
+
if (trackStub.called) {
|
|
2146
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
2147
|
+
let data = trackStub.args[0][0].baseData;
|
|
2148
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
2149
|
+
let props = data.properties;
|
|
2150
|
+
Assert.notEqual(undefined, props, "Should contain properties");
|
|
2151
|
+
if (props) {
|
|
2152
|
+
let perf = props.ajaxPerf || {};
|
|
2153
|
+
Assert.equal(10, perf.duration, "Duration exists - " + JSON.stringify(data));
|
|
2154
|
+
Assert.equal(23.2, perf.serverTiming.cache.duration, "Check that the server timing was set")
|
|
2155
|
+
Assert.equal("Cache Read", perf.serverTiming.cache.description, "Check that the server timing was set")
|
|
2156
|
+
Assert.equal("dup1;dup2", perf.serverTiming.dup.description, "Check that the server timing was set")
|
|
2157
|
+
}
|
|
2158
|
+
return true;
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
return false;
|
|
2162
|
+
}, 'response received', 600, 1000) as any)
|
|
2163
|
+
});
|
|
2164
|
+
|
|
2165
|
+
this.testCaseAsync({
|
|
2166
|
+
name: "AjaxPerf: check that performance tracking is reported, even if the entry is missing when enabled for xhr requests",
|
|
2167
|
+
stepDelay: 10,
|
|
2168
|
+
steps: [ (testContext) => {
|
|
2169
|
+
let performance = getPerformance();
|
|
2170
|
+
let markSpy = this.sandbox.spy(performance, "mark");
|
|
2171
|
+
|
|
2172
|
+
this._ajax = new AjaxMonitor();
|
|
2173
|
+
let appInsightsCore = new AppInsightsCore();
|
|
2174
|
+
let coreConfig = {
|
|
2175
|
+
instrumentationKey: "instrumentationKey",
|
|
2176
|
+
extensionConfig: {
|
|
2177
|
+
"AjaxDependencyPlugin": {
|
|
2178
|
+
appId: "appId",
|
|
2179
|
+
enableAjaxPerfTracking: true
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
};
|
|
2183
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
2184
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
2185
|
+
|
|
2186
|
+
// Used to "wait" for App Insights to finish initializing which should complete after the XHR request
|
|
2187
|
+
this._context["trackStub"] = this.sandbox.stub(appInsightsCore, "track");
|
|
2188
|
+
|
|
2189
|
+
// Act
|
|
2190
|
+
var xhr = new XMLHttpRequest();
|
|
2191
|
+
|
|
2192
|
+
// trigger the request that should cause a track event once the xhr request is complete
|
|
2193
|
+
xhr.open("GET", "https://httpbin.org/status/200");
|
|
2194
|
+
xhr.send();
|
|
2195
|
+
Assert.equal(true, markSpy.called, "The code should have called been mark()");
|
|
2196
|
+
}]
|
|
2197
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
2198
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
2199
|
+
if (trackStub.called) {
|
|
2200
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
2201
|
+
let data = trackStub.args[0][0].baseData;
|
|
2202
|
+
Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
2203
|
+
let props = data.properties;
|
|
2204
|
+
Assert.notEqual(undefined, props, "Should contain properties");
|
|
2205
|
+
if (props) {
|
|
2206
|
+
let perf = props.ajaxPerf || {};
|
|
2207
|
+
Assert.equal(true, !!perf.missing, "Performance was executed but browser did not populate the window.performance entries - " + JSON.stringify(data));
|
|
2208
|
+
}
|
|
2209
|
+
return true;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
return false;
|
|
2213
|
+
}, 'response received', 60, 1000) as any)
|
|
2214
|
+
});
|
|
2215
|
+
|
|
2216
|
+
this.testCaseAsync({
|
|
2217
|
+
name: "AjaxPerf: check that performance tracking is disabled for fetch requests by default",
|
|
2218
|
+
stepDelay: 10,
|
|
2219
|
+
steps: [ (testContext) => {
|
|
2220
|
+
|
|
2221
|
+
hookFetch((resolve) => {
|
|
2222
|
+
AITestClass.orgSetTimeout(function() {
|
|
2223
|
+
resolve();
|
|
2224
|
+
}, 0);
|
|
2225
|
+
});
|
|
2226
|
+
|
|
2227
|
+
let performance = getPerformance();
|
|
2228
|
+
let markSpy = this.sandbox.spy(performance, "mark");
|
|
2229
|
+
|
|
2230
|
+
this._ajax = new AjaxMonitor();
|
|
2231
|
+
let appInsightsCore = new AppInsightsCore();
|
|
2232
|
+
let coreConfig = {
|
|
2233
|
+
instrumentationKey: "instrumentationKey",
|
|
2234
|
+
extensionConfig: {
|
|
2235
|
+
"AjaxDependencyPlugin": {
|
|
2236
|
+
appId: "appId"
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
};
|
|
2240
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
2241
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
2242
|
+
|
|
2243
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
2244
|
+
this._context["trackStub"] = trackSpy;
|
|
2245
|
+
|
|
2246
|
+
// Send fetch request that should trigger a track event when complete
|
|
2247
|
+
Assert.ok(trackSpy.notCalled, "No fetch called yet");
|
|
2248
|
+
fetch("https://httpbin.org/status/200", {method: "post", }).then((value) => {
|
|
2249
|
+
this._context["fetchComplete"] = true;
|
|
2250
|
+
return value;
|
|
2251
|
+
});
|
|
2252
|
+
Assert.equal(false, markSpy.called, "The code should not have called been mark()");
|
|
2253
|
+
}]
|
|
2254
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
2255
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
2256
|
+
if (this._context["fetchComplete"]) {
|
|
2257
|
+
Assert.ok(trackStub.notCalled, "No fetch called yet");
|
|
2258
|
+
return true;
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
return false;
|
|
2262
|
+
}, 'response received', 60, 1000) as any)
|
|
2263
|
+
});
|
|
2264
|
+
|
|
2265
|
+
this.testCaseAsync({
|
|
2266
|
+
name: "AjaxPerf: check that performance tracking is included for fetch requests when enabled",
|
|
2267
|
+
stepDelay: 10,
|
|
2268
|
+
steps: [ (testContext) => {
|
|
2269
|
+
hookFetch((resolve) => {
|
|
2270
|
+
AITestClass.orgSetTimeout(function() {
|
|
2271
|
+
resolve({
|
|
2272
|
+
headers: new Headers(),
|
|
2273
|
+
ok: true,
|
|
2274
|
+
body: null,
|
|
2275
|
+
bodyUsed: false,
|
|
2276
|
+
redirected: false,
|
|
2277
|
+
status: 200,
|
|
2278
|
+
statusText: "Hello",
|
|
2279
|
+
trailer: null,
|
|
2280
|
+
type: "basic",
|
|
2281
|
+
url: "https://httpbin.org/status/200"
|
|
2282
|
+
});
|
|
2283
|
+
}, 0);
|
|
2284
|
+
});
|
|
2285
|
+
|
|
2286
|
+
let performance = getPerformance();
|
|
2287
|
+
let markSpy = this.sandbox.spy(performance, "mark");
|
|
2288
|
+
|
|
2289
|
+
this._ajax = new AjaxMonitor();
|
|
2290
|
+
let appInsightsCore = new AppInsightsCore();
|
|
2291
|
+
let coreConfig = {
|
|
2292
|
+
instrumentationKey: "instrumentationKey",
|
|
2293
|
+
extensionConfig: {
|
|
2294
|
+
"AjaxDependencyPlugin": {
|
|
2295
|
+
appId: "appId",
|
|
2296
|
+
disableFetchTracking: false,
|
|
2297
|
+
enableAjaxPerfTracking: true
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
};
|
|
2301
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
2302
|
+
this._ajax["_currentWindowHost"] = "www.example.com";
|
|
2303
|
+
|
|
2304
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
2305
|
+
this._context["trackStub"] = trackSpy;
|
|
2306
|
+
|
|
2307
|
+
// Send fetch request that should trigger a track event when complete
|
|
2308
|
+
Assert.ok(trackSpy.notCalled, "No fetch called yet");
|
|
2309
|
+
fetch("https://httpbin.org/status/200", {method: "post" });
|
|
2310
|
+
Assert.equal(true, markSpy.called, "The code should have called been mark()");
|
|
2311
|
+
}]
|
|
2312
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
2313
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
2314
|
+
if (trackStub.called) {
|
|
2315
|
+
window.console && window.console.warn("Performance Entries: " + window.performance.getEntries().length);
|
|
2316
|
+
|
|
2317
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
2318
|
+
let data = trackStub.args[0][0].baseData;
|
|
2319
|
+
Assert.equal("Fetch", data.type, "request is Fetch type");
|
|
2320
|
+
let props = data.properties;
|
|
2321
|
+
Assert.notEqual(undefined, props, "Should contain properties");
|
|
2322
|
+
if (props) {
|
|
2323
|
+
let perf = props.ajaxPerf || {};
|
|
2324
|
+
if (perf.missing) {
|
|
2325
|
+
Assert.equal(true, !!perf.missing, "Performance was executed but browser did not populate the window.performance entries - " + JSON.stringify(data));
|
|
2326
|
+
} else {
|
|
2327
|
+
Assert.notEqual(undefined, perf.duration, "Duration exists - " + JSON.stringify(data));
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
return true;
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
return false;
|
|
2334
|
+
}, 'response received', 30, 1000) as any)
|
|
2335
|
+
});
|
|
2336
|
+
|
|
2337
|
+
this.testCaseAsync({
|
|
2338
|
+
name: "AjaxPerf: check that performance tracking is included for fetch requests when enabled when the fetch has a delayed promise",
|
|
2339
|
+
stepDelay: 10,
|
|
2340
|
+
steps: [ (testContext) => {
|
|
2341
|
+
hookFetch((resolve) => {
|
|
2342
|
+
AITestClass.orgSetTimeout(function() {
|
|
2343
|
+
resolve({
|
|
2344
|
+
headers: new Headers(),
|
|
2345
|
+
ok: true,
|
|
2346
|
+
body: null,
|
|
2347
|
+
bodyUsed: false,
|
|
2348
|
+
redirected: false,
|
|
2349
|
+
status: 200,
|
|
2350
|
+
statusText: "Hello",
|
|
2351
|
+
trailer: null,
|
|
2352
|
+
type: "basic",
|
|
2353
|
+
url: "https://httpbin.org/status/200"
|
|
2354
|
+
});
|
|
2355
|
+
}, 500);
|
|
2356
|
+
});
|
|
2357
|
+
|
|
2358
|
+
let performance = getPerformance();
|
|
2359
|
+
let markSpy = this.sandbox.spy(performance, "mark");
|
|
2360
|
+
|
|
2361
|
+
this._ajax = new AjaxMonitor();
|
|
2362
|
+
let appInsightsCore = new AppInsightsCore();
|
|
2363
|
+
let coreConfig = {
|
|
2364
|
+
instrumentationKey: "instrumentationKey",
|
|
2365
|
+
extensionConfig: {
|
|
2366
|
+
"AjaxDependencyPlugin": {
|
|
2367
|
+
appId: "appId",
|
|
2368
|
+
disableFetchTracking: false,
|
|
2369
|
+
enableAjaxPerfTracking: true
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
};
|
|
2373
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
2374
|
+
this._ajax["_currentWindowHost"] = "www.example.com";
|
|
2375
|
+
|
|
2376
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
2377
|
+
this._context["trackStub"] = trackSpy;
|
|
2378
|
+
|
|
2379
|
+
// Send fetch request that should trigger a track event when complete
|
|
2380
|
+
Assert.ok(trackSpy.notCalled, "No fetch called yet");
|
|
2381
|
+
fetch("https://httpbin.org/status/200", { method: "post" });
|
|
2382
|
+
Assert.equal(true, markSpy.called, "The code should have called been mark()");
|
|
2383
|
+
}]
|
|
2384
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
2385
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
2386
|
+
if (trackStub.called) {
|
|
2387
|
+
window.console && window.console.warn("Performance Entries: " + window.performance.getEntries().length);
|
|
2388
|
+
|
|
2389
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
2390
|
+
let data = trackStub.args[0][0].baseData;
|
|
2391
|
+
Assert.equal("Fetch", data.type, "request is Fetch type");
|
|
2392
|
+
let props = data.properties;
|
|
2393
|
+
Assert.notEqual(undefined, props, "Should contain properties");
|
|
2394
|
+
if (props) {
|
|
2395
|
+
Assert.notEqual(undefined, props.ajaxPerf, "Perf detail exists")
|
|
2396
|
+
let perf = props.ajaxPerf || {};
|
|
2397
|
+
if (perf.missing) {
|
|
2398
|
+
Assert.equal(true, !!perf.missing, "Performance was executed but browser did not populate the window.performance entries - " + JSON.stringify(data));
|
|
2399
|
+
} else {
|
|
2400
|
+
Assert.notEqual(undefined, perf.duration, "Duration exists - " + JSON.stringify(data));
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
return true;
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
return false;
|
|
2407
|
+
}, 'response received', 600, 1000) as any)
|
|
2408
|
+
});
|
|
2409
|
+
|
|
2410
|
+
this.testCaseAsync({
|
|
2411
|
+
name: "Fetch: should not create and pass correlation header if correlationHeaderExcludePatterns set to exclude all.",
|
|
2412
|
+
stepDelay: 10,
|
|
2413
|
+
timeOut: 10000,
|
|
2414
|
+
steps: [ (testContext) => {
|
|
2415
|
+
let fetchCalls = hookFetch((resolve) => {
|
|
2416
|
+
AITestClass.orgSetTimeout(function() {
|
|
2417
|
+
resolve({
|
|
2418
|
+
headers: new Headers(),
|
|
2419
|
+
ok: true,
|
|
2420
|
+
body: null,
|
|
2421
|
+
bodyUsed: false,
|
|
2422
|
+
redirected: false,
|
|
2423
|
+
status: 200,
|
|
2424
|
+
statusText: "Hello",
|
|
2425
|
+
trailer: null,
|
|
2426
|
+
type: "basic",
|
|
2427
|
+
url: "https://httpbin.org/status/200"
|
|
2428
|
+
});
|
|
2429
|
+
}, 0);
|
|
2430
|
+
});
|
|
2431
|
+
|
|
2432
|
+
this._ajax = new AjaxMonitor();
|
|
2433
|
+
let appInsightsCore = new AppInsightsCore();
|
|
2434
|
+
let coreConfig = {
|
|
2435
|
+
instrumentationKey: "instrumentationKey",
|
|
2436
|
+
disableFetchTracking: false,
|
|
2437
|
+
disableAjaxTracking: false,
|
|
2438
|
+
correlationHeaderExcludePatterns: [/.*/],
|
|
2439
|
+
extensionConfig: {
|
|
2440
|
+
"AjaxDependencyPlugin": {
|
|
2441
|
+
appId: "appId",
|
|
2442
|
+
distributedTracingMode: DistributedTracingModes.AI_AND_W3C
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
};
|
|
2446
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
2447
|
+
let trackSpy = this.sandbox.spy(appInsightsCore, "track")
|
|
2448
|
+
this._context["trackStub"] = trackSpy;
|
|
2449
|
+
|
|
2450
|
+
// Use test hook to simulate the correct url location
|
|
2451
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
2452
|
+
|
|
2453
|
+
// Setup
|
|
2454
|
+
let headers = new Headers();
|
|
2455
|
+
headers.append('My-Header', 'Header field');
|
|
2456
|
+
let init = {
|
|
2457
|
+
method: 'get',
|
|
2458
|
+
headers
|
|
2459
|
+
};
|
|
2460
|
+
const url = 'https://httpbin.org/status/200';
|
|
2461
|
+
|
|
2462
|
+
// Act
|
|
2463
|
+
Assert.ok(trackSpy.notCalled, "No fetch called yet");
|
|
2464
|
+
fetch(url, init).then(() => {
|
|
2465
|
+
// Assert
|
|
2466
|
+
Assert.ok(trackSpy.called, "The request was not tracked");
|
|
2467
|
+
Assert.equal(1, fetchCalls.length);
|
|
2468
|
+
Assert.notEqual(undefined, fetchCalls[0].init, "Has init param");
|
|
2469
|
+
let headers:Headers = fetchCalls[0].init.headers as Headers;
|
|
2470
|
+
Assert.equal(true, headers.has("My-Header"), "My-Header should be present");
|
|
2471
|
+
Assert.equal(false, headers.has(RequestHeaders.requestIdHeader), "Correlation header - AI header should be excluded"); // AI
|
|
2472
|
+
Assert.equal(false, headers.has(RequestHeaders.traceParentHeader), "Correlation header - W3c header should be excluded"); // W3C
|
|
2473
|
+
}, () => {
|
|
2474
|
+
Assert.ok(false, "fetch failed!");
|
|
2475
|
+
testContext.testDone();
|
|
2476
|
+
});
|
|
2477
|
+
}]
|
|
2478
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
2479
|
+
let trackStub = this._context["trackStub"] as SinonStub;
|
|
2480
|
+
if (trackStub.called) {
|
|
2481
|
+
Assert.ok(trackStub.calledOnce, "track is called");
|
|
2482
|
+
let data = trackStub.args[0][0].baseData;
|
|
2483
|
+
Assert.equal("Fetch", data.type, "request is Fatch type");
|
|
2484
|
+
var id = data.id;
|
|
2485
|
+
Assert.equal("|", id[0]);
|
|
2486
|
+
Assert.equal(".", id[id.length - 1]);
|
|
2487
|
+
return true;
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
return false;
|
|
2491
|
+
}, 'response received', 60, 1000) as any)
|
|
2492
|
+
})
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
export class AjaxFrozenTests extends AITestClass {
|
|
2497
|
+
|
|
2498
|
+
private _fetch:any;
|
|
2499
|
+
private _xmlHttpRequest:XMLHttpRequest;
|
|
2500
|
+
private _ajax:AjaxMonitor;
|
|
2501
|
+
private _context:any;
|
|
2502
|
+
|
|
2503
|
+
constructor(name?: string) {
|
|
2504
|
+
super(name);
|
|
2505
|
+
|
|
2506
|
+
this.useFakeServer = false;
|
|
2507
|
+
this._context = {};
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
public testInitialize() {
|
|
2511
|
+
this._context = {};
|
|
2512
|
+
this._fetch = getGlobalInst("fetch");
|
|
2513
|
+
this._xmlHttpRequest = getGlobalInst("XMLHttpRquest)");
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
public testCleanup() {
|
|
2517
|
+
this._context = {};
|
|
2518
|
+
if (this._ajax) {
|
|
2519
|
+
this._ajax.teardown();
|
|
2520
|
+
this._ajax = null;
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
// Restore the real fetch
|
|
2524
|
+
window.fetch = this._fetch;
|
|
2525
|
+
if (this._xmlHttpRequest) {
|
|
2526
|
+
getGlobal()["XMLHttpRequest"] = this._xmlHttpRequest;
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
public registerTests() {
|
|
2531
|
+
|
|
2532
|
+
this.testCaseAsync({
|
|
2533
|
+
name: "AjaxFrozenTests: check for prevent extensions",
|
|
2534
|
+
stepDelay: 10,
|
|
2535
|
+
steps: [ () => {
|
|
2536
|
+
Object.preventExtensions(XMLHttpRequest);
|
|
2537
|
+
Object.freeze(XMLHttpRequest);
|
|
2538
|
+
let reflect:any = getGlobalInst("Reflect");
|
|
2539
|
+
if (reflect) {
|
|
2540
|
+
reflect.preventExtensions(XMLHttpRequest);
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
this._ajax = new AjaxMonitor();
|
|
2544
|
+
let appInsightsCore = new AppInsightsCore();
|
|
2545
|
+
let coreConfig = {
|
|
2546
|
+
instrumentationKey: "instrumentationKey",
|
|
2547
|
+
extensionConfig: {
|
|
2548
|
+
"AjaxDependencyPlugin": {
|
|
2549
|
+
appId: "appId"
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
};
|
|
2553
|
+
appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
2554
|
+
this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
2555
|
+
|
|
2556
|
+
// Used to "wait" for App Insights to finish initializing which should complete after the XHR request
|
|
2557
|
+
this._context["trackStub"] = this.sandbox.stub(appInsightsCore, "track");
|
|
2558
|
+
this._context["throwSpy"] = this.sandbox.spy(appInsightsCore.logger, "throwInternal");
|
|
2559
|
+
|
|
2560
|
+
// Act
|
|
2561
|
+
var xhr = new XMLHttpRequest();
|
|
2562
|
+
|
|
2563
|
+
// Make sure the instance can't be changed
|
|
2564
|
+
Object.preventExtensions(xhr);
|
|
2565
|
+
Object.freeze(xhr);
|
|
2566
|
+
if (reflect) {
|
|
2567
|
+
reflect["preventExtensions"](xhr);
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
// trigger the request that should cause a track event once the xhr request is complete
|
|
2571
|
+
xhr.open("GET", "https://httpbin.org/status/200");
|
|
2572
|
+
xhr.send();
|
|
2573
|
+
}]
|
|
2574
|
+
.concat(PollingAssert.createPollingAssert(() => {
|
|
2575
|
+
let throwSpy = this._context["throwSpy"] as SinonStub;
|
|
2576
|
+
if (throwSpy.called) {
|
|
2577
|
+
Assert.ok(throwSpy.calledOnce, "track is called");
|
|
2578
|
+
let message = throwSpy.args[0][2];
|
|
2579
|
+
Assert.notEqual(-1, message.indexOf("Failed to monitor XMLHttpRequest"));
|
|
2580
|
+
let data = throwSpy.args[0][3];
|
|
2581
|
+
Assert.notEqual(-1, data.exception.indexOf("Cannot add property ajaxData"));
|
|
2582
|
+
return true;
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
return false;
|
|
2586
|
+
}, 'response received', 60, 1000) as any)
|
|
2587
|
+
});
|
|
2588
|
+
|
|
2589
|
+
// This is currently a manual test as we don't have hooks / mocks defined to automated this today
|
|
2590
|
+
// this.testCaseAsync({
|
|
2591
|
+
// name: "AjaxFrozenTests: check frozen prototype",
|
|
2592
|
+
// stepDelay: 10,
|
|
2593
|
+
// steps: [ () => {
|
|
2594
|
+
// Object.preventExtensions(XMLHttpRequest.prototype);
|
|
2595
|
+
// Object.freeze(XMLHttpRequest.prototype);
|
|
2596
|
+
// let reflect:any = getGlobalInst("Reflect");
|
|
2597
|
+
// if (reflect) {
|
|
2598
|
+
// reflect.preventExtensions(XMLHttpRequest.prototype);
|
|
2599
|
+
// }
|
|
2600
|
+
|
|
2601
|
+
// this._ajax = new AjaxMonitor();
|
|
2602
|
+
// let appInsightsCore = new AppInsightsCore();
|
|
2603
|
+
// let coreConfig = {
|
|
2604
|
+
// instrumentationKey: "instrumentationKey",
|
|
2605
|
+
// extensionConfig: {
|
|
2606
|
+
// "AjaxDependencyPlugin": {
|
|
2607
|
+
// appId: "appId"
|
|
2608
|
+
// }
|
|
2609
|
+
// }
|
|
2610
|
+
// };
|
|
2611
|
+
// let testThis = this;
|
|
2612
|
+
// appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]);
|
|
2613
|
+
// appInsightsCore.addNotificationListener({
|
|
2614
|
+
// eventsSent: (events: ITelemetryItem[]) => {
|
|
2615
|
+
// testThis._context["_eventsSent"] = events;
|
|
2616
|
+
// }
|
|
2617
|
+
// });
|
|
2618
|
+
// this._ajax["_currentWindowHost"] = "httpbin.org";
|
|
2619
|
+
|
|
2620
|
+
// // Used to "wait" for App Insights to finish initializing which should complete after the XHR request
|
|
2621
|
+
// this._context["trackStub"] = this.sandbox.stub(appInsightsCore, "track");
|
|
2622
|
+
|
|
2623
|
+
// // Act
|
|
2624
|
+
// var xhr = new XMLHttpRequest();
|
|
2625
|
+
|
|
2626
|
+
// // Make sure the instance can't be changed
|
|
2627
|
+
// Object.preventExtensions(xhr);
|
|
2628
|
+
// Object.freeze(xhr);
|
|
2629
|
+
// if (reflect) {
|
|
2630
|
+
// reflect["preventExtensions"](xhr);
|
|
2631
|
+
// }
|
|
2632
|
+
|
|
2633
|
+
// // trigger the request that should cause a track event once the xhr request is complete
|
|
2634
|
+
// xhr.open("GET", "https://httpbin.org/status/200");
|
|
2635
|
+
// xhr.send();
|
|
2636
|
+
// appInsightsCore.track({
|
|
2637
|
+
// name: "Hello World!"
|
|
2638
|
+
// });
|
|
2639
|
+
// }]
|
|
2640
|
+
// .concat(PollingAssert.createPollingAssert(() => {
|
|
2641
|
+
// let trackStub = this._context["trackStub"] as SinonStub;
|
|
2642
|
+
// if (trackStub.called) {
|
|
2643
|
+
// Assert.ok(trackStub.calledOnce, "track is called");
|
|
2644
|
+
// let data = trackStub.args[0][0].baseData;
|
|
2645
|
+
// Assert.equal("Ajax", data.type, "request is Ajax type");
|
|
2646
|
+
// let props = data.properties || {};
|
|
2647
|
+
// Assert.equal(undefined, props.ajaxPerf, "Should contain properties perf object");
|
|
2648
|
+
// return true;
|
|
2649
|
+
// }
|
|
2650
|
+
|
|
2651
|
+
// return false;
|
|
2652
|
+
// }, 'response received', 600, 1000) as any)
|
|
2653
|
+
// });
|
|
2654
|
+
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
class TestChannelPlugin implements IChannelControls {
|
|
2659
|
+
|
|
2660
|
+
public isFlushInvoked = false;
|
|
2661
|
+
public isUnloadInvoked = false;
|
|
2662
|
+
public isTearDownInvoked = false;
|
|
2663
|
+
public isResumeInvoked = false;
|
|
2664
|
+
public isPauseInvoked = false;
|
|
2665
|
+
|
|
2666
|
+
constructor() {
|
|
2667
|
+
this.processTelemetry = this._processTelemetry.bind(this);
|
|
2668
|
+
}
|
|
2669
|
+
public pause(): void {
|
|
2670
|
+
this.isPauseInvoked = true;
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
public resume(): void {
|
|
2674
|
+
this.isResumeInvoked = true;
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
public teardown(): void {
|
|
2678
|
+
this.isTearDownInvoked = true;
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
flush(async?: boolean, callBack?: () => void): void {
|
|
2682
|
+
this.isFlushInvoked = true;
|
|
2683
|
+
if (callBack) {
|
|
2684
|
+
callBack();
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
public processTelemetry;
|
|
2689
|
+
|
|
2690
|
+
public identifier = "Sender";
|
|
2691
|
+
|
|
2692
|
+
setNextPlugin(next: ITelemetryPlugin) {
|
|
2693
|
+
// no next setup
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
public priority: number = 1001;
|
|
2697
|
+
|
|
2698
|
+
public initialize = (config: IConfiguration) => {
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
private _processTelemetry(env: ITelemetryItem) {
|
|
2702
|
+
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
class TestAjaxMonitor extends AjaxMonitor {
|
|
2707
|
+
|
|
2708
|
+
}
|