From f2d1625a1e36123ab183df427fc2fae65d0f7c52 Mon Sep 17 00:00:00 2001 From: Nev <54870357+MSNev@users.noreply.github.com> Date: Tue, 15 Mar 2022 12:47:35 -0700 Subject: [PATCH] Beta Part 5: Part of Mega Dynamic Load/Unload support (#1782) - Add Missing Exports - AnalyticsPlugin: Implement teardown and initial test validation - Dependencies Plugin: Implement teardown and initial test validation - Add flush() to IAppInsightsCore --- .../Tests/Unit/src/Sender.tests.ts | 2 +- common/Tests/Framework/src/AITestClass.ts | 121 ++++++- .../Tests/Unit/src/AnalyticsPlugin.tests.ts | 326 ++++++++++++++---- .../Unit/src/TelemetryItemCreator.tests.ts | 16 +- .../src/JavaScriptSDK/AnalyticsPlugin.ts | 4 +- .../Telemetry/PageViewManager.ts | 37 +- .../Tests/Unit/src/ajax.tests.ts | 40 ++- .../IAppInsightsCore.ts | 27 +- .../src/JavaScriptSDK/BaseCore.ts | 46 ++- .../src/JavaScriptSDK/BaseTelemetryPlugin.ts | 2 +- .../src/JavaScriptSDK/ChannelController.ts | 2 + .../src/applicationinsights-core-js.ts | 4 +- 12 files changed, 498 insertions(+), 129 deletions(-) diff --git a/channels/applicationinsights-channel-js/Tests/Unit/src/Sender.tests.ts b/channels/applicationinsights-channel-js/Tests/Unit/src/Sender.tests.ts index 631b34de..c17726fa 100644 --- a/channels/applicationinsights-channel-js/Tests/Unit/src/Sender.tests.ts +++ b/channels/applicationinsights-channel-js/Tests/Unit/src/Sender.tests.ts @@ -16,7 +16,7 @@ export class SenderTests extends AITestClass { this._offline = createOfflineListener("SenderTests"); } - public testCleanup() { + public testFinishedCleanup() { if (this._offline) { this._offline.unload(); } diff --git a/common/Tests/Framework/src/AITestClass.ts b/common/Tests/Framework/src/AITestClass.ts index dd5eaeea..123a7d32 100644 --- a/common/Tests/Framework/src/AITestClass.ts +++ b/common/Tests/Framework/src/AITestClass.ts @@ -78,6 +78,16 @@ export class AITestClass { public fakeServerAutoRespond: boolean = false; public isEmulatingEs3: boolean; + /** + * Automatically assert that all registered events have been removed + */ + public assertNoEvents: boolean = false; + + /** + * Automatically assert that all hooks have been removed + */ + public assertNoHooks: boolean = false; + protected _orgCrypto: Crypto | null; protected _orgLocation: Location | null; @@ -96,6 +106,8 @@ export class AITestClass { private _orgObjectFuncs: any = null; private _orgFetch: any = null; + private _onDoneFuncs: VoidFunction[] = []; + constructor(name?: string, emulateEs3?: boolean) { this._moduleName = (emulateEs3 ? "(ES3) " : "") + (name || _getObjName(this, "")); this.isEmulatingEs3 = emulateEs3 @@ -122,7 +134,24 @@ export class AITestClass { public testInitialize() { } - /** Method called after each test method has completed */ + /** + * Method called immediately after the test case has finished, but before the automatic test case assertions. + * Use this method to call unload / teardown so the SDK can remove it's own events before being validated + */ + public testFinishedCleanup() { + this._onDoneFuncs.forEach((fn) => { + try { + fn(); + } catch (e) { + // Do nothing during cleanup + } + }); + this._onDoneFuncs = []; + } + + /** Method called after each test method has completed and after the test sandbox has been cleanup up. + * This is the final step before the next test is executed + */ public testCleanup() { } @@ -185,14 +214,29 @@ export class AITestClass { this._emulateEs3(); } + if (testInfo.assertNoEvents === undefined) { + testInfo.assertNoEvents = this.assertNoEvents; + } + + if (testInfo.assertNoHooks === undefined) { + testInfo.assertNoHooks = this.assertNoHooks; + } + // Run the test. try { let self = this; let testComplete = false; let timeOutTimer: any = null; let stepIndex = 0; - + const testDone = () => { + this._onDoneFuncs.forEach((fn) => { + fn(); + }); + this._onDoneFuncs = []; + + self.testFinishedCleanup(); + if (testInfo.assertNoEvents) { self._assertEventsRemoved(); } @@ -325,6 +369,8 @@ export class AITestClass { AITestClass.currentTestInfo = testInfo; function _testFinished(failed?: boolean) { + self.testFinishedCleanup(); + if (testInfo.assertNoEvents) { self._assertEventsRemoved(); } @@ -366,6 +412,14 @@ export class AITestClass { this._emulateEs3(); } + if (testInfo.assertNoEvents === undefined) { + testInfo.assertNoEvents = this.assertNoEvents; + } + + if (testInfo.assertNoHooks === undefined) { + testInfo.assertNoHooks = this.assertNoHooks; + } + // Run the test. try { this._testStarting(); @@ -472,6 +526,12 @@ export class AITestClass { Assert.ok(false, msg); } + protected onDone(cleanupFn: VoidFunction) { + if (cleanupFn) { + this._onDoneFuncs.push(cleanupFn); + } + } + protected setUserAgent(userAgent: string) { // Hook Send beacon which also mocks navigator this.hookSendBeacon(null); @@ -829,6 +889,7 @@ export class AITestClass { this._beaconHooks = []; this._cleanupAllHooks(); + this._cleanupEvents(); this._restoreEs3(); if (failed) { @@ -841,7 +902,6 @@ export class AITestClass { } this.testCleanup(); - this._cleanupEvents(); // Clear the instance of the currently running suite. AITestClass.currentTestClass = null; @@ -849,13 +909,20 @@ export class AITestClass { } private _assertRemoveFuncHooks(fn:any, targetName: string) { + let failed = false; + if (typeof fn === "function") { let aiHook:any = fn["_aiHooks"]; if (aiHook && aiHook.h) { aiHook.h.forEach((hook: any) => { Assert.ok(false, targetName + " Hook: " + aiHook.n + "." + _formatNamespace(hook.cbks.ns || "") + " exists"); + failed = true; }); + + if (!failed) { + QUnit.assert.ok(true, "Validated [" + targetName + "] has no registered hooks"); + } } } } @@ -864,7 +931,7 @@ export class AITestClass { if (target) { Object.keys(target).forEach(name => { try { - this._assertRemoveFuncHooks(target[name], targetName); + this._assertRemoveFuncHooks(target[name], targetName + "." + name); } catch (e) { // eslint-disable-next-line no-empty } @@ -994,17 +1061,29 @@ export class AITestClass { } private _assertNoEvents(target: any, targetName: string): void { + let failed = false; _getAllAiDataKeys(target, (name, value) => { if (value && name.startsWith("_aiDataEvents")) { let events = value.events; - _objForEachKey(events, (evtName, evts) => { - for (let lp = 0; lp < evts.length; lp++) { - let theEvent = evts[lp]; - Assert.ok(false, "[" + targetName + "] has registered event handler [" + evtName + "." + (theEvent.evtName.ns || "") + "]"); - } - }); + if (events) { + _objForEachKey(events, (evtName, evts) => { + if (evts) { + for (let lp = 0; lp < evts.length; lp++) { + let theEvent = evts[lp]; + if (theEvent) { + Assert.ok(false, "[" + targetName + "] has registered event handler [" + evtName + "." + (theEvent.evtName.ns || "") + "]"); + failed = true; + } + } + } + }); + } } }); + + if (!failed) { + QUnit.assert.ok(true, "Validated [" + targetName + "] has no registered event handlers"); + } } private _removeAllEvents(target: any, targetName: string): any { @@ -1013,15 +1092,21 @@ export class AITestClass { if (value && name.startsWith("_aiDataEvents")) { dataName.push(name); let events = value.events; - _objForEachKey(events, (evtName, evts) => { - for (let lp = 0; lp < evts.length; lp++) { - let theEvent = evts[lp]; - console && console.log("Removing [" + targetName + "] event handler " + evtName + "." + (theEvent.evtName.ns || "")); - if (target.removeEventListener) { - target.removeEventListener(evtName, theEvent.handler, theEvent.capture); + if (events) { + _objForEachKey(events, (evtName, evts) => { + if (evts) { + for (let lp = 0; lp < evts.length; lp++) { + let theEvent = evts[lp]; + if (theEvent) { + console && console.log("Removing [" + targetName + "] event handler " + evtName + "." + (theEvent.evtName.ns || "")); + if (target.removeEventListener) { + target.removeEventListener(evtName, theEvent.handler, theEvent.capture); + } + } + } } - } - }); + }); + } delete value.events; } diff --git a/extensions/applicationinsights-analytics-js/Tests/Unit/src/AnalyticsPlugin.tests.ts b/extensions/applicationinsights-analytics-js/Tests/Unit/src/AnalyticsPlugin.tests.ts index b01fe7ad..87052afe 100644 --- a/extensions/applicationinsights-analytics-js/Tests/Unit/src/AnalyticsPlugin.tests.ts +++ b/extensions/applicationinsights-analytics-js/Tests/Unit/src/AnalyticsPlugin.tests.ts @@ -28,6 +28,12 @@ export class AnalyticsPluginTests extends AITestClass { private throwInternalSpy:SinonSpy; private exceptionHelper: any = new ExceptionHelper(); + constructor(name?: string, emulateEs3?: boolean) { + super(name, emulateEs3); + this.assertNoEvents = true; + this.assertNoHooks = true; + } + public testInitialize() { this._onerror = window.onerror; setEnableEnvMocks(false); @@ -59,8 +65,6 @@ export class AnalyticsPluginTests extends AITestClass { this.testCase({ name: 'enableAutoRouteTracking: event listener is added to the popstate event', - assertNoEvents: true, - assertNoHooks: true, test: () => { // Setup const appInsights = new AnalyticsPlugin(); @@ -72,6 +76,10 @@ export class AnalyticsPluginTests extends AITestClass { let registeredEvents = __getRegisteredEvents(window, null, evtNamespace); Assert.equal(0, registeredEvents.length, "No Events should be registered"); + this.onDone(() => { + core.unload(false); + }); + // Act core.initialize({ instrumentationKey: '', @@ -90,8 +98,6 @@ export class AnalyticsPluginTests extends AITestClass { registeredEvents = __getRegisteredEvents(window, null, evtNamespace); Assert.equal(0, registeredEvents.length, "All Events should have been removed"); - - core.unload(); } }); @@ -117,6 +123,12 @@ export class AnalyticsPluginTests extends AITestClass { let registeredEvents = __getRegisteredEvents(window, null, evtNamespace); Assert.equal(0, registeredEvents.length, "No Events should be registered"); + this.onDone(() => { + if (core.isInitialized()) { + core.unload(false); + } + }); + // Act core.initialize({ instrumentationKey: '', @@ -169,6 +181,10 @@ export class AnalyticsPluginTests extends AITestClass { appInsights['_prevUri'] = "firstUri"; const trackPageViewStub = this.sandbox.stub(appInsights, 'trackPageView'); + this.onDone(() => { + core.unload(false); + }); + // Act core.initialize({ instrumentationKey: '', @@ -211,6 +227,10 @@ export class AnalyticsPluginTests extends AITestClass { properties.context = { telemetryTrace: { traceID: 'not set', parentID: undefined} } as any; this.sandbox.stub(appInsights, 'trackPageView'); + this.onDone(() => { + core.unload(false); + }); + // Act core.initialize({ instrumentationKey: '', @@ -240,6 +260,10 @@ export class AnalyticsPluginTests extends AITestClass { instrumentationKey: 'ikey' }; + this.onDone(() => { + core.unload(false); + }); + core.initialize( config, [appInsights, channel] @@ -265,6 +289,11 @@ export class AnalyticsPluginTests extends AITestClass { instrumentationKey: 'ikey', autoTrackPageVisitTime: true }; + + this.onDone(() => { + core.unload(false); + }); + core.initialize( config, [appInsights, channel] @@ -304,6 +333,10 @@ export class AnalyticsPluginTests extends AITestClass { } }; + this.onDone(() => { + core.unload(false); + }); + // Initialize core.initialize(config, [appInsights, channel, properties]); @@ -379,7 +412,12 @@ export class AnalyticsPluginTests extends AITestClass { [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({instrumentationKey: core.config.instrumentationKey}, core, []); + + this.onDone(() => { + core.unload(false); + }); + + core.addPlugin(appInsights); const trackStub = this.sandbox.stub(appInsights.core, "track"); let envelope: ITelemetryItem; @@ -410,12 +448,17 @@ export class AnalyticsPluginTests extends AITestClass { // setup const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, []); + core.addPlugin(appInsights); const senderStub = this.sandbox.stub(appInsights.core, "track"); // Act @@ -436,12 +479,17 @@ export class AnalyticsPluginTests extends AITestClass { // setup const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, []); + core.addPlugin(appInsights); const trackStub = this.sandbox.stub(appInsights.core, "track"); // Test @@ -461,12 +509,17 @@ export class AnalyticsPluginTests extends AITestClass { // setup const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, []); + core.addPlugin(appInsights); const trackStub = this.sandbox.stub(appInsights.core, "track"); // Test @@ -490,12 +543,17 @@ export class AnalyticsPluginTests extends AITestClass { // setup const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, []); + core.addPlugin(appInsights); const unexpectedError = new Error(); const expectedString = dumpObj(unexpectedError); @@ -520,12 +578,17 @@ export class AnalyticsPluginTests extends AITestClass { // setup const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ instrumentationKey: "ikey"}, core, []); + core.addPlugin(appInsights); const unexpectedError = new Error("some message"); const throwSpy = this.sandbox.spy(core.logger, "throwInternal"); const stub = this.sandbox.stub(appInsights, "trackException").throws(unexpectedError); @@ -547,12 +610,17 @@ export class AnalyticsPluginTests extends AITestClass { // setup const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( {instrumentationKey: "key"}, [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ instrumentationKey: "key" }, core, []); + core.addPlugin(appInsights); const throwInternal = this.sandbox.spy(appInsights.core.logger, "throwInternal"); @@ -573,12 +641,17 @@ export class AnalyticsPluginTests extends AITestClass { // setup const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, []); + core.addPlugin(appInsights); const trackSpy = this.sandbox.spy(appInsights.core, "track"); // Act @@ -595,12 +668,17 @@ export class AnalyticsPluginTests extends AITestClass { // setup const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, []); + core.addPlugin(appInsights); const trackExceptionSpy = this.sandbox.spy(appInsights, "trackException"); // Act @@ -619,12 +697,17 @@ export class AnalyticsPluginTests extends AITestClass { // setup const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( {instrumentationKey: "key"}, [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ instrumentationKey: "key" }, core, []); + core.addPlugin(appInsights); const throwInternal = this.sandbox.spy(appInsights.core.logger, "throwInternal"); @@ -653,6 +736,11 @@ export class AnalyticsPluginTests extends AITestClass { // setup const sender: Sender = new Sender(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( { instrumentationKey: "key", @@ -666,7 +754,7 @@ export class AnalyticsPluginTests extends AITestClass { [sender] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ instrumentationKey: "key" }, core, []); + core.addPlugin(appInsights); appInsights.addTelemetryInitializer((item: ITelemetryItem) => { Assert.equal("4.0", item.ver, "Telemetry items inside telemetry initializers should be in CS4.0 format"); }); @@ -731,6 +819,11 @@ export class AnalyticsPluginTests extends AITestClass { // setup const sender: Sender = new Sender(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( { instrumentationKey: "key", @@ -744,7 +837,7 @@ export class AnalyticsPluginTests extends AITestClass { [sender] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ instrumentationKey: "key" }, core, []); + core.addPlugin(appInsights); appInsights.addTelemetryInitializer((item: ITelemetryItem) => { Assert.equal("4.0", item.ver, "Telemetry items inside telemetry initializers should be in CS4.0 format"); }); @@ -800,6 +893,11 @@ export class AnalyticsPluginTests extends AITestClass { // setup const sender: Sender = new Sender(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( { instrumentationKey: "key", @@ -813,7 +911,7 @@ export class AnalyticsPluginTests extends AITestClass { [sender] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ instrumentationKey: "key" }, core, []); + core.addPlugin(appInsights); appInsights.addTelemetryInitializer((item: ITelemetryItem) => { Assert.equal("4.0", item.ver, "Telemetry items inside telemetry initializers should be in CS4.0 format"); }); @@ -877,6 +975,11 @@ export class AnalyticsPluginTests extends AITestClass { // setup const sender: Sender = new Sender(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( { instrumentationKey: "key", @@ -890,7 +993,7 @@ export class AnalyticsPluginTests extends AITestClass { [sender] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ instrumentationKey: "key" }, core, []); + core.addPlugin(appInsights); appInsights.addTelemetryInitializer((item: ITelemetryItem) => { Assert.equal("4.0", item.ver, "Telemetry items inside telemetry initializers should be in CS4.0 format"); }); @@ -980,12 +1083,17 @@ export class AnalyticsPluginTests extends AITestClass { // setup const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( {instrumentationKey: "key"}, [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, []); + core.addPlugin(appInsights); const spy = this.sandbox.spy(appInsights, "sendPageViewInternal"); this.clock.tick(1); @@ -1015,8 +1123,13 @@ export class AnalyticsPluginTests extends AITestClass { test: () => { // setup const core = new AppInsightsCore(); + this.sandbox.stub(core, "getTransmissionControls"); const appInsights = new AnalyticsPlugin(); + this.onDone(() => { + appInsights.teardown(); + }); + appInsights.initialize({ "instrumentationKey": "ikey" }, core, []); const trackStub = this.sandbox.stub(appInsights.core, "track"); this.clock.tick(10); // Needed to ensure the duration calculation works @@ -1040,8 +1153,13 @@ export class AnalyticsPluginTests extends AITestClass { test: () => { // setup const core = new AppInsightsCore(); + this.sandbox.stub(core, "getTransmissionControls"); const appInsights = new AnalyticsPlugin(); + this.onDone(() => { + appInsights.teardown(); + }); + appInsights.initialize({ "instrumentationKey": "ikey" }, core, []); const trackStub = this.sandbox.stub(appInsights.core, "track"); this.clock.tick(10); // Needed to ensure the duration calculation works @@ -1080,12 +1198,17 @@ export class AnalyticsPluginTests extends AITestClass { // setup const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, []); + core.addPlugin(appInsights); const logStub = this.sandbox.stub(core.logger, "throwInternal"); core.logger.consoleLoggingLevel = () => 999; @@ -1105,12 +1228,17 @@ export class AnalyticsPluginTests extends AITestClass { // setup const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, []); + core.addPlugin(appInsights); const logStub = this.sandbox.stub(core.logger, "throwInternal"); core.logger.consoleLoggingLevel = () => 999; @@ -1131,12 +1259,17 @@ export class AnalyticsPluginTests extends AITestClass { // Setup const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin] ); const appInsights = new AnalyticsPlugin(); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, []); + core.addPlugin(appInsights); const trackStub = this.sandbox.stub(appInsights.core, "track"); // Act @@ -1169,12 +1302,15 @@ export class AnalyticsPluginTests extends AITestClass { const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); const appInsights = new AnalyticsPlugin(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin, appInsights] ); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]); - plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]); const trackStub = this.sandbox.spy(appInsights.core.getTransmissionControls()[0][0], 'processTelemetry'); const telemetryInitializer = { @@ -1201,12 +1337,16 @@ export class AnalyticsPluginTests extends AITestClass { const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); const appInsights = new AnalyticsPlugin(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin, appInsights] ); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]); - plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]); + const trackStub = this.sandbox.spy(appInsights.core.getTransmissionControls()[0][0], 'processTelemetry'); const nameOverride = "my unique name"; const telemetryInitializer = { @@ -1235,12 +1375,16 @@ export class AnalyticsPluginTests extends AITestClass { const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); const appInsights = new AnalyticsPlugin(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin, appInsights] ); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]); - plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]); + const trackStub = this.sandbox.spy(appInsights.core.getTransmissionControls()[0][0], 'processTelemetry'); const messageOverride = "my unique name"; @@ -1281,12 +1425,16 @@ export class AnalyticsPluginTests extends AITestClass { const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); const appInsights = new AnalyticsPlugin(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin, appInsights] ); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]); - plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]); + const initializer1 = { init: () => { } }; const initializer2 = { init: () => { } }; const spy1 = this.sandbox.spy(initializer1, "init"); @@ -1311,12 +1459,16 @@ export class AnalyticsPluginTests extends AITestClass { const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); const appInsights = new AnalyticsPlugin(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin, appInsights] ); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]); - plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]); + const initializer1 = { init: (item: ITelemetryItem) => { if (item.data !== undefined) { item.data.init1 = true; @@ -1350,12 +1502,16 @@ export class AnalyticsPluginTests extends AITestClass { const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); const appInsights = new AnalyticsPlugin(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin, appInsights] ); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]); - plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]); + const initializer1 = { init: () => { } }; const initializer2 = { init: () => { } }; const spy1 = this.sandbox.spy(initializer1, "init"); @@ -1380,12 +1536,16 @@ export class AnalyticsPluginTests extends AITestClass { const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); const appInsights = new AnalyticsPlugin(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin, appInsights] ); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]); - plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]); + const trackStub = this.sandbox.spy(appInsights.core.getTransmissionControls()[0][0], 'processTelemetry'); // act @@ -1404,12 +1564,16 @@ export class AnalyticsPluginTests extends AITestClass { const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); const appInsights = new AnalyticsPlugin(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin, appInsights] ); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]); - plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]); + const trackStub = this.sandbox.spy(appInsights.core.getTransmissionControls()[0][0], 'processTelemetry'); // act @@ -1428,12 +1592,16 @@ export class AnalyticsPluginTests extends AITestClass { const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); const appInsights = new AnalyticsPlugin(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin, appInsights] ); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]); - plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]); + const trackStub = this.sandbox.spy(appInsights.core.getTransmissionControls()[0][0], 'processTelemetry'); // act @@ -1452,12 +1620,16 @@ export class AnalyticsPluginTests extends AITestClass { const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); const appInsights = new AnalyticsPlugin(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin, appInsights] ); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]); - plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]); + const trackStub = this.sandbox.spy(appInsights.core.getTransmissionControls()[0][0], 'processTelemetry'); // act @@ -1478,12 +1650,16 @@ export class AnalyticsPluginTests extends AITestClass { const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); const appInsights = new AnalyticsPlugin(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin, appInsights] ); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]); - plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]); + const trackStub = this.sandbox.spy(appInsights.core.getTransmissionControls()[0][0], 'processTelemetry'); // act @@ -1504,12 +1680,16 @@ export class AnalyticsPluginTests extends AITestClass { const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); const appInsights = new AnalyticsPlugin(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin, appInsights] ); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]); - plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]); + const trackStub = this.sandbox.spy(appInsights.core.getTransmissionControls()[0][0], 'processTelemetry'); // act @@ -1530,12 +1710,16 @@ export class AnalyticsPluginTests extends AITestClass { const plugin = new ChannelPlugin(); const core = new AppInsightsCore(); const appInsights = new AnalyticsPlugin(); + + this.onDone(() => { + core.unload(false); + }); + core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin, appInsights] ); - appInsights.initialize({ "instrumentationKey": "ikey" }, core, [plugin, appInsights]); - plugin.initialize({instrumentationKey: 'ikey'}, core, [plugin, appInsights]); + const trackStub = this.sandbox.spy(appInsights.core.getTransmissionControls()[0][0], 'processTelemetry'); const logStub = this.sandbox.spy(appInsights.core.logger, "throwInternal") // act diff --git a/extensions/applicationinsights-analytics-js/Tests/Unit/src/TelemetryItemCreator.tests.ts b/extensions/applicationinsights-analytics-js/Tests/Unit/src/TelemetryItemCreator.tests.ts index 02cff4d3..510ac2d3 100644 --- a/extensions/applicationinsights-analytics-js/Tests/Unit/src/TelemetryItemCreator.tests.ts +++ b/extensions/applicationinsights-analytics-js/Tests/Unit/src/TelemetryItemCreator.tests.ts @@ -26,15 +26,27 @@ export class TelemetryItemCreatorTests extends AITestClass { private _core: IAppInsightsCore; private _appInsights: AnalyticsPlugin; + constructor(name?: string, emulateEs3?: boolean) { + super(name, emulateEs3); + this.assertNoEvents = true; + this.assertNoHooks = true; + } + public testInitialize() { const plugin: IPlugin = new ChannelPlugin(); this._core = new AppInsightsCore(); this._core.initialize( - {instrumentationKey: "key"}, + {instrumentationKey: "ikey"}, [plugin] ); this._appInsights = new AnalyticsPlugin(); - this._appInsights.initialize({ "instrumentationKey": "ikey" }, this._core, []); + this._core.addPlugin(this._appInsights); + } + + public testFinishedCleanup(): void { + if (this._core) { + this._core.unload(false); + } } public registerTests() { diff --git a/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/AnalyticsPlugin.ts b/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/AnalyticsPlugin.ts index fbc8c7cc..8ed1c7c4 100644 --- a/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/AnalyticsPlugin.ts +++ b/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/AnalyticsPlugin.ts @@ -543,7 +543,7 @@ export class AnalyticsPlugin extends BaseTelemetryPlugin implements IAppInsights _base.initialize(config, core, extensions, pluginChain); try { - _evtNamespace = mergeEvtNamespace(createUniqueNamespace("AnalyticsPlugin"), core.evtNamespace && core.evtNamespace()); + _evtNamespace = mergeEvtNamespace(createUniqueNamespace(_self.identifier), core.evtNamespace && core.evtNamespace()); if (_preInitTelemetryInitializers) { arrForEach(_preInitTelemetryInitializers, (initializer) => { core.addTelemetryInitializer(initializer); @@ -605,6 +605,8 @@ export class AnalyticsPlugin extends BaseTelemetryPlugin implements IAppInsights }; _self._doTeardown = (unloadCtx?: IProcessTelemetryUnloadContext, unloadState?: ITelemetryUnloadState) => { + _pageViewManager && _pageViewManager.teardown(unloadCtx, unloadState) + // Just register to remove all events associated with this namespace eventOff(window, null, null, _evtNamespace); _initDefaults(); diff --git a/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/Telemetry/PageViewManager.ts b/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/Telemetry/PageViewManager.ts index 7eb4491b..0c7571ae 100644 --- a/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/Telemetry/PageViewManager.ts +++ b/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/Telemetry/PageViewManager.ts @@ -6,7 +6,7 @@ import { } from "@microsoft/applicationinsights-common"; import { IAppInsightsCore, IDiagnosticLogger, LoggingSeverity, - _InternalMessageId, getDocument, getLocation, arrForEach, isNullOrUndefined, getExceptionName, dumpObj + _InternalMessageId, getDocument, getLocation, arrForEach, isNullOrUndefined, getExceptionName, dumpObj, IProcessTelemetryUnloadContext, ITelemetryUnloadState } from "@microsoft/applicationinsights-core-js"; import { PageViewPerformanceManager } from "./PageViewPerformanceManager"; import dynamicProto from "@microsoft/dynamicproto-js"; @@ -40,11 +40,9 @@ export class PageViewManager { _logger = core.logger; } - function _flushChannels() { + function _flushChannels(isAsync: boolean) { if (core) { - arrForEach(core.getTransmissionControls(), queues => { - arrForEach(queues, (q) => { q.flush(true); }) - }); + core.flush(isAsync); } } @@ -72,7 +70,7 @@ export class PageViewManager { if (doFlush) { // We process at least one item so flush the queue - _flushChannels(); + _flushChannels(true); } }), 100); } @@ -99,7 +97,7 @@ export class PageViewManager { pageView, customProperties ); - _flushChannels(); + _flushChannels(true); // no navigation timing (IE 8, iOS Safari 8.4, Opera Mini 8 - see http://caniuse.com/#feat=nav-timing) _logger.throwInternal( @@ -143,7 +141,7 @@ export class PageViewManager { pageView, customProperties ); - _flushChannels(); + _flushChannels(true); pageViewSent = true; } @@ -207,7 +205,24 @@ export class PageViewManager { return processed; }); - } + }; + + _self.teardown = (unloadCtx?: IProcessTelemetryUnloadContext, unloadState?: ITelemetryUnloadState) => { + if (intervalHandle) { + clearInterval(intervalHandle); + intervalHandle = null; + + let allItems = itemQueue.slice(0); + let doFlush = false; + itemQueue = []; + arrForEach(allItems, (item) => { + if (item()) { + doFlush = true; + } + }); + } + }; + }); } @@ -224,4 +239,8 @@ export class PageViewManager { public trackPageView(pageView: IPageViewTelemetry, customProperties?: { [key: string]: any }) { // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging } + + public teardown(unloadCtx?: IProcessTelemetryUnloadContext, unloadState?: ITelemetryUnloadState) { + // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging + } } 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 5ad7d45d..6c9bbf99 100644 --- a/extensions/applicationinsights-dependencies-js/Tests/Unit/src/ajax.tests.ts +++ b/extensions/applicationinsights-dependencies-js/Tests/Unit/src/ajax.tests.ts @@ -44,18 +44,27 @@ export class AjaxTests extends AITestClass { private _ajax:AjaxMonitor = null; private _context:any = {}; + constructor(name?: string, emulateEs3?: boolean) { + super(name, emulateEs3); + this.assertNoEvents = true; + this.assertNoHooks = true; + } + public testInitialize() { this._context = {}; this.useFakeServer = true; this._fetch = getGlobalInst("fetch"); } - public testCleanup() { - this._context = {}; + public testFinishedCleanup(): void { if (this._ajax !== null) { this._ajax.teardown(); this._ajax = null; } + } + + public testCleanup() { + this._context = {}; getGlobal().fetch = this._fetch; } @@ -1912,9 +1921,10 @@ export class AjaxPerfTrackTests extends AITestClass { private _perfEntries: PerformanceEntry[]; private _context:any; - constructor(name?: string) { - super(name); - + constructor(name?: string, emulateEs3?: boolean) { + super(name, emulateEs3); + this.assertNoEvents = true; + this.assertNoHooks = true; this.useFakeServer = false; this._perfEntries = []; this._context = {}; @@ -1971,12 +1981,15 @@ export class AjaxPerfTrackTests extends AITestClass { } } - public testCleanup() { - this._context = {}; + public testFinishedCleanup(): void { if (this._ajax) { this._ajax.teardown(); this._ajax = null; } + } + + public testCleanup() { + this._context = {}; if (this._initialPerformance) { (window).performance = this._initialPerformance; @@ -2500,8 +2513,10 @@ export class AjaxFrozenTests extends AITestClass { private _ajax:AjaxMonitor; private _context:any; - constructor(name?: string) { - super(name); + constructor(name?: string, emulateEs3?: boolean) { + super(name, emulateEs3); + this.assertNoEvents = true; + this.assertNoHooks = true; this.useFakeServer = false; this._context = {}; @@ -2513,12 +2528,15 @@ export class AjaxFrozenTests extends AITestClass { this._xmlHttpRequest = getGlobalInst("XMLHttpRquest)"); } - public testCleanup() { - this._context = {}; + public testFinishedCleanup(): void { if (this._ajax) { this._ajax.teardown(); this._ajax = null; } + } + + public testCleanup() { + this._context = {}; // Restore the real fetch window.fetch = this._fetch; diff --git a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IAppInsightsCore.ts b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IAppInsightsCore.ts index 42c12c4a..e5ac6689 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IAppInsightsCore.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IAppInsightsCore.ts @@ -11,7 +11,9 @@ import { IProcessTelemetryContext } from "./IProcessTelemetryContext"; import { IPerfManagerProvider } from "./IPerfManager"; import { ICookieMgr } from "./ICookieMgr"; import { ITelemetryInitializerHandler, TelemetryInitializerFunction } from "./ITelemetryInitializers"; -import { UnloadHandler } from "../applicationinsights-core-js"; +import { ITelemetryUnloadState } from "./ITelemetryUnloadState"; +import { UnloadHandler } from "../JavaScriptSDK/UnloadHandlerContainer"; +import { SendRequestReason } from "../JavaScriptSDK.Enums/SendRequestReason"; export interface ILoadedPlugin { plugin: T; @@ -116,8 +118,11 @@ export interface IAppInsightsCore extends IPerfManagerProvider { * approach is to create a new instance and initialize that instance. * This is due to possible unexpected side effects caused by plugins not supporting unload / teardown, unable * to successfully remove any global references or they may just be completing the unload process asynchronously. + * @param isAsync - Can the unload be performed asynchronously (default) + * @param unloadComplete - An optional callback that will be called once the unload has completed + * @param cbTimeout - An optional timeout to wait for any flush operations to complete before proceeding with the unload. Defaults to 5 seconds. */ - unload(isAsync?: boolean, unloadComplete?: () => void): void; + unload(isAsync?: boolean, unloadComplete?: (unloadState: ITelemetryUnloadState) => void, cbTimeout?: number): void; /** * Find and return the (first) plugin with the specified identifier if present @@ -128,10 +133,11 @@ export interface IAppInsightsCore extends IPerfManagerProvider { /** * Add a new plugin to the installation * @param plugin - The new plugin to add - * @param replaceExisting - should any existing plugin be replaced + * @param replaceExisting - should any existing plugin be replaced, default is false * @param doAsync - Should the add be performed asynchronously + * @param addCb - [Optional] callback to call after the plugin has been added */ - addPlugin(plugin: T, replaceExisting: boolean, doAsync: boolean, addCb?: (added?: boolean) => void): void; + addPlugin(plugin: T, replaceExisting?: boolean, doAsync?: boolean, addCb?: (added?: boolean) => void): void; /** * Returns the unique event namespace that should be used when registering events @@ -143,4 +149,15 @@ export interface IAppInsightsCore extends IPerfManagerProvider { * @param handler - the handler */ addUnloadCb(handler: UnloadHandler): void; - } + + /** + * Flush and send any batched / cached data immediately + * @param async - send data asynchronously when true (defaults to true) + * @param callBack - if specified, notify caller when send is complete, the channel should return true to indicate to the caller that it will be called. + * If the caller doesn't return true the caller should assume that it may never be called. + * @param sendReason - specify the reason that you are calling "flush" defaults to ManualFlush (1) if not specified + * @param cbTimeout - An optional timeout to wait for any flush operations to complete before proceeding with the unload. Defaults to 5 seconds. + * @returns - true if the callback will be return after the flush is complete otherwise the caller should assume that any provided callback will never be called + */ + flush(isAsync?: boolean, callBack?: (flushComplete?: boolean) => void, sendReason?: SendRequestReason, cbTimeout?: number): boolean | void; +} diff --git a/shared/AppInsightsCore/src/JavaScriptSDK/BaseCore.ts b/shared/AppInsightsCore/src/JavaScriptSDK/BaseCore.ts index f0fa4ae2..29d83a52 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK/BaseCore.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK/BaseCore.ts @@ -194,7 +194,7 @@ export class BaseCore implements IAppInsightsCore { _initPluginChain(config, null); - if (_self.getTransmissionControls().length === 0) { + if (!_channelQueue || _channelQueue.length === 0) { throwError("No channels available"); } @@ -379,16 +379,14 @@ export class BaseCore implements IAppInsightsCore { processUnloadCtx.processNext(unloadState); } - if (_channelControl) { - _channelControl.flush(isAsync, _doUnload, SendRequestReason.SdkUnload, cbTimeout); - } else { - _doUnload(true); + if (!_flushChannels(isAsync, _doUnload, SendRequestReason.SdkUnload, cbTimeout)) { + _doUnload(false); } }; _self.getPlugin = _getPlugin; - _self.addPlugin = (plugin: T, replaceExisting: boolean, isAsync: boolean = true, addCb?: (added?: boolean) => void): void => { + _self.addPlugin = (plugin: T, replaceExisting?: boolean, isAsync?: boolean, addCb?: (added?: boolean) => void): void => { if (!plugin) { addCb && addCb(false); _logOrThrowError(strValidationError); @@ -442,6 +440,8 @@ export class BaseCore implements IAppInsightsCore { return _evtNamespace; }; + _self.flush = _flushChannels; + // Create the addUnloadCb proxyFunctionAs(_self, "addUnloadCb", () => _unloadHandlers, "add"); @@ -662,6 +662,15 @@ export class BaseCore implements IAppInsightsCore { } } + function _flushChannels(isAsync?: boolean, callBack?: (flushComplete?: boolean) => void, sendReason?: SendRequestReason, cbTimeout?: number) { + if (_channelControl) { + return _channelControl.flush(isAsync, callBack, sendReason || SendRequestReason.SdkUnload, cbTimeout); + } + + callBack && callBack(false); + return true; + } + function _initDebugListener(config: IConfiguration) { if (config.disableDbgExt === true && _debugListener) { @@ -815,10 +824,12 @@ export class BaseCore implements IAppInsightsCore { * approach is to create a new instance and initialize that instance. * This is due to possible unexpected side effects caused by plugins not supporting unload / teardown, unable * to successfully remove any global references or they may just be completing the unload process asynchronously. + * @param isAsync - Can the unload be performed asynchronously (default) + * @param unloadComplete - An optional callback that will be called once the unload has completed + * @param cbTimeout - An optional timeout to wait for any flush operations to complete before proceeding with the unload. Defaults to 5 seconds. */ public unload(isAsync?: boolean, unloadComplete?: (unloadState: ITelemetryUnloadState) => void, cbTimeout?: number): void { // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging - return null; } public getPlugin(pluginIdentifier: string): ILoadedPlugin { @@ -826,7 +837,14 @@ export class BaseCore implements IAppInsightsCore { return null; } - public addPlugin(plugin: T, replaceExisting: boolean, doAsync: boolean, addCb?: (added?: boolean) => void): void { + /** + * Add a new plugin to the installation + * @param plugin - The new plugin to add + * @param replaceExisting - should any existing plugin be replaced, default is false + * @param doAsync - Should the add be performed asynchronously + * @param addCb - [Optional] callback to call after the plugin has been added + */ + public addPlugin(plugin: T, replaceExisting?: boolean, doAsync?: boolean, addCb?: (added?: boolean) => void): void { // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging } @@ -846,6 +864,18 @@ export class BaseCore implements IAppInsightsCore { // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging } + /** + * Flush and send any batched / cached data immediately + * @param async - send data asynchronously when true (defaults to true) + * @param callBack - if specified, notify caller when send is complete, the channel should return true to indicate to the caller that it will be called. + * If the caller doesn't return true the caller should assume that it may never be called. + * @param sendReason - specify the reason that you are calling "flush" defaults to ManualFlush (1) if not specified + * @returns - true if the callback will be return after the flush is complete otherwise the caller should assume that any provided callback will never be called + */ + public flush(isAsync?: boolean, callBack?: (flushComplete?: boolean) => void, sendReason?: SendRequestReason): void { + // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging + } + protected releaseQueue() { // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging } diff --git a/shared/AppInsightsCore/src/JavaScriptSDK/BaseTelemetryPlugin.ts b/shared/AppInsightsCore/src/JavaScriptSDK/BaseTelemetryPlugin.ts index 983258ce..b0d45c8d 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK/BaseTelemetryPlugin.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK/BaseTelemetryPlugin.ts @@ -121,7 +121,7 @@ export abstract class BaseTelemetryPlugin implements ITelemetryPlugin { // If this plugin has already been torn down (not operational) or is not initialized (core is not set) // or the core being used for unload was not the same core used for initialization. if (!_self.core || (unloadCtx && _self.core !== unloadCtx.core())) { - // Do Nothing + // Do Nothing as either the plugin is not initialized or was not initialized by the current core return; } diff --git a/shared/AppInsightsCore/src/JavaScriptSDK/ChannelController.ts b/shared/AppInsightsCore/src/JavaScriptSDK/ChannelController.ts index cf1e64bb..e36f0f12 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK/ChannelController.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK/ChannelController.ts @@ -237,6 +237,8 @@ export function createChannelControllerPlugin(channelQueue: _IInternalChannels[] doneIterating = true; doCallback(); }); + + return true; }, _setQueue: (queue: _IInternalChannels[]) => { channelQueue = queue; diff --git a/shared/AppInsightsCore/src/applicationinsights-core-js.ts b/shared/AppInsightsCore/src/applicationinsights-core-js.ts index 07e0f456..f5c9caf8 100644 --- a/shared/AppInsightsCore/src/applicationinsights-core-js.ts +++ b/shared/AppInsightsCore/src/applicationinsights-core-js.ts @@ -31,8 +31,8 @@ export { } from "./JavaScriptSDK/HelperFuncs"; export { attachEvent, detachEvent, addEventHandler, addEventListeners, addPageUnloadEventListener, addPageHideEventListener, addPageShowEventListener, - removeEventHandler, removeEventListeners, removePageUnloadEventListener, removePageHideEventListener, eventOn, eventOff, mergeEvtNamespace, - _IRegisteredEvents, __getRegisteredEvents + removeEventHandler, removeEventListeners, removePageUnloadEventListener, removePageHideEventListener, removePageShowEventListener, eventOn, eventOff, + mergeEvtNamespace, _IRegisteredEvents, __getRegisteredEvents } from "./JavaScriptSDK/EventHelpers"; export {