From 222f2b067a93c38f65a62f48c1433390c909ff8e Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Tue, 19 Aug 2025 10:04:33 -0700 Subject: [PATCH 1/2] udpate --- .../Tests/Unit/src/ajax.tests.ts | 2072 +++++++++-------- 1 file changed, 1073 insertions(+), 999 deletions(-) diff --git a/extensions/applicationinsights-dependencies-js/Tests/Unit/src/ajax.tests.ts b/extensions/applicationinsights-dependencies-js/Tests/Unit/src/ajax.tests.ts index 78a5bb969..b21a4f9f1 100644 --- a/extensions/applicationinsights-dependencies-js/Tests/Unit/src/ajax.tests.ts +++ b/extensions/applicationinsights-dependencies-js/Tests/Unit/src/ajax.tests.ts @@ -265,11 +265,10 @@ export class AjaxTests extends AITestClass { } }); - this.testCaseAsync({ + this.testCase({ name: "Dependencies Configuration: init with cs promise ikey promise and default enableAjaxPerfTracking", - stepDelay: 100, useFakeTimers: true, - steps: [() => { + test: () => { this._ajax = new AjaxMonitor(); let csPromise = createAsyncResolvedPromise("testIkey"); let appInsightsCore = new AppInsightsCore(); @@ -289,33 +288,34 @@ export class AjaxTests extends AITestClass { this._context.trackStub = trackStub; this._context.throwSpy = throwSpy; - let xhr = new XMLHttpRequest(); - xhr.open("GET", "http://microsoft.com"); - xhr.setRequestHeader("Content-type", "application/json"); - xhr.send(); - // Emulate response - (xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*"}, ""); - Assert.ok((xhr)[AJAX_DATA_CONTAINER], "should have xhr hooks"); - - - - }].concat(PollingAssert.createPollingAssert(() => { - let core = this._context.core - let activeStatus = core.activeStatus && core.activeStatus(); - let trackStub = this._context.trackStub; - let throwSpy = this._context.throwSpy; - - if (activeStatus === ActiveStatus.ACTIVE) { - Assert.equal("testIkey", core.config.instrumentationKey, "ikey should be set"); - Assert.equal(1, trackStub.callCount, "Track should be called once"); - Assert.equal(false, throwSpy.called, "We should not have thrown an internal error test1"); - let data = trackStub.args[0][0].baseData; - Assert.equal(data.type, "Ajax", "request type should be ajax"); - Assert.ok(data.properties, "properties should be added"); - return true; - } - return false; - }, "Wait for promise response" + new Date().toISOString(), 60, 1000) as any) + return this._asyncQueue() + .add(() => { + let xhr = new XMLHttpRequest(); + xhr.open("GET", "http://microsoft.com"); + xhr.setRequestHeader("Content-type", "application/json"); + xhr.send(); + // Emulate response + (xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*"}, ""); + Assert.ok((xhr)[AJAX_DATA_CONTAINER], "should have xhr hooks"); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + let core = this._context.core + let activeStatus = core.activeStatus && core.activeStatus(); + let trackStub = this._context.trackStub; + let throwSpy = this._context.throwSpy; + + if (activeStatus === ActiveStatus.ACTIVE) { + Assert.equal("testIkey", core.config.instrumentationKey, "ikey should be set"); + Assert.equal(1, trackStub.callCount, "Track should be called once"); + Assert.equal(false, throwSpy.called, "We should not have thrown an internal error test1"); + let data = trackStub.args[0][0].baseData; + Assert.equal(data.type, "Ajax", "request type should be ajax"); + Assert.ok(data.properties, "properties should be added"); + return true; + } + return false; + }, "Wait for promise response" + new Date().toISOString(), 60, 1000)) + } }); this.testCase({ @@ -1040,46 +1040,45 @@ export class AjaxTests extends AITestClass { } }); - this.testCaseAsync({ + this.testCase({ name: "Fetch: fetch with disabled flag isn't tracked", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { - hookFetch((resolve) => { - AITestClass.orgSetTimeout(function() { - resolve({ - headers: new Headers(), - ok: true, - body: null, - bodyUsed: false, - redirected: false, - status: 200, - statusText: "Hello", - trailer: null, - type: "basic", - url: "https://httpbin.org/status/200" - }); - }, 0); - }); - + timeout: 10000, + test: () => { this._ajax = new AjaxMonitor(); let appInsightsCore = new AppInsightsCore(); let coreConfig = { instrumentationKey: "", disableFetchTracking: false }; appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); let fetchSpy = this.sandbox.spy(appInsightsCore, "track") - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: true}).then(() => { - // Assert - Assert.ok(fetchSpy.notCalled, "The request was not tracked"); - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + return this._asyncQueue() + .add(() => { + hookFetch((resolve) => { + AITestClass.orgSetTimeout(function() { + resolve({ + headers: new Headers(), + ok: true, + body: null, + bodyUsed: false, + redirected: false, + status: 200, + statusText: "Hello", + trailer: null, + type: "basic", + url: "https://httpbin.org/status/200" + }); + }, 0); + }); + + // Act + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: true}).then(() => { + // Assert + Assert.ok(fetchSpy.notCalled, "The request was not tracked"); + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + } }); const endpointUrls = [ @@ -1097,12 +1096,11 @@ export class AjaxTests extends AITestClass { arrForEach(endpointUrls, (endpointUrl) => { - this.testCaseAsync({ + this.testCase({ name: "Fetch: internal url fetch isn't tracked [" + endpointUrl + "]", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { + hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1119,32 +1117,32 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - + this._ajax = new AjaxMonitor(); let appInsightsCore = new AppInsightsCore(); let coreConfig = { instrumentationKey: "", disableFetchTracking: false }; appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); - let fetchSpy = this.sandbox.spy(appInsightsCore, "track") - - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch(endpointUrl, {method: "post" }).then(() => { - // Assert - Assert.ok(fetchSpy.notCalled, "The request was not tracked"); - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + let fetchSpy = this.sandbox.spy(appInsightsCore, "track"); + + return this._asyncQueue() + .add(() => { + // Act + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch(endpointUrl, {method: "post" }).then(() => { + // Assert + Assert.ok(fetchSpy.notCalled, "The request was not tracked"); + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + } }); - - this.testCaseAsync({ + + + this.testCase({ name: "Fetch: internal url using fetch is tracked [" + endpointUrl + "]", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1161,7 +1159,7 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - + this._ajax = new AjaxMonitor(); let appInsightsCore = new AppInsightsCore(); let coreConfig = { @@ -1171,27 +1169,27 @@ export class AjaxTests extends AITestClass { }; appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); let fetchSpy = this.sandbox.spy(appInsightsCore, "track") - - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch(endpointUrl, {method: "post" }).then(() => { - // Assert - Assert.ok(fetchSpy.called, "The request was tracked"); - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + + return this._asyncQueue() + .add(() => { + + // Act + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch(endpointUrl, {method: "post" }).then(() => { + // Assert + Assert.ok(fetchSpy.called, "The request was tracked"); + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + } }); }); - this.testCaseAsync({ + this.testCase({ 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", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1208,40 +1206,38 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - this._ajax = new AjaxMonitor(); let appInsightsCore = new AppInsightsCore(); let coreConfig = { instrumentationKey: "", disableFetchTracking: false }; appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); let fetchSpy = this.sandbox.spy(appInsightsCore, "track") - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: true}).then(() => { - // Assert - Assert.ok(fetchSpy.notCalled, "The initial request was not tracked"); - - fetch("https://httpbin.org/status/200", {method: "post" }).then(() => { - // Assert - Assert.ok(fetchSpy.notCalled, "The follow up request should also not have been tracked"); - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + return this._asyncQueue() + .add(() => { + // Act + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: true}).then(() => { + // Assert + Assert.ok(fetchSpy.notCalled, "The initial request was not tracked"); + + return fetch("https://httpbin.org/status/200", {method: "post" }).then(() => { + // Assert + Assert.ok(fetchSpy.notCalled, "The follow up request should also not have been tracked"); + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + } }); - this.testCaseAsync({ + this.testCase({ 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", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { + hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1258,7 +1254,6 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - this._ajax = new AjaxMonitor(); let appInsightsCore = new AppInsightsCore(); const ExcludeRequestRegex = ["bin"]; @@ -1266,33 +1261,34 @@ export class AjaxTests extends AITestClass { appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); let fetchSpy = this.sandbox.spy(appInsightsCore, "track") - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch("https://httpbin.org/status/200", {method: "post"}).then(() => { - // Assert - Assert.ok(fetchSpy.notCalled, "The initial request was not tracked"); - - fetch("https://httpbin.org/status/200", {method: "post" }).then(() => { - // Assert - Assert.ok(fetchSpy.notCalled, "The follow up request should also not have been tracked"); - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + return this._asyncQueue() + .add(() => { + + + // Act + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch("https://httpbin.org/status/200", {method: "post"}).then(() => { + // Assert + Assert.ok(fetchSpy.notCalled, "The initial request was not tracked"); + + return fetch("https://httpbin.org/status/200", {method: "post" }).then(() => { + // Assert + Assert.ok(fetchSpy.notCalled, "The follow up request should also not have been tracked"); + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + } }); - this.testCaseAsync({ + this.testCase({ name: "Fetch: add context into custom dimension with call back configuration on AI initialization.", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { + hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1309,12 +1305,11 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - this._ajax = new AjaxMonitor(); let dependencyFields = hookTrackDependencyInternal(this._ajax); let appInsightsCore = new AppInsightsCore(); - let coreConfig = { - instrumentationKey: "", + let coreConfig = { + instrumentationKey: "", disableFetchTracking: false, addRequestContext: (requestContext: IRequestContext) => { return { @@ -1327,32 +1322,31 @@ export class AjaxTests extends AITestClass { appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); let fetchSpy = this.sandbox.spy(appInsightsCore, "track") - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { - // assert - Assert.ok(fetchSpy.calledOnce, "track is called"); - let data = fetchSpy.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fetch type"); - Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); - Assert.equal("Fetch context", data.properties.test, "Fetch request's request context is added when customer configures addRequestContext."); - Assert.equal("https://httpbin.org/status/200", data.properties.fetchRequestUrl, "Fetch request is captured."); - Assert.equal("basic", data.properties.fetchResponseType, "Fetch response is captured."); - - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + return this._asyncQueue() + .add(() => { + + // Act + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { + // assert + Assert.ok(fetchSpy.calledOnce, "track is called"); + let data = fetchSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fetch type"); + Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); + Assert.equal("Fetch context", data.properties.test, "Fetch request's request context is added when customer configures addRequestContext."); + Assert.equal("https://httpbin.org/status/200", data.properties.fetchRequestUrl, "Fetch request is captured."); + Assert.equal("basic", data.properties.fetchResponseType, "Fetch response is captured."); + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + } }); - this.testCaseAsync({ + this.testCase({ name: "Fetch: fetch gets instrumented", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1369,7 +1363,6 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - this._ajax = new AjaxMonitor(); let dependencyFields = hookTrackDependencyInternal(this._ajax); let appInsightsCore = new AppInsightsCore(); @@ -1378,30 +1371,30 @@ export class AjaxTests extends AITestClass { let fetchSpy = this.sandbox.spy(appInsightsCore, "track") let throwSpy = this.sandbox.spy(appInsightsCore.logger, "throwInternal"); - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { - // Assert - Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); - let data = fetchSpy.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fetch type"); - Assert.ok(throwSpy.notCalled, "Make sure we didn't fail internally"); - Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); - Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called") - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + return this._asyncQueue() + .add(() => { + // Act + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { + // Assert + Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); + let data = fetchSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fetch type"); + Assert.ok(throwSpy.notCalled, "Make sure we didn't fail internally"); + Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); + Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called") + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + } }); - - this.testCaseAsync({ + + this.testCase({ name: "Fetch: Respond with status 0 and no status text", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { + hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1418,7 +1411,6 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - this._ajax = new AjaxMonitor(); let dependencyFields = hookTrackDependencyInternal(this._ajax); let appInsightsCore = new AppInsightsCore(); @@ -1427,32 +1419,40 @@ export class AjaxTests extends AITestClass { let fetchSpy = this.sandbox.spy(appInsightsCore, "track") let throwSpy = this.sandbox.spy(appInsightsCore.logger, "throwInternal"); - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { - // Assert - Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); - let data = fetchSpy.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fetch type"); - Assert.ok(throwSpy.notCalled, "Make sure we didn't fail internally"); - Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); - Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); - Assert.equal(0, dependencyFields[0].dependency.responseCode, "Check the response code"); - Assert.equal(undefined, dependencyFields[0].dependency.properties.responseText); - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + this._context.dependencyFields = dependencyFields; + this._context.fetchSpy = fetchSpy; + this._context.throwSpy = throwSpy; + + return this._asyncQueue() + .add(() => { + // Act + let fetchSpy = this._context.fetchSpy; + let throwSpy = this._context.throwSpy; + let dependencyFields = this._context.dependencyFields; + + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { + // Assert + Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); + let data = fetchSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fetch type"); + Assert.ok(throwSpy.notCalled, "Make sure we didn't fail internally"); + Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); + Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); + Assert.equal(0, dependencyFields[0].dependency.responseCode, "Check the response code"); + Assert.equal(undefined, dependencyFields[0].dependency.properties.responseText); + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + } }); - this.testCaseAsync({ + this.testCase({ name: "Fetch: Respond with status 0 and no status text", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { + hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1469,7 +1469,6 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - this._ajax = new AjaxMonitor(); let dependencyFields = hookTrackDependencyInternal(this._ajax); let appInsightsCore = new AppInsightsCore(); @@ -1478,32 +1477,42 @@ export class AjaxTests extends AITestClass { let fetchSpy = this.sandbox.spy(appInsightsCore, "track") let throwSpy = this.sandbox.spy(appInsightsCore.logger, "throwInternal"); - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { - // Assert - Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); - let data = fetchSpy.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fetch type"); - Assert.ok(throwSpy.notCalled, "Make sure we didn't fail internally"); - Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); - Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); - Assert.equal(0, dependencyFields[0].dependency.responseCode, "Check the response code"); - Assert.equal("Blocked", dependencyFields[0].dependency!.properties.responseText); - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + this._context.dependencyFields = dependencyFields; + this._context.fetchSpy = fetchSpy; + this._context.throwSpy = throwSpy; + + return this._asyncQueue() + .add(() => { + + // Act + let fetchSpy = this._context.fetchSpy; + let throwSpy = this._context.throwSpy; + let dependencyFields = this._context.dependencyFields; + + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { + // Assert + Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); + let data = fetchSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fetch type"); + Assert.ok(throwSpy.notCalled, "Make sure we didn't fail internally"); + Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); + Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); + Assert.equal(0, dependencyFields[0].dependency.responseCode, "Check the response code"); + Assert.equal("Blocked", dependencyFields[0].dependency!.properties.responseText); + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + } }); - this.testCaseAsync({ + + this.testCase({ name: "Fetch: fetch addDependencyInitializer adding context", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { + hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1536,32 +1545,37 @@ export class AjaxTests extends AITestClass { let fetchSpy = this.sandbox.spy(appInsightsCore, "track") let throwSpy = this.sandbox.spy(appInsightsCore.logger, "throwInternal"); - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { - // Assert - Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); - let data = fetchSpy.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fetch type"); - Assert.ok(throwSpy.notCalled, "Make sure we didn't fail internally"); - Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); - Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); - Assert.ok(initializerCalled, "Initializer was called"); - Assert.equal(true, data.properties.initializer.called, "The value set in the initializer was added"); - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + this._context.dependencyFields = dependencyFields; + this._context.fetchSpy = fetchSpy; + this._context.throwSpy = throwSpy; + this._context.initializerCalled = () => initializerCalled; + + return this._asyncQueue() + .add(() => { + + // Act + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { + // Assert + Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); + let data = fetchSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fetch type"); + Assert.ok(throwSpy.notCalled, "Make sure we didn't fail internally"); + Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); + Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); + Assert.ok(initializerCalled, "Initializer was called"); + Assert.equal(true, data.properties.initializer.called, "The value set in the initializer was added"); + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + } }); - this.testCaseAsync({ + this.testCase({ name: "Fetch: fetch addDependencyInitializer drops the event", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1578,7 +1592,6 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - let initializerCalled = false; this._ajax = new AjaxMonitor(); this._ajax.addDependencyInitializer((details) => { @@ -1593,26 +1606,28 @@ export class AjaxTests extends AITestClass { let fetchSpy = this.sandbox.spy(appInsightsCore, "track") let throwSpy = this.sandbox.spy(appInsightsCore.logger, "throwInternal"); - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { - // Assert - Assert.ok(initializerCalled, "Initializer was not called"); - Assert.ok(fetchSpy.notCalled, "track was not called"); - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + return this._asyncQueue() + .add(() => { + // Act + + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { + // Assert + Assert.ok(initializerCalled, "Initializer was not called"); + Assert.ok(fetchSpy.notCalled, "track was not called"); + + }, () => { + Assert.ok(false, "fetch failed!"); + + }); + }) + } }); - this.testCaseAsync({ + this.testCase({ name: "Fetch: instrumentation handles invalid / missing request or url", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1638,43 +1653,51 @@ export class AjaxTests extends AITestClass { let fetchSpy = this.sandbox.spy(appInsightsCore, "track") let throwSpy = this.sandbox.spy(appInsightsCore.logger, "throwInternal"); - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch(null, {method: "post", [DisabledPropertyName]: false}).then(() => { - // Assert - Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); - let data = fetchSpy.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fetch type"); - Assert.equal(false, throwSpy.called, "We should not have failed internally"); - Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); - Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); - Assert.equal(undefined, dependencyFields[0].sysProperties, "no system properties"); - - fetch(undefined, null).then(() => { - // Assert - Assert.ok(fetchSpy.calledTwice, "createFetchRecord called once after using fetch"); - Assert.equal(false, throwSpy.called, "We should still not have failed internally"); - Assert.equal(2, dependencyFields.length, "trackDependencyDataInternal was called"); - Assert.ok(dependencyFields[1].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); - Assert.equal(undefined, dependencyFields[1].sysProperties, "no system properties"); - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + this._context.dependencyFields = dependencyFields; + this._context.fetchSpy = fetchSpy; + this._context.throwSpy = throwSpy; + + return this._asyncQueue() + .add(() => { + + // Act + + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch(null, {method: "post", [DisabledPropertyName]: false}).then(() => { + // Assert + Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); + let data = fetchSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fetch type"); + Assert.equal(false, throwSpy.called, "We should not have failed internally"); + Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); + Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); + Assert.equal(undefined, dependencyFields[0].sysProperties, "no system properties"); + + return fetch(undefined, null).then(() => { + // Assert + Assert.ok(fetchSpy.calledTwice, "createFetchRecord called once after using fetch"); + Assert.equal(false, throwSpy.called, "We should still not have failed internally"); + Assert.equal(2, dependencyFields.length, "trackDependencyDataInternal was called"); + Assert.ok(dependencyFields[1].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); + Assert.equal(undefined, dependencyFields[1].sysProperties, "no system properties"); + + }, () => { + Assert.ok(false, "fetch failed!"); + + }); + }, () => { + Assert.ok(false, "fetch failed!"); + + }); + }) + } }); - this.testCaseAsync({ + this.testCase({ name: "Fetch: instrumentation handles invalid / missing request or url with traceId", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { + hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1691,7 +1714,6 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - this._ajax = new AjaxMonitor(); let dependencyFields = hookTrackDependencyInternal(this._ajax); let appInsightsCore = new AppInsightsCore(); @@ -1705,45 +1727,53 @@ export class AjaxTests extends AITestClass { traceCtx!.setTraceId(expectedTraceId); traceCtx!.setSpanId(expectedSpanId); - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch(null, {method: "post", [DisabledPropertyName]: false}).then(() => { - // Assert - Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); - let data = fetchSpy.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fetch type"); - Assert.equal(false, throwSpy.called, "We should not have failed internally"); - Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); - Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); - Assert.equal(expectedTraceId, dependencyFields[0].sysProperties!.trace.traceID, "system properties traceId"); - Assert.equal(expectedSpanId, dependencyFields[0].sysProperties!.trace.parentID, "system properties spanId"); - - fetch(undefined, null).then(() => { - // Assert - Assert.ok(fetchSpy.calledTwice, "createFetchRecord called once after using fetch"); - Assert.equal(false, throwSpy.called, "We should still not have failed internally"); - Assert.equal(2, dependencyFields.length, "trackDependencyDataInternal was called"); - Assert.ok(dependencyFields[1].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); - Assert.equal(expectedTraceId, dependencyFields[1].sysProperties!.trace.traceID, "system properties traceId"); - Assert.equal(expectedSpanId, dependencyFields[1].sysProperties!.trace.parentID, "system properties spanId"); - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + this._context.dependencyFields = dependencyFields; + this._context.fetchSpy = fetchSpy; + this._context.throwSpy = throwSpy; + this._context.expectedTraceId = expectedTraceId; + this._context.expectedSpanId = expectedSpanId; + + return this._asyncQueue() + .add(() => { + + // Act + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch(null, {method: "post", [DisabledPropertyName]: false}).then(() => { + // Assert + Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); + let data = fetchSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fetch type"); + Assert.equal(false, throwSpy.called, "We should not have failed internally"); + Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); + Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); + Assert.equal(expectedTraceId, dependencyFields[0].sysProperties!.trace.traceID, "system properties traceId"); + Assert.equal(expectedSpanId, dependencyFields[0].sysProperties!.trace.parentID, "system properties spanId"); + + return fetch(undefined, null).then(() => { + // Assert + Assert.ok(fetchSpy.calledTwice, "createFetchRecord called once after using fetch"); + Assert.equal(false, throwSpy.called, "We should still not have failed internally"); + Assert.equal(2, dependencyFields.length, "trackDependencyDataInternal was called"); + Assert.ok(dependencyFields[1].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); + Assert.equal(expectedTraceId, dependencyFields[1].sysProperties!.trace.traceID, "system properties traceId"); + Assert.equal(expectedSpanId, dependencyFields[1].sysProperties!.trace.parentID, "system properties spanId"); + + }, () => { + Assert.ok(false, "fetch failed!"); + + }); + }, () => { + Assert.ok(false, "fetch failed!"); + + }); + }) + } }); - this.testCaseAsync({ + this.testCase({ name: "Fetch: instrumentation handles empty string", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { let fetchCalls = hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1760,7 +1790,6 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - this._ajax = new AjaxMonitor(); let dependencyFields = hookTrackDependencyInternal(this._ajax); let appInsightsCore = new AppInsightsCore(); @@ -1769,38 +1798,44 @@ export class AjaxTests extends AITestClass { let fetchSpy = this.sandbox.spy(appInsightsCore, "track") let throwSpy = this.sandbox.spy(appInsightsCore.logger, "throwInternal"); - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch("", {method: "post", [DisabledPropertyName]: false}).then(() => { - // Assert - Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); - let data = fetchSpy.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fetch type"); - Assert.equal(false, throwSpy.called, "We should not have failed internally"); - Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); - Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); - Assert.equal(undefined, dependencyFields[0].sysProperties, "no system properties"); - Assert.equal(window.location.href.split("#")[0], dependencyFields[0].dependency.target, "Target is captured."); - - // Assert that the HTTP method was preserved - Assert.equal(1, fetchCalls.length); - Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); - Assert.equal("post", fetchCalls[0].init?.method, "Has post method"); - - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + this._context.dependencyFields = dependencyFields; + this._context.fetchSpy = fetchSpy; + this._context.throwSpy = throwSpy; + this._context.fetchCalls = fetchCalls; + + return this._asyncQueue() + .add(() => { + + // Act + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch("", {method: "post", [DisabledPropertyName]: false}).then(() => { + // Assert + Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); + let data = fetchSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fetch type"); + Assert.equal(false, throwSpy.called, "We should not have failed internally"); + Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); + Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); + Assert.equal(undefined, dependencyFields[0].sysProperties, "no system properties"); + Assert.equal(window.location.href.split("#")[0], dependencyFields[0].dependency.target, "Target is captured."); + + // Assert that the HTTP method was preserved + Assert.equal(1, fetchCalls.length); + Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); + Assert.equal("post", fetchCalls[0].init?.method, "Has post method"); + + }, () => { + Assert.ok(false, "fetch failed!"); + + }); + }) + } }); - this.testCaseAsync({ + this.testCase({ name: "Fetch: instrumentation handles empty string with traceId", - stepDelay: 10, - autoComplete: false, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { let fetchCalls = hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1817,7 +1852,6 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - this._ajax = new AjaxMonitor(); let dependencyFields = hookTrackDependencyInternal(this._ajax); let appInsightsCore = new AppInsightsCore(); @@ -1831,35 +1865,43 @@ export class AjaxTests extends AITestClass { traceCtx!.setTraceId(expectedTraceId); traceCtx!.setSpanId(expectedSpanId); - // Act - Assert.ok(fetchSpy.notCalled, "No fetch called yet"); - fetch("", {method: "post", [DisabledPropertyName]: false}).then(() => { - // Assert - Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); - let data = fetchSpy.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fetch type"); - Assert.equal(false, throwSpy.called, "We should not have failed internally"); - Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); - Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); - Assert.equal(expectedTraceId, dependencyFields[0].sysProperties!.trace.traceID, "system properties traceId"); - Assert.equal(expectedSpanId, dependencyFields[0].sysProperties!.trace.parentID, "system properties spanId"); - Assert.equal(window.location.href.split("#")[0], dependencyFields[0].dependency.target, "Target is captured."); - - // Assert that the HTTP method was preserved - Assert.equal(1, fetchCalls.length); - Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); - Assert.equal("post", fetchCalls[0].init?.method, "Has post method"); - let headers:Headers = fetchCalls[0].init.headers as Headers; - Assert.notEqual(undefined, headers, "has headers"); - Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header should be present"); // AI - Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C - - testContext.testDone(); - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] + this._context.dependencyFields = dependencyFields; + this._context.fetchSpy = fetchSpy; + this._context.throwSpy = throwSpy; + this._context.expectedTraceId = expectedTraceId; + this._context.expectedSpanId = expectedSpanId; + this._context.fetchCalls = fetchCalls; + + return this._asyncQueue() + .add(() => { + // Act + + Assert.ok(fetchSpy.notCalled, "No fetch called yet"); + return fetch("", {method: "post", [DisabledPropertyName]: false}).then(() => { + // Assert + Assert.ok(fetchSpy.calledOnce, "createFetchRecord called once after using fetch"); + let data = fetchSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fetch type"); + Assert.equal(false, throwSpy.called, "We should not have failed internally"); + Assert.equal(1, dependencyFields.length, "trackDependencyDataInternal was called"); + Assert.ok(dependencyFields[0].dependency.startTime, "startTime was specified before trackDependencyDataInternal was called"); + Assert.equal(expectedTraceId, dependencyFields[0].sysProperties!.trace.traceID, "system properties traceId"); + Assert.equal(expectedSpanId, dependencyFields[0].sysProperties!.trace.parentID, "system properties spanId"); + Assert.equal(window.location.href.split("#")[0], dependencyFields[0].dependency.target, "Target is captured."); + + // Assert that the HTTP method was preserved + Assert.equal(1, fetchCalls.length); + Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); + Assert.equal("post", fetchCalls[0].init?.method, "Has post method"); + let headers:Headers = fetchCalls[0].init.headers as Headers; + Assert.notEqual(undefined, headers, "has headers"); + Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header should be present"); // AI + Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + } }); @@ -1902,7 +1944,7 @@ export class AjaxTests extends AITestClass { Assert.ok(fetchSpy.calledOnce); Assert.ok(headerSpy.calledOnce); Assert.deepEqual(init, headerSpy.returnValue || headerSpy.returnValues[0]); - } catch (e) { + } catch (e) { console && console.warn("Exception: " + e); Assert.ok(false, e); } @@ -1954,11 +1996,10 @@ export class AjaxTests extends AITestClass { } }); - this.testCaseAsync({ + this.testCase({ name: "Fetch: should create and pass a traceparent header if ai and w3c is enabled with custom headers", - stepDelay: 10, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { let fetchCalls = hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1975,7 +2016,6 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - this._ajax = new AjaxMonitor(); let appInsightsCore = new AppInsightsCore(); let coreConfig = { @@ -1996,54 +2036,55 @@ export class AjaxTests extends AITestClass { // Use test hook to simulate the correct url location this._ajax["_currentWindowHost"] = "httpbin.org"; - // Setup - let headers = new Headers(); - headers.append('My-Header', 'Header field'); - let init = { - method: 'get', - headers: headers - }; - const url = 'https://httpbin.org/status/200'; - - // Act - Assert.ok(trackSpy.notCalled, "No fetch called yet"); - fetch(url, init).then(() => { - // Assert - Assert.ok(trackSpy.called, "The request was not tracked"); - // Assert that both headers are sent - Assert.equal(1, fetchCalls.length); - Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); - let headers:Headers = fetchCalls[0].init.headers as Headers; - Assert.notEqual(undefined, headers, "has headers"); - Assert.equal(true, headers.has("My-Header"), "My-Header should be present"); - Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI - Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (trackStub.called) { - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fatch type"); - var id = data.id; - Assert.equal("|", id[0]); - Assert.equal(".", id[id.length - 1]); - return true; - } - - return false; - }, 'response received', 60, 1000) as any) + return this._asyncQueue() + .add(() => { + + // Setup + + let headers = new Headers(); + headers.append('My-Header', 'Header field'); + let init = { + method: 'get', + headers: headers + }; + const url = 'https://httpbin.org/status/200'; + + // Act + Assert.ok(trackSpy.notCalled, "No fetch called yet"); + return fetch(url, init).then(() => { + // Assert + Assert.ok(trackSpy.called, "The request was not tracked"); + // Assert that both headers are sent + Assert.equal(1, fetchCalls.length); + Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); + let headers:Headers = fetchCalls[0].init.headers as Headers; + Assert.notEqual(undefined, headers, "has headers"); + Assert.equal(true, headers.has("My-Header"), "My-Header should be present"); + Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI + Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + if (trackSpy.called) { + Assert.ok(trackSpy.calledOnce, "track is called"); + let data = trackSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fatch type"); + var id = data.id; + Assert.equal("|", id[0]); + Assert.equal(".", id[id.length - 1]); + return true; + } + return false; + }, 'response received', 60, 1000)) + } }) - this.testCaseAsync({ + this.testCase({ name: "Fetch: should create and pass a traceparent header if ai and w3c is enabled with no init param", - stepDelay: 10, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { let fetchCalls = hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -2060,7 +2101,6 @@ export class AjaxTests extends AITestClass { }); }, 0); }); - this._ajax = new AjaxMonitor(); let appInsightsCore = new AppInsightsCore(); let coreConfig = { @@ -2080,48 +2120,48 @@ export class AjaxTests extends AITestClass { // Use test hook to simulate the correct url location this._ajax["_currentWindowHost"] = "httpbin.org"; - - // Setup + const url = 'https://httpbin.org/status/200'; - // Act - Assert.ok(trackSpy.notCalled, "No fetch called yet"); - fetch(url).then(() => { - // Assert - Assert.ok(trackSpy.called, "The request was not tracked"); - // Assert that both headers are sent - Assert.equal(1, fetchCalls.length); - Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); - let headers:Headers = fetchCalls[0].init.headers as Headers; - Assert.notEqual(undefined, headers, "has headers"); - Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI - Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (trackStub.called) { - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fatch type"); - var id = data.id; - Assert.equal("|", id[0]); - Assert.equal(".", id[id.length - 1]); - return true; - } + return this._asyncQueue() + .add(() => { + + // Act + Assert.ok(trackSpy.notCalled, "No fetch called yet"); + return fetch(url).then(() => { + // Assert + Assert.ok(trackSpy.called, "The request was not tracked"); + // Assert that both headers are sent + Assert.equal(1, fetchCalls.length); + Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); + let headers:Headers = fetchCalls[0].init.headers as Headers; + Assert.notEqual(undefined, headers, "has headers"); + Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI + Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + if (trackSpy.called) { + Assert.ok(trackSpy.calledOnce, "track is called"); + let data = trackSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fatch type"); + var id = data.id; + Assert.equal("|", id[0]); + Assert.equal(".", id[id.length - 1]); + return true; + } - return false; - }, 'response received', 60, 1000) as any) - }) + return false; + }, 'response received', 60, 1000)) + } + }); - this.testCaseAsync({ + this.testCase({ name: "Fetch: should create and pass a traceparent header if w3c only is enabled with custom headers", - stepDelay: 10, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { let fetchCalls = hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -2167,46 +2207,47 @@ export class AjaxTests extends AITestClass { headers: headers }; const url = 'https://httpbin.org/status/200'; + return this._asyncQueue() + .add(() => { + // Act + Assert.ok(trackSpy.notCalled, "No fetch called yet"); + fetch(url, init).then(() => { + // Assert + Assert.ok(trackSpy.called, "The request was not tracked"); + // Assert that both headers are sent + Assert.equal(1, fetchCalls.length); + Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); + let headers:Headers = fetchCalls[0].init.headers as Headers; + Assert.notEqual(undefined, headers, "has headers"); + Assert.equal(true, headers.has("My-Header"), "My-Header should be present"); + Assert.equal(false, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI + Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C + }, () => { + Assert.ok(false, "fetch failed!"); + + }); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + let trackStub = this._context["trackStub"] as SinonStub; + if (trackStub.called) { + Assert.ok(trackStub.calledOnce, "track is called"); + let data = trackStub.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fatch type"); + var id = data.id; + Assert.equal("|", id[0]); + Assert.equal(".", id[id.length - 1]); + return true; + } - // Act - Assert.ok(trackSpy.notCalled, "No fetch called yet"); - fetch(url, init).then(() => { - // Assert - Assert.ok(trackSpy.called, "The request was not tracked"); - // Assert that both headers are sent - Assert.equal(1, fetchCalls.length); - Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); - let headers:Headers = fetchCalls[0].init.headers as Headers; - Assert.notEqual(undefined, headers, "has headers"); - Assert.equal(true, headers.has("My-Header"), "My-Header should be present"); - Assert.equal(false, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI - Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (trackStub.called) { - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fatch type"); - var id = data.id; - Assert.equal("|", id[0]); - Assert.equal(".", id[id.length - 1]); - return true; - } - - return false; - }, 'response received', 60, 1000) as any) - }) + return false; + }, 'response received', 60, 1000)) + } + }); - this.testCaseAsync({ + this.testCase({ name: "Fetch: should create and pass a traceparent header if w3c only is enabled with no init param", - stepDelay: 10, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { let fetchCalls = hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -2240,6 +2281,7 @@ export class AjaxTests extends AITestClass { appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); let trackSpy = this.sandbox.spy(appInsightsCore, "track") this._context["trackStub"] = trackSpy; + this._context["fetchCalls"] = fetchCalls; // Use test hook to simulate the correct url location this._ajax["_currentWindowHost"] = "httpbin.org"; @@ -2247,44 +2289,47 @@ export class AjaxTests extends AITestClass { // Setup const url = 'https://httpbin.org/status/200'; - // Act - Assert.ok(trackSpy.notCalled, "No fetch called yet"); - fetch(url).then(() => { - // Assert - Assert.ok(trackSpy.called, "The request was not tracked"); - // Assert that both headers are sent - Assert.equal(1, fetchCalls.length); - Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); - let headers:Headers = fetchCalls[0].init.headers as Headers; - Assert.notEqual(undefined, headers, "has headers"); - Assert.equal(false, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI - Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (trackStub.called) { - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fatch type"); - var id = data.id; - Assert.equal("|", id[0]); - Assert.equal(".", id[id.length - 1]); - return true; - } + return this._asyncQueue() + .add(() => { + + + // Act + Assert.ok(trackSpy.notCalled, "No fetch called yet"); + fetch(url).then(() => { + // Assert + Assert.ok(trackSpy.called, "The request was not tracked"); + // Assert that both headers are sent + Assert.equal(1, fetchCalls.length); + Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); + let headers:Headers = fetchCalls[0].init.headers as Headers; + Assert.notEqual(undefined, headers, "has headers"); + Assert.equal(false, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI + Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C + }, () => { + Assert.ok(false, "fetch failed!"); + + }); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + if (trackSpy.called) { + Assert.ok(trackSpy.calledOnce, "track is called"); + let data = trackSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fatch type"); + var id = data.id; + Assert.equal("|", id[0]); + Assert.equal(".", id[id.length - 1]); + return true; + } - return false; - }, 'response received', 60, 1000) as any) + return false; + }, 'response received', 60, 1000)) + } }) - this.testCaseAsync({ + this.testCase({ name: "Fetch: should create and pass a request header if AI only is enabled with custom headers", - stepDelay: 10, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { let fetchCalls = hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -2318,6 +2363,7 @@ export class AjaxTests extends AITestClass { appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); let trackSpy = this.sandbox.spy(appInsightsCore, "track") this._context["trackStub"] = trackSpy; + this._context["fetchCalls"] = fetchCalls; // Use test hook to simulate the correct url location this._ajax["_currentWindowHost"] = "httpbin.org"; @@ -2331,44 +2377,45 @@ export class AjaxTests extends AITestClass { }; const url = 'https://httpbin.org/status/200'; - // Act - Assert.ok(trackSpy.notCalled, "No fetch called yet"); - fetch(url, init).then(() => { - // Assert - Assert.ok(trackSpy.called, "The request was not tracked"); - // Assert that both headers are sent - Assert.equal(1, fetchCalls.length); - Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); - let headers:Headers = fetchCalls[0].init.headers as Headers; - Assert.notEqual(undefined, headers, "has headers"); - Assert.equal(true, headers.has("My-Header"), "My-Header should be present"); - Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI - Assert.equal(false, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (trackStub.called) { - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fatch type"); - var id = data.id; - Assert.equal("|", id[0]); - return true; - } + return this._asyncQueue() + .add(() => { + // Act + Assert.ok(trackSpy.notCalled, "No fetch called yet"); + fetch(url, init).then(() => { + // Assert + Assert.ok(trackSpy.called, "The request was not tracked"); + // Assert that both headers are sent + Assert.equal(1, fetchCalls.length); + Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); + let headers:Headers = fetchCalls[0].init.headers as Headers; + Assert.notEqual(undefined, headers, "has headers"); + Assert.equal(true, headers.has("My-Header"), "My-Header should be present"); + Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI + Assert.equal(false, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C + }, () => { + Assert.ok(false, "fetch failed!"); + + }); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + if (trackSpy.called) { + Assert.ok(trackSpy.calledOnce, "track is called"); + let data = trackSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fatch type"); + var id = data.id; + Assert.equal("|", id[0]); + return true; + } - return false; - }, 'response received', 60, 1000) as any) + return false; + }, 'response received', 60, 1000)) + } }) - this.testCaseAsync({ + this.testCase({ name: "Fetch: should create and pass a request header if AI only is enabled with no init param", - stepDelay: 10, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { let fetchCalls = hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -2402,51 +2449,53 @@ export class AjaxTests extends AITestClass { appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); let trackSpy = this.sandbox.spy(appInsightsCore, "track") this._context["trackStub"] = trackSpy; + this._context["fetchCalls"] = fetchCalls; // Use test hook to simulate the correct url location this._ajax["_currentWindowHost"] = "httpbin.org"; - // Setup const url = 'https://httpbin.org/status/200'; - // Act - Assert.ok(trackSpy.notCalled, "No fetch called yet"); - fetch(url).then(() => { - // Assert - Assert.ok(trackSpy.called, "The request was not tracked"); - // Assert that both headers are sent - Assert.equal(1, fetchCalls.length); - Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); - let headers:Headers = fetchCalls[0].init.headers as Headers; - Assert.notEqual(undefined, headers, "has headers"); - Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI - Assert.equal(false, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (trackStub.called) { - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fatch type"); - var id = data.id; - Assert.equal("|", id[0]); - return true; - } + return this._asyncQueue() + .add(() => { + + // Act + Assert.ok(trackSpy.notCalled, "No fetch called yet"); + fetch(url).then(() => { + // Assert + Assert.ok(trackSpy.called, "The request was not tracked"); + // Assert that both headers are sent + Assert.equal(1, fetchCalls.length); + Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); + let headers:Headers = fetchCalls[0].init.headers as Headers; + Assert.notEqual(undefined, headers, "has headers"); + Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI + Assert.equal(false, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C + }, () => { + Assert.ok(false, "fetch failed!"); + + }); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + if (trackSpy.called) { + Assert.ok(trackSpy.calledOnce, "track is called"); + let data = trackSpy.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fatch type"); + var id = data.id; + Assert.equal("|", id[0]); + return true; + } - return false; - }, 'response received', 60, 1000) as any) + return false; + }, 'response received', 60, 1000)) + } }) - this.testCaseAsync({ + this.testCase({ name: "Fetch: should add request headers to all valid argument variants", - stepDelay: 10, - timeOut: 10000, + timeout: 10000, useFakeTimers: true, - steps: [ (testContext) => { + test: () => { this._context["fetchCalls"] = hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -2495,64 +2544,67 @@ export class AjaxTests extends AITestClass { }; const url = 'https://httpbin.org/status/200'; - Assert.ok(trackSpy.notCalled, "No fetch called yet"); - fetch(url); - fetch(url, {}); - fetch(url, { headers: {} }); - fetch(url, { headers: new Headers() }); - fetch(url, { headers }); - fetch(url, init); - fetch(new Request(url)); - fetch(new Request(url, {})); - fetch(new Request(url, { headers: {} })); - fetch(new Request(url, { headers: new Headers() })); - fetch(new Request(url, { headers })); - fetch(new Request(url, init)); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - let fetchCalls = this._context["fetchCalls"] as IFetchArgs[]; - Assert.ok(true, "Track: " + trackStub.args.length + " Fetch Calls: " + fetchCalls.length); - if (trackStub.called && trackStub.args.length === 12 && fetchCalls.length === 12) { - for (let lp = 0; lp < trackStub.args.length; lp++) { - let evtData = trackStub.args[lp][0]; - this._checkFetchTraceId(evtData, "Fetch " + lp); - let properties = evtData.baseData.properties || {}; - let trackHeaders = properties.requestHeaders || {}; - - Assert.notEqual(undefined, fetchCalls[lp].init, "Has init param"); - let headers:Headers = fetchCalls[lp].init.headers as Headers; - Assert.notEqual(undefined, headers, "has headers"); - switch (lp) { - case 4: - case 5: - case 10: - case 11: - // All headers should be added to the init (2nd param) as this overrides - // any headers on any request object - Assert.equal(true, headers.has("My-Header"), "My-Header should be present"); - Assert.equal(true, headers.has("Authorization"), "Authorization should be present"); - Assert.equal("Header field", trackHeaders["my-header"], "my-header present in outbound event"); - Assert.equal(undefined, trackHeaders["Authorization"],"Authorization header should be ignored") - break; - } + return this._asyncQueue() + .add(() => { + Assert.ok(trackSpy.notCalled, "No fetch called yet"); + fetch(url); + fetch(url, {}); + fetch(url, { headers: {} }); + fetch(url, { headers: new Headers() }); + fetch(url, { headers }); + fetch(url, init); + fetch(new Request(url)); + fetch(new Request(url, {})); + fetch(new Request(url, { headers: {} })); + fetch(new Request(url, { headers: new Headers() })); + fetch(new Request(url, { headers })); + fetch(new Request(url, init)); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + let trackStub = this._context["trackStub"] as SinonStub; + let fetchCalls = this._context["fetchCalls"] as IFetchArgs[]; + Assert.ok(true, "Track: " + trackStub.args.length + " Fetch Calls: " + fetchCalls.length); + if (trackStub.called && trackStub.args.length === 12 && fetchCalls.length === 12) { + for (let lp = 0; lp < trackStub.args.length; lp++) { + let evtData = trackStub.args[lp][0]; + this._checkFetchTraceId(evtData, "Fetch " + lp); + let properties = evtData.baseData.properties || {}; + let trackHeaders = properties.requestHeaders || {}; + + Assert.notEqual(undefined, fetchCalls[lp].init, "Has init param"); + let headers:Headers = fetchCalls[lp].init.headers as Headers; + Assert.notEqual(undefined, headers, "has headers"); + switch (lp) { + case 4: + case 5: + case 10: + case 11: + // All headers should be added to the init (2nd param) as this overrides + // any headers on any request object + Assert.equal(true, headers.has("My-Header"), "My-Header should be present"); + Assert.equal(true, headers.has("Authorization"), "Authorization should be present"); + Assert.equal("Header field", trackHeaders["my-header"], "my-header present in outbound event"); + Assert.equal(undefined, trackHeaders["Authorization"],"Authorization header should be ignored") + break; + } - Assert.equal(true, headers.has(RequestHeaders.requestContextHeader), "requestContext header shoud be present"); - Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI - Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C + Assert.equal(true, headers.has(RequestHeaders.requestContextHeader), "requestContext header shoud be present"); + Assert.equal(true, headers.has(RequestHeaders.requestIdHeader), "AI header shoud be present"); // AI + Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C - Assert.notEqual(undefined, trackHeaders[RequestHeaders.requestIdHeader], "RequestId present in outbound event"); - Assert.notEqual(undefined, trackHeaders[RequestHeaders.requestContextHeader], "RequestContext present in outbound event"); - Assert.notEqual(undefined, trackHeaders[RequestHeaders.traceParentHeader], "traceParent present in outbound event"); + Assert.notEqual(undefined, trackHeaders[RequestHeaders.requestIdHeader], "RequestId present in outbound event"); + Assert.notEqual(undefined, trackHeaders[RequestHeaders.requestContextHeader], "RequestContext present in outbound event"); + Assert.notEqual(undefined, trackHeaders[RequestHeaders.traceParentHeader], "traceParent present in outbound event"); - } + } - return true; - } + return true; + } - this.clock.tick(1000); - return false; - }, 'response received', 60, 1000) as any) + this.clock.tick(1000); + return false; + }, 'response received', 60, 1000)) + } }) this.testCase({ @@ -3306,10 +3358,9 @@ export class AjaxPerfTrackTests extends AITestClass { public registerTests() { - this.testCaseAsync({ + this.testCase({ name: "AjaxPerf: check that performance tracking is disabled for xhr requests by default", - stepDelay: 10, - steps: [ () => { + test: () => { let performance = getPerformance(); let markSpy = this.sandbox.spy(performance, "mark"); @@ -3329,34 +3380,34 @@ export class AjaxPerfTrackTests extends AITestClass { // Used to "wait" for App Insights to finish initializing which should complete after the XHR request this._context["trackStub"] = this.sandbox.stub(appInsightsCore, "track"); - - // Act - var xhr = new XMLHttpRequest(); - - // trigger the request that should cause a track event once the xhr request is complete - xhr.open("GET", "https://httpbin.org/status/200"); - xhr.send(); - Assert.equal(false, markSpy.called, "The code should not have called mark()"); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (trackStub.called) { - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Ajax", data.type, "request is Ajax type"); - let props = data.properties || {}; - Assert.equal(undefined, props.ajaxPerf, "Should contain properties perf object"); - return true; - } - - return false; - }, 'response received', 600, 1000) as any) + return this._asyncQueue() + .add(() => { + // Act + var xhr = new XMLHttpRequest(); + + // trigger the request that should cause a track event once the xhr request is complete + xhr.open("GET", "https://httpbin.org/status/200"); + xhr.send(); + Assert.equal(false, markSpy.called, "The code should not have called mark()"); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + let trackStub = this._context["trackStub"] as SinonStub; + if (trackStub.called) { + Assert.ok(trackStub.calledOnce, "track is called"); + let data = trackStub.args[0][0].baseData; + Assert.equal("Ajax", data.type, "request is Ajax type"); + let props = data.properties || {}; + Assert.equal(undefined, props.ajaxPerf, "Should contain properties perf object"); + return true; + } + return false; + }, 'response received', 600, 1000)) + } }); - this.testCaseAsync({ + this.testCase({ name: "AjaxPerf: check that performance tracking is included when enabled for xhr requests", - stepDelay: 10, - steps: [ (testContext) => { + test: () => { let performance = getPerformance(); let markSpy = this.sandbox.spy(performance, "mark"); @@ -3377,44 +3428,46 @@ export class AjaxPerfTrackTests extends AITestClass { // Used to "wait" for App Insights to finish initializing which should complete after the XHR request this._context["trackStub"] = this.sandbox.stub(appInsightsCore, "track"); - // Act - var xhr = new XMLHttpRequest(); - - // trigger the request that should cause a track event once the xhr request is complete - xhr.open("GET", "https://httpbin.org/status/200"); - xhr.send(); - Assert.equal(true, markSpy.called, "The code should have called been mark()"); - this.addPerfEntry({ - entryType: "resource", - initiatorType: "xmlhttprequest", - name: "https://httpbin.org/status/200", - startTime: getPerformance().now(), - duration: 10 - }); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (trackStub.called) { - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Ajax", data.type, "request is Ajax type"); - let props = data.properties; - Assert.notEqual(undefined, props, "Should contain properties"); - if (props) { - let perf = props.ajaxPerf || {}; - Assert.equal(10, perf.duration, "Duration exists - " + JSON.stringify(data)); - } - return true; - } + return this._asyncQueue() + .add(() => { + // Act + var xhr = new XMLHttpRequest(); + + // trigger the request that should cause a track event once the xhr request is complete + xhr.open("GET", "https://httpbin.org/status/200"); + xhr.send(); + Assert.equal(true, markSpy.called, "The code should have called been mark()"); + this.addPerfEntry({ + entryType: "resource", + initiatorType: "xmlhttprequest", + name: "https://httpbin.org/status/200", + startTime: getPerformance().now(), + duration: 10 + }); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + let trackStub = this._context["trackStub"] as SinonStub; + if (trackStub.called) { + Assert.ok(trackStub.calledOnce, "track is called"); + let data = trackStub.args[0][0].baseData; + Assert.equal("Ajax", data.type, "request is Ajax type"); + let props = data.properties; + Assert.notEqual(undefined, props, "Should contain properties"); + if (props) { + let perf = props.ajaxPerf || {}; + Assert.equal(10, perf.duration, "Duration exists - " + JSON.stringify(data)); + } + return true; + } - return false; - }, 'response received', 600, 1000) as any) + return false; + }, 'response received', 600, 1000)) + } }); - this.testCaseAsync({ + this.testCase({ name: "AjaxPerf: check that performance tracking is included when enabled for xhr requests with server timing", - stepDelay: 10, - steps: [ (testContext) => { + test: () => { let performance = getPerformance(); let markSpy = this.sandbox.spy(performance, "mark"); @@ -3435,54 +3488,56 @@ export class AjaxPerfTrackTests extends AITestClass { // Used to "wait" for App Insights to finish initializing which should complete after the XHR request this._context["trackStub"] = this.sandbox.stub(appInsightsCore, "track"); - // Act - var xhr = new XMLHttpRequest(); - - // trigger the request that should cause a track event once the xhr request is complete - xhr.open("GET", "https://httpbin.org/status/200"); - xhr.send(); - Assert.equal(true, markSpy.called, "The code should have called been mark()"); - this.addPerfEntry({ - entryType: "resource", - initiatorType: "xmlhttprequest", - name: "https://httpbin.org/status/200", - startTime: getPerformance().now(), - duration: 10, - serverTiming: [ - { name: "cache", duration: 23.2, description: "Cache Read"}, - { name: "db", duration: 53, description: ""}, - { name: "app", duration: 47.2, description: ""}, - { name: "dup", description: "dup1"}, - { name: "dup", description: "dup2"}, - ] - }); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (trackStub.called) { - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Ajax", data.type, "request is Ajax type"); - let props = data.properties; - Assert.notEqual(undefined, props, "Should contain properties"); - if (props) { - let perf = props.ajaxPerf || {}; - Assert.equal(10, perf.duration, "Duration exists - " + JSON.stringify(data)); - Assert.equal(23.2, perf.serverTiming.cache.duration, "Check that the server timing was set") - Assert.equal("Cache Read", perf.serverTiming.cache.description, "Check that the server timing was set") - Assert.equal("dup1;dup2", perf.serverTiming.dup.description, "Check that the server timing was set") - } - return true; - } + return this._asyncQueue() + .add(() => { + // Act + var xhr = new XMLHttpRequest(); + + // trigger the request that should cause a track event once the xhr request is complete + xhr.open("GET", "https://httpbin.org/status/200"); + xhr.send(); + Assert.equal(true, markSpy.called, "The code should have called been mark()"); + this.addPerfEntry({ + entryType: "resource", + initiatorType: "xmlhttprequest", + name: "https://httpbin.org/status/200", + startTime: getPerformance().now(), + duration: 10, + serverTiming: [ + { name: "cache", duration: 23.2, description: "Cache Read"}, + { name: "db", duration: 53, description: ""}, + { name: "app", duration: 47.2, description: ""}, + { name: "dup", description: "dup1"}, + { name: "dup", description: "dup2"}, + ] + }); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + let trackStub = this._context["trackStub"] as SinonStub; + if (trackStub.called) { + Assert.ok(trackStub.calledOnce, "track is called"); + let data = trackStub.args[0][0].baseData; + Assert.equal("Ajax", data.type, "request is Ajax type"); + let props = data.properties; + Assert.notEqual(undefined, props, "Should contain properties"); + if (props) { + let perf = props.ajaxPerf || {}; + Assert.equal(10, perf.duration, "Duration exists - " + JSON.stringify(data)); + Assert.equal(23.2, perf.serverTiming.cache.duration, "Check that the server timing was set") + Assert.equal("Cache Read", perf.serverTiming.cache.description, "Check that the server timing was set") + Assert.equal("dup1;dup2", perf.serverTiming.dup.description, "Check that the server timing was set") + } + return true; + } - return false; - }, 'response received', 600, 1000) as any) + return false; + }, 'response received', 600, 1000)) + } }); - this.testCaseAsync({ + this.testCase({ name: "AjaxPerf: check perf mark prefix is correctly set for multiple xhr requests", - stepDelay: 10, - steps: [ (testContext) => { + test: () => { let performance = getPerformance(); let markSpy = this.sandbox.spy(performance, "mark"); @@ -3501,34 +3556,39 @@ export class AjaxPerfTrackTests extends AITestClass { this._ajax["_currentWindowHost"] = "httpbin.org"; // Used to "wait" for App Insights to finish initializing which should complete after the XHR request this._context["trackStub"] = this.sandbox.stub(appInsightsCore, "track"); - // Act - var xhr = new XMLHttpRequest(); - // trigger the request that should cause a track event once the xhr request is complete - xhr.open("GET", "https://httpbin.org/status/200"); - xhr.send(); - - var xhr2 = new XMLHttpRequest(); - xhr2.open("GET", "https://httpbin.org/anything"); - xhr2.send(); - - Assert.equal(true, markSpy.called, "The code should have called been mark()"); - let spyDetails = markSpy.args; - let prefix1 = spyDetails[0][0]; - let prefix2 = spyDetails[1][0]; - Assert.equal(prefix1.indexOf("ajaxData"), 0, "Prefix1 should start with 'ajaxData'"); - Assert.equal(prefix2.indexOf("ajaxData"), 0, "Prefix2 should start with 'ajaxData'"); - - let ajaxCountOne = parseInt(prefix1.substring(prefix1.indexOf('#') + 1), 10); - let ajaxCountTwo = parseInt(prefix2.substring(prefix1.indexOf('#') + 1), 10); - Assert.equal(1, ajaxCountTwo-ajaxCountOne, "the count should increase by 1"); - }] + return this._asyncQueue() + .add(() => { + // Act + var xhr = new XMLHttpRequest(); + + // trigger the request that should cause a track event once the xhr request is complete + xhr.open("GET", "https://httpbin.org/status/200"); + xhr.send(); + + var xhr2 = new XMLHttpRequest(); + xhr2.open("GET", "https://httpbin.org/anything"); + xhr2.send(); + + Assert.equal(true, markSpy.called, "The code should have called been mark()"); + let spyDetails = markSpy.args; + let prefix1 = spyDetails[0][0]; + let prefix2 = spyDetails[1][0]; + Assert.equal(prefix1.indexOf("ajaxData"), 0, "Prefix1 should start with 'ajaxData'"); + Assert.equal(prefix2.indexOf("ajaxData"), 0, "Prefix2 should start with 'ajaxData'"); + + let ajaxCountOne = parseInt(prefix1.substring(prefix1.indexOf('#') + 1), 10); + let ajaxCountTwo = parseInt(prefix2.substring(prefix1.indexOf('#') + 1), 10); + Assert.equal(1, ajaxCountTwo-ajaxCountOne, "the count should increase by 1"); + }) + } }); - this.testCaseAsync({ + + this.testCase({ name: "AjaxPerf: check that performance tracking is reported, even if the entry is missing when enabled for xhr requests", - stepDelay: 10, - steps: [ (testContext) => { + + test: () => { let performance = getPerformance(); let markSpy = this.sandbox.spy(performance, "mark"); @@ -3549,39 +3609,41 @@ export class AjaxPerfTrackTests extends AITestClass { // Used to "wait" for App Insights to finish initializing which should complete after the XHR request this._context["trackStub"] = this.sandbox.stub(appInsightsCore, "track"); - // Act - var xhr = new XMLHttpRequest(); - - // trigger the request that should cause a track event once the xhr request is complete - xhr.open("GET", "https://httpbin.org/status/200"); - xhr.send(); - Assert.equal(true, markSpy.called, "The code should have called been mark()"); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (trackStub.called) { - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Ajax", data.type, "request is Ajax type"); - let props = data.properties; - Assert.notEqual(undefined, props, "Should contain properties"); - if (props) { - let perf = props.ajaxPerf || {}; - Assert.equal(true, !!perf.missing, "Performance was executed but browser did not populate the window.performance entries - " + JSON.stringify(data)); - } - return true; - } + return this._asyncQueue() + .add(() => { + // Act + var xhr = new XMLHttpRequest(); + + // trigger the request that should cause a track event once the xhr request is complete + xhr.open("GET", "https://httpbin.org/status/200"); + xhr.send(); + Assert.equal(true, markSpy.called, "The code should have called been mark()"); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + let trackStub = this._context["trackStub"] as SinonStub; + if (trackStub.called) { + Assert.ok(trackStub.calledOnce, "track is called"); + let data = trackStub.args[0][0].baseData; + Assert.equal("Ajax", data.type, "request is Ajax type"); + let props = data.properties; + Assert.notEqual(undefined, props, "Should contain properties"); + if (props) { + let perf = props.ajaxPerf || {}; + Assert.equal(true, !!perf.missing, "Performance was executed but browser did not populate the window.performance entries - " + JSON.stringify(data)); + } + return true; + } - return false; - }, 'response received', 60, 1000) as any) + return false; + }, 'response received', 60, 1000)) + } }); - this.testCaseAsync({ + this.testCase({ name: "AjaxPerf: check that performance tracking is disabled for fetch requests by default", - stepDelay: 10, - steps: [ (testContext) => { + test: () => { - hookFetch((resolve) => { + hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve(); }, 0); @@ -3603,37 +3665,39 @@ export class AjaxPerfTrackTests extends AITestClass { appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); this._ajax["_currentWindowHost"] = "httpbin.org"; - let trackSpy = this.sandbox.spy(appInsightsCore, "track") + let trackSpy = this.sandbox.spy(appInsightsCore, "track"); this._context["trackStub"] = trackSpy; + return this._asyncQueue() + .add(() => { + + // Send fetch request that should trigger a track event when complete + Assert.ok(trackSpy.notCalled, "No fetch called yet"); + fetch("https://httpbin.org/status/200", {method: "post", }).then((value) => { + this._context["fetchComplete"] = true; + return value; + }); + Assert.equal(false, markSpy.called, "The code should not have called been mark()"); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + let trackStub = this._context["trackStub"] as SinonStub; + if (this._context["fetchComplete"]) { + Assert.ok(trackStub.calledOnce, "track is called"); + let data = trackStub.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fetch type"); + let props = data.properties; + Assert.notEqual(undefined, props, "Should contain properties"); + Assert.equal(undefined, props.ajaxPerf, "No performance data should exist"); + return true; + } - // Send fetch request that should trigger a track event when complete - Assert.ok(trackSpy.notCalled, "No fetch called yet"); - fetch("https://httpbin.org/status/200", {method: "post", }).then((value) => { - this._context["fetchComplete"] = true; - return value; - }); - Assert.equal(false, markSpy.called, "The code should not have called been mark()"); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (this._context["fetchComplete"]) { - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fetch type"); - let props = data.properties; - Assert.notEqual(undefined, props, "Should contain properties"); - Assert.equal(undefined, props.ajaxPerf, "No performance data should exist"); - return true; - } - - return false; - }, 'response received', 60, 1000) as any) + return false; + }, 'response received', 60, 1000)) + } }); - this.testCaseAsync({ + this.testCase({ name: "AjaxPerf: check that performance tracking is included for fetch requests when enabled", - stepDelay: 10, - steps: [ (testContext) => { + test: () => { hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -3669,43 +3733,46 @@ export class AjaxPerfTrackTests extends AITestClass { appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); this._ajax["_currentWindowHost"] = "www.example.com"; - let trackSpy = this.sandbox.spy(appInsightsCore, "track") + let trackSpy = this.sandbox.spy(appInsightsCore, "track"); this._context["trackStub"] = trackSpy; - // Send fetch request that should trigger a track event when complete - Assert.ok(trackSpy.notCalled, "No fetch called yet"); - fetch("https://httpbin.org/status/200", {method: "post" }); - Assert.equal(true, markSpy.called, "The code should have called been mark()"); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (trackStub.called) { - window.console && window.console.warn("Performance Entries: " + window.performance.getEntries().length); - - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fetch type"); - let props = data.properties; - Assert.notEqual(undefined, props, "Should contain properties"); - if (props) { - let perf = props.ajaxPerf || {}; - if (perf.missing) { - Assert.equal(true, !!perf.missing, "Performance was executed but browser did not populate the window.performance entries - " + JSON.stringify(data)); - } else { - Assert.notEqual(undefined, perf.duration, "Duration exists - " + JSON.stringify(data)); + return this._asyncQueue() + .add(() => { + // Send fetch request that should trigger a track event when complete + Assert.ok(trackSpy.notCalled, "No fetch called yet"); + fetch("https://httpbin.org/status/200", {method: "post" }); + Assert.equal(true, markSpy.called, "The code should have called been mark()"); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + let trackStub = this._context["trackStub"] as SinonStub; + if (trackStub.called) { + window.console && window.console.warn("Performance Entries: " + window.performance.getEntries().length); + + Assert.ok(trackStub.calledOnce, "track is called"); + let data = trackStub.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fetch type"); + let props = data.properties; + Assert.notEqual(undefined, props, "Should contain properties"); + if (props) { + let perf = props.ajaxPerf || {}; + if (perf.missing) { + Assert.equal(true, !!perf.missing, "Performance was executed but browser did not populate the window.performance entries - " + JSON.stringify(data)); + } else { + Assert.notEqual(undefined, perf.duration, "Duration exists - " + JSON.stringify(data)); + } + } + return true; } - } - return true; - } - return false; - }, 'response received', 30, 1000) as any) + return false; + }, 'response received', 30, 1000)) + } }); - this.testCaseAsync({ + + this.testCase({ name: "AjaxPerf: check that performance tracking is included for fetch requests when enabled when the fetch has a delayed promise", - stepDelay: 10, - steps: [ (testContext) => { + test: () => { hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -3741,45 +3808,47 @@ export class AjaxPerfTrackTests extends AITestClass { appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); this._ajax["_currentWindowHost"] = "www.example.com"; - let trackSpy = this.sandbox.spy(appInsightsCore, "track") + let trackSpy = this.sandbox.spy(appInsightsCore, "track"); this._context["trackStub"] = trackSpy; - // Send fetch request that should trigger a track event when complete - Assert.ok(trackSpy.notCalled, "No fetch called yet"); - fetch("https://httpbin.org/status/200", { method: "post" }); - Assert.equal(true, markSpy.called, "The code should have called been mark()"); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (trackStub.called) { - window.console && window.console.warn("Performance Entries: " + window.performance.getEntries().length); - - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fetch type"); - let props = data.properties; - Assert.notEqual(undefined, props, "Should contain properties"); - if (props) { - Assert.notEqual(undefined, props.ajaxPerf, "Perf detail exists") - let perf = props.ajaxPerf || {}; - if (perf.missing) { - Assert.equal(true, !!perf.missing, "Performance was executed but browser did not populate the window.performance entries - " + JSON.stringify(data)); - } else { - Assert.notEqual(undefined, perf.duration, "Duration exists - " + JSON.stringify(data)); + return this._asyncQueue() + .add(() => { + // Send fetch request that should trigger a track event when complete + Assert.ok(trackSpy.notCalled, "No fetch called yet"); + fetch("https://httpbin.org/status/200", { method: "post" }); + Assert.equal(true, markSpy.called, "The code should have called been mark()"); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + let trackStub = this._context["trackStub"] as SinonStub; + if (trackStub.called) { + window.console && window.console.warn("Performance Entries: " + window.performance.getEntries().length); + + Assert.ok(trackStub.calledOnce, "track is called"); + let data = trackStub.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fetch type"); + let props = data.properties; + Assert.notEqual(undefined, props, "Should contain properties"); + if (props) { + Assert.notEqual(undefined, props.ajaxPerf, "Perf detail exists"); + let perf = props.ajaxPerf || {}; + if (perf.missing) { + Assert.equal(true, !!perf.missing, "Performance was executed but browser did not populate the window.performance entries - " + JSON.stringify(data)); + } else { + Assert.notEqual(undefined, perf.duration, "Duration exists - " + JSON.stringify(data)); + } + } + return true; } - } - return true; - } - return false; - }, 'response received', 600, 1000) as any) + return false; + }, 'response received', 600, 1000)) + } }); - this.testCaseAsync({ + this.testCase({ name: "Fetch: should not create and pass correlation header if correlationHeaderExcludePatterns set to exclude all.", - stepDelay: 10, - timeOut: 10000, - steps: [ (testContext) => { + timeout: 10000, + test: () => { let fetchCalls = hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -3812,8 +3881,9 @@ export class AjaxPerfTrackTests extends AITestClass { } }; appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); - let trackSpy = this.sandbox.spy(appInsightsCore, "track") + let trackSpy = this.sandbox.spy(appInsightsCore, "track"); this._context["trackStub"] = trackSpy; + this._context["fetchCalls"] = fetchCalls; // Use test hook to simulate the correct url location this._ajax["_currentWindowHost"] = "httpbin.org"; @@ -3827,37 +3897,40 @@ export class AjaxPerfTrackTests extends AITestClass { }; const url = 'https://httpbin.org/status/200'; - // Act - Assert.ok(trackSpy.notCalled, "No fetch called yet"); - fetch(url, init).then(() => { - // Assert - Assert.ok(trackSpy.called, "The request was not tracked"); - Assert.equal(1, fetchCalls.length); - Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); - let headers:Headers = fetchCalls[0].init.headers as Headers; - Assert.equal(true, headers.has("My-Header"), "My-Header should be present"); - Assert.equal(false, headers.has(RequestHeaders.requestIdHeader), "Correlation header - AI header should be excluded"); // AI - Assert.equal(false, headers.has(RequestHeaders.traceParentHeader), "Correlation header - W3c header should be excluded"); // W3C - }, () => { - Assert.ok(false, "fetch failed!"); - testContext.testDone(); - }); - }] - .concat(PollingAssert.createPollingAssert(() => { - let trackStub = this._context["trackStub"] as SinonStub; - if (trackStub.called) { - Assert.ok(trackStub.calledOnce, "track is called"); - let data = trackStub.args[0][0].baseData; - Assert.equal("Fetch", data.type, "request is Fatch type"); - var id = data.id; - Assert.equal("|", id[0]); - Assert.equal(".", id[id.length - 1]); - return true; - } + return this._asyncQueue() + .add(() => { + + // Act + Assert.ok(trackSpy.notCalled, "No fetch called yet"); + fetch(url, init).then(() => { + // Assert + Assert.ok(trackSpy.called, "The request was not tracked"); + Assert.equal(1, fetchCalls.length); + Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); + let headers:Headers = fetchCalls[0].init.headers as Headers; + Assert.equal(true, headers.has("My-Header"), "My-Header should be present"); + Assert.equal(false, headers.has(RequestHeaders.requestIdHeader), "Correlation header - AI header should be excluded"); // AI + Assert.equal(false, headers.has(RequestHeaders.traceParentHeader), "Correlation header - W3c header should be excluded"); // W3C + }, () => { + Assert.ok(false, "fetch failed!"); + }); + }) + .add(PollingAssert.asyncTaskPollingAssert(() => { + let trackStub = this._context["trackStub"] as SinonStub; + if (trackStub.called) { + Assert.ok(trackStub.calledOnce, "track is called"); + let data = trackStub.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fatch type"); + var id = data.id; + Assert.equal("|", id[0]); + Assert.equal(".", id[id.length - 1]); + return true; + } - return false; - }, 'response received', 60, 1000) as any) - }) + return false; + }, 'response received', 60, 1000)) + } + }); } } @@ -3902,10 +3975,9 @@ export class AjaxFrozenTests extends AITestClass { public registerTests() { - this.testCaseAsync({ + this.testCase({ name: "AjaxFrozenTests: check for prevent extensions", - stepDelay: 10, - steps: [ () => { + test: () => { Object.preventExtensions(XMLHttpRequest); Object.freeze(XMLHttpRequest); let reflect:any = getGlobalInst("Reflect"); @@ -3943,20 +4015,22 @@ export class AjaxFrozenTests extends AITestClass { // trigger the request that should cause a track event once the xhr request is complete xhr.open("GET", "https://httpbin.org/status/200"); xhr.send(); - }] - .concat(PollingAssert.createPollingAssert(() => { - let throwSpy = this._context["throwSpy"] as SinonStub; - if (throwSpy.called) { - Assert.ok(throwSpy.calledOnce, "track is called"); - let message = throwSpy.args[0][2]; - Assert.notEqual(-1, message.indexOf("Failed to monitor XMLHttpRequest")); - let data = throwSpy.args[0][3]; - Assert.notEqual(-1, data.exception.indexOf("Cannot add property _ajaxData")); - return true; - } - return false; - }, 'response received', 60, 1000) as any) + return this._asyncQueue() + .add(PollingAssert.asyncTaskPollingAssert(() => { + let throwSpy = this._context["throwSpy"] as SinonStub; + if (throwSpy.called) { + Assert.ok(throwSpy.calledOnce, "track is called"); + let message = throwSpy.args[0][2]; + Assert.notEqual(-1, message.indexOf("Failed to monitor XMLHttpRequest")); + let data = throwSpy.args[0][3]; + Assert.notEqual(-1, data.exception.indexOf("Cannot add property _ajaxData")); + return true; + } + + return false; + }, 'response received', 60, 1000)) + } }); // This is currently a manual test as we don't have hooks / mocks defined to automated this today From e9003cf485a4261eef6be8eec4a73409db76dd02 Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Tue, 19 Aug 2025 11:19:48 -0700 Subject: [PATCH 2/2] merge main --- .../Tests/Unit/src/ajax.tests.ts | 35 +------------------ 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/extensions/applicationinsights-dependencies-js/Tests/Unit/src/ajax.tests.ts b/extensions/applicationinsights-dependencies-js/Tests/Unit/src/ajax.tests.ts index b21a4f9f1..ef1f84be6 100644 --- a/extensions/applicationinsights-dependencies-js/Tests/Unit/src/ajax.tests.ts +++ b/extensions/applicationinsights-dependencies-js/Tests/Unit/src/ajax.tests.ts @@ -1100,7 +1100,6 @@ export class AjaxTests extends AITestClass { name: "Fetch: internal url fetch isn't tracked [" + endpointUrl + "]", timeout: 10000, test: () => { - hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1172,7 +1171,6 @@ export class AjaxTests extends AITestClass { return this._asyncQueue() .add(() => { - // Act Assert.ok(fetchSpy.notCalled, "No fetch called yet"); return fetch(endpointUrl, {method: "post" }).then(() => { @@ -1237,7 +1235,6 @@ export class AjaxTests extends AITestClass { 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", timeout: 10000, test: () => { - hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1263,8 +1260,6 @@ export class AjaxTests extends AITestClass { return this._asyncQueue() .add(() => { - - // Act Assert.ok(fetchSpy.notCalled, "No fetch called yet"); return fetch("https://httpbin.org/status/200", {method: "post"}).then(() => { @@ -1288,7 +1283,6 @@ export class AjaxTests extends AITestClass { name: "Fetch: add context into custom dimension with call back configuration on AI initialization.", timeout: 10000, test: () => { - hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1324,7 +1318,6 @@ export class AjaxTests extends AITestClass { return this._asyncQueue() .add(() => { - // Act Assert.ok(fetchSpy.notCalled, "No fetch called yet"); return fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { @@ -1394,7 +1387,6 @@ export class AjaxTests extends AITestClass { name: "Fetch: Respond with status 0 and no status text", timeout: 10000, test: () => { - hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1452,7 +1444,6 @@ export class AjaxTests extends AITestClass { name: "Fetch: Respond with status 0 and no status text", timeout: 10000, test: () => { - hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1483,7 +1474,6 @@ export class AjaxTests extends AITestClass { return this._asyncQueue() .add(() => { - // Act let fetchSpy = this._context.fetchSpy; let throwSpy = this._context.throwSpy; @@ -1512,7 +1502,6 @@ export class AjaxTests extends AITestClass { name: "Fetch: fetch addDependencyInitializer adding context", timeout: 10000, test: () => { - hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1552,7 +1541,6 @@ export class AjaxTests extends AITestClass { return this._asyncQueue() .add(() => { - // Act Assert.ok(fetchSpy.notCalled, "No fetch called yet"); return fetch("https://httpbin.org/status/200", {method: "post", [DisabledPropertyName]: false}).then(() => { @@ -1697,7 +1685,6 @@ export class AjaxTests extends AITestClass { name: "Fetch: instrumentation handles invalid / missing request or url with traceId", timeout: 10000, test: () => { - hookFetch((resolve) => { AITestClass.orgSetTimeout(function() { resolve({ @@ -1735,7 +1722,6 @@ export class AjaxTests extends AITestClass { return this._asyncQueue() .add(() => { - // Act Assert.ok(fetchSpy.notCalled, "No fetch called yet"); return fetch(null, {method: "post", [DisabledPropertyName]: false}).then(() => { @@ -2125,7 +2111,6 @@ export class AjaxTests extends AITestClass { return this._asyncQueue() .add(() => { - // Act Assert.ok(trackSpy.notCalled, "No fetch called yet"); return fetch(url).then(() => { @@ -2224,7 +2209,6 @@ export class AjaxTests extends AITestClass { Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C }, () => { Assert.ok(false, "fetch failed!"); - }); }) .add(PollingAssert.asyncTaskPollingAssert(() => { @@ -2291,8 +2275,6 @@ export class AjaxTests extends AITestClass { return this._asyncQueue() .add(() => { - - // Act Assert.ok(trackSpy.notCalled, "No fetch called yet"); fetch(url).then(() => { @@ -2307,7 +2289,6 @@ export class AjaxTests extends AITestClass { Assert.equal(true, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C }, () => { Assert.ok(false, "fetch failed!"); - }); }) .add(PollingAssert.asyncTaskPollingAssert(() => { @@ -2406,7 +2387,6 @@ export class AjaxTests extends AITestClass { Assert.equal("|", id[0]); return true; } - return false; }, 'response received', 60, 1000)) } @@ -2458,7 +2438,6 @@ export class AjaxTests extends AITestClass { return this._asyncQueue() .add(() => { - // Act Assert.ok(trackSpy.notCalled, "No fetch called yet"); fetch(url).then(() => { @@ -2473,7 +2452,6 @@ export class AjaxTests extends AITestClass { Assert.equal(false, headers.has(RequestHeaders.traceParentHeader), "W3c header should be present"); // W3C }, () => { Assert.ok(false, "fetch failed!"); - }); }) .add(PollingAssert.asyncTaskPollingAssert(() => { @@ -2485,7 +2463,6 @@ export class AjaxTests extends AITestClass { Assert.equal("|", id[0]); return true; } - return false; }, 'response received', 60, 1000)) } @@ -2595,12 +2572,9 @@ export class AjaxTests extends AITestClass { Assert.notEqual(undefined, trackHeaders[RequestHeaders.requestIdHeader], "RequestId present in outbound event"); Assert.notEqual(undefined, trackHeaders[RequestHeaders.requestContextHeader], "RequestContext present in outbound event"); Assert.notEqual(undefined, trackHeaders[RequestHeaders.traceParentHeader], "traceParent present in outbound event"); - } - return true; } - this.clock.tick(1000); return false; }, 'response received', 60, 1000)) @@ -3585,9 +3559,8 @@ export class AjaxPerfTrackTests extends AITestClass { }); - this.testCase({ + this.testCase({ name: "AjaxPerf: check that performance tracking is reported, even if the entry is missing when enabled for xhr requests", - test: () => { let performance = getPerformance(); let markSpy = this.sandbox.spy(performance, "mark"); @@ -3613,7 +3586,6 @@ export class AjaxPerfTrackTests extends AITestClass { .add(() => { // Act var xhr = new XMLHttpRequest(); - // trigger the request that should cause a track event once the xhr request is complete xhr.open("GET", "https://httpbin.org/status/200"); xhr.send(); @@ -3633,7 +3605,6 @@ export class AjaxPerfTrackTests extends AITestClass { } return true; } - return false; }, 'response received', 60, 1000)) } @@ -3839,7 +3810,6 @@ export class AjaxPerfTrackTests extends AITestClass { } return true; } - return false; }, 'response received', 600, 1000)) } @@ -3899,7 +3869,6 @@ export class AjaxPerfTrackTests extends AITestClass { return this._asyncQueue() .add(() => { - // Act Assert.ok(trackSpy.notCalled, "No fetch called yet"); fetch(url, init).then(() => { @@ -3926,7 +3895,6 @@ export class AjaxPerfTrackTests extends AITestClass { Assert.equal(".", id[id.length - 1]); return true; } - return false; }, 'response received', 60, 1000)) } @@ -4027,7 +3995,6 @@ export class AjaxFrozenTests extends AITestClass { Assert.notEqual(-1, data.exception.indexOf("Cannot add property _ajaxData")); return true; } - return false; }, 'response received', 60, 1000)) }