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
This commit is contained in:
Nev 2022-03-15 12:47:35 -07:00 коммит произвёл GitHub
Родитель 4936a52cee
Коммит f2d1625a1e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 498 добавлений и 129 удалений

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

@ -16,7 +16,7 @@ export class SenderTests extends AITestClass {
this._offline = createOfflineListener("SenderTests");
}
public testCleanup() {
public testFinishedCleanup() {
if (this._offline) {
this._offline.unload();
}

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

@ -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;
}

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

@ -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

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

@ -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() {

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

@ -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();

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

@ -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
}
}

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

@ -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) {
(<any>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;

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

@ -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<T extends IPlugin> {
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<T extends IPlugin = ITelemetryPlugin>(plugin: T, replaceExisting: boolean, doAsync: boolean, addCb?: (added?: boolean) => void): void;
addPlugin<T extends IPlugin = ITelemetryPlugin>(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;
}

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

@ -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 = <T extends IPlugin = ITelemetryPlugin>(plugin: T, replaceExisting: boolean, isAsync: boolean = true, addCb?: (added?: boolean) => void): void => {
_self.addPlugin = <T extends IPlugin = ITelemetryPlugin>(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<T extends IPlugin = IPlugin>(pluginIdentifier: string): ILoadedPlugin<T> {
@ -826,7 +837,14 @@ export class BaseCore implements IAppInsightsCore {
return null;
}
public addPlugin<T extends IPlugin = ITelemetryPlugin>(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<T extends IPlugin = ITelemetryPlugin>(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
}

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

@ -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;
}

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

@ -237,6 +237,8 @@ export function createChannelControllerPlugin(channelQueue: _IInternalChannels[]
doneIterating = true;
doCallback();
});
return true;
},
_setQueue: (queue: _IInternalChannels[]) => {
channelQueue = queue;

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

@ -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 {