Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release-3.0] Cherrypick: fix fetchkeepalive (#2216) #2223

Merged
merged 1 commit into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { Sender } from "../../../src/Sender";
import { createOfflineListener, IOfflineListener } from '../../../src/Offline';
import { EnvelopeCreator } from '../../../src/EnvelopeCreator';
import { Exception, CtxTagKeys, isBeaconApiSupported, DEFAULT_BREEZE_ENDPOINT, DEFAULT_BREEZE_PATH, utlCanUseSessionStorage, utlGetSessionStorage, utlSetSessionStorage } from "@microsoft/applicationinsights-common";
import { ITelemetryItem, AppInsightsCore, ITelemetryPlugin, DiagnosticLogger, NotificationManager, SendRequestReason, _eInternalMessageId, getGlobalInst, safeGetLogger, getJSON, isString, isArray, arrForEach, isBeaconsSupported, IXHROverride, IPayloadData, isFetchSupported, getWindow} from "@microsoft/applicationinsights-core-js";
import { ITelemetryItem, AppInsightsCore, ITelemetryPlugin, DiagnosticLogger, NotificationManager, SendRequestReason, _eInternalMessageId, safeGetLogger, isString, isArray, arrForEach, isBeaconsSupported, IXHROverride, IPayloadData, isFetchSupported, TransportType, getWindow, getGlobal, getGlobalInst} from "@microsoft/applicationinsights-core-js";
import { ArraySendBuffer, SessionStorageSendBuffer } from "../../../src/SendBuffer";
import { ISenderConfig } from "../../../src/Interfaces";



const BUFFER_KEY = "AI_buffer";
const SENT_BUFFER_KEY = "AI_sentBuffer";
export class SenderTests extends AITestClass {
Expand Down Expand Up @@ -1418,6 +1419,167 @@ export class SenderTests extends AITestClass {
}
});

this.testCase({
name: "fetchKeepAliveSender should not send duplicacted events during unload.",
useFakeTimers: true,
test: () => {
let window = getWindow();
let fetchstub = this.sandbox.stub((window as any), "fetch");
let fakeXMLHttpRequest = (window as any).XMLHttpRequest;
let sessionStorage = window.sessionStorage;
QUnit.assert.ok(sessionStorage, "sessionStorage API is supported");
sessionStorage.clear();

let sendBeaconCalled = 0;
this.hookSendBeacon((url: string) => {
sendBeaconCalled += 1;
return true;
});
const sender = new Sender();
const cr = new AppInsightsCore();

sender.initialize({
instrumentationKey: "abc",
extensionConfig: {
[sender.identifier]: {
disableXhr: true
}
}

}, cr, []);
this.onDone(() => {
sender.teardown();
});

const telemetryItem: ITelemetryItem = {
name: "item",
iKey: "iKey",
baseType: "type",
baseData: {}
};


let buffer = sender._buffer;

QUnit.assert.ok(isBeaconApiSupported(), "Beacon API is supported");
QUnit.assert.equal(0, sendBeaconCalled, "Beacon API was not called before");
QUnit.assert.equal(0, this._getXhrRequests().length, "xhr sender was not called before");
QUnit.assert.equal(0, buffer.getItems().length, "sender buffer should be clear");
QUnit.assert.equal(false, fetchstub.called, "fetch sender is not called before");

try {
sender.processTelemetry(telemetryItem, null);
QUnit.assert.equal(1, buffer.getItems().length, "sender buffer should have one payload");
let bufferItems = JSON.parse(sessionStorage.getItem("AI_buffer") as any);
QUnit.assert.equal(bufferItems.length, 1, "sender buffer should have one payload");
let sentItems = JSON.parse(sessionStorage.getItem("AI_sentBuffer") as any);
QUnit.assert.equal(0, sentItems.length, "sent buffer should have zero payload");
sender.onunloadFlush();
} catch(e) {
QUnit.assert.ok(false);
}
this.clock.tick(5);

QUnit.assert.equal(0, sendBeaconCalled, "Beacon API should be not called");
QUnit.assert.equal(0, this._getXhrRequests().length, "xhr sender should not be called");
QUnit.assert.equal(true, fetchstub.calledOnce, "fetch sender is called once");
QUnit.assert.equal(0, buffer.getItems().length, "sender buffer should not have one payload");
QUnit.assert.equal(0, buffer.count(), "sender buffer should not have any payload");
let bufferItems = JSON.parse(sessionStorage.getItem("AI_buffer") as any);
QUnit.assert.equal(bufferItems.length, 0, "sender buffer should be clear payload");
let sentItems = JSON.parse(sessionStorage.getItem("AI_sentBuffer") as any);
QUnit.assert.equal(1, sentItems.length, "sent buffer should have one payload");


(window as any).XMLHttpRequest = fakeXMLHttpRequest;
sessionStorage.clear();

}
});

this.testCase({
name: "fetchKeepAliveSender should only trigger fetch once during unload.",
useFakeTimers: true,
test: () => {
let window = getWindow();
let fakeXMLHttpRequest = (window as any).XMLHttpRequest;
let sessionStorage = window.sessionStorage;
QUnit.assert.ok(sessionStorage, "sessionStorage API is supported");
sessionStorage.clear();

let sendBeaconCalled = 0;
this.hookSendBeacon((url: string) => {
sendBeaconCalled += 1;
return true;
});
let fetchCalls = this.hookFetch((resolve) => {
setTimeout(function() {
resolve();
}, 0);
});

const sender = new Sender();
const cr = new AppInsightsCore();

sender.initialize({
instrumentationKey: "abc",
extensionConfig: {
[sender.identifier]: {
disableXhr: true
}
}

}, cr, []);
this.onDone(() => {
sender.teardown();
});

const telemetryItem: ITelemetryItem = {
name: "item",
iKey: "iKey",
baseType: "type",
baseData: {}
};


let buffer = sender._buffer;

QUnit.assert.ok(isBeaconApiSupported(), "Beacon API is supported");
QUnit.assert.equal(0, sendBeaconCalled, "Beacon API was not called before");
QUnit.assert.equal(0, this._getXhrRequests().length, "xhr sender was not called before");
QUnit.assert.equal(0, buffer.getItems().length, "sender buffer should be clear");
QUnit.assert.equal(0, fetchCalls.length, "fetch calls should not be called before");

try {
sender.processTelemetry(telemetryItem, null);
QUnit.assert.equal(1, buffer.getItems().length, "sender buffer should have one payload");
let bufferItems = JSON.parse(sessionStorage.getItem("AI_buffer") as any);
QUnit.assert.equal(bufferItems.length, 1, "sender buffer should have one payload");
let sentItems = JSON.parse(sessionStorage.getItem("AI_sentBuffer") as any);
QUnit.assert.equal(0, sentItems.length, "sent buffer should have zero payload");
sender.onunloadFlush();
} catch(e) {
QUnit.assert.ok(false);
}
this.clock.tick(5);

QUnit.assert.equal(0, sendBeaconCalled, "Beacon API should be not called");
QUnit.assert.equal(1, fetchCalls.length, "fetch calls should be called only once");
QUnit.assert.equal(0, this._getXhrRequests().length, "xhr sender should not be called");
QUnit.assert.equal(0, buffer.getItems().length, "sender buffer should not have one payload");
QUnit.assert.equal(0, buffer.count(), "sender buffer should not have any payload");
let bufferItems = JSON.parse(sessionStorage.getItem("AI_buffer") as any);
QUnit.assert.equal(bufferItems.length, 0, "sender buffer should be clear payload");
let sentItems = JSON.parse(sessionStorage.getItem("AI_sentBuffer") as any);
QUnit.assert.equal(0, sentItems.length, "sent buffer should not have one payload");


(window as any).XMLHttpRequest = fakeXMLHttpRequest;
sessionStorage.clear();

}
});

this.testCase({
name: 'FetchAPI is used when isBeaconApiDisabled flag is true and disableXhr flag is true , use fetch sender.',
test: () => {
Expand Down
1 change: 1 addition & 0 deletions channels/applicationinsights-channel-js/src/Sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,7 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControls {
payloadSize += payload[lp].length;
}
let payloadData = _getPayload(payload);
_self._buffer.markAsSent(payload);

if ((_syncFetchPayload + payloadSize) <= FetchSyncRequestSizeLimitBytes) {
_doFetchSender(payloadData, onComplete, true);
Expand Down
Loading