Skip to content
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
@@ -1,10 +1,11 @@
import { isSDKRunningInChromeExtension } from '../../src/utilities/detect';
import { isIE11, isSDKRunningInChromeExtension } from '../../src/utilities/detect';

describe('Detect env/feature/capabilities', () => {
const reset = () => {
(window as any).chrome = undefined;
};
afterEach(reset);

describe('isSDKRunningInChromeExtension', () => {
it('should return true if SDK is running inside chrome extension', () => {
(window as any).chrome = {
Expand All @@ -19,4 +20,23 @@ describe('Detect env/feature/capabilities', () => {
expect(isSDKRunningInChromeExtension()).toBeFalsy();
});
});

describe('isIE11', () => {
const testCases = [
{
userAgent:
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
expected: false,
},
{
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko',
expected: true,
},
];

it.each(testCases)('should return $expected for $userAgent', ({ userAgent, expected }) => {
(globalThis.navigator as any).userAgent = userAgent;
expect(isIE11()).toBe(expected);
});
});
Comment thread
saikumarrs marked this conversation as resolved.
});
25 changes: 21 additions & 4 deletions packages/analytics-js-common/__tests__/utilities/page.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ describe('onPageLeave', () => {
});
};

const originalUserAgent = (globalThis.navigator as any).userAgent;

beforeEach(() => {
jest.useFakeTimers();
setVisibilityState('visible');
});

afterEach(() => {
(globalThis.navigator as any).userAgent = originalUserAgent;
jest.useRealTimers();
});

it('should fire the callback on pagehide event', () => {
const evCallback = jest.fn();
onPageLeave(evCallback);
Expand All @@ -40,7 +47,9 @@ describe('onPageLeave', () => {
expect(evCallback).toHaveBeenCalledWith(true);
});

it('should fire the callback on beforeunload event', () => {
it('should fire the callback on beforeunload event for IE11', () => {
(globalThis.navigator as any).userAgent =
'Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko';
const evCallback = jest.fn();
onPageLeave(evCallback);

Expand Down Expand Up @@ -95,7 +104,9 @@ describe('onPageLeave', () => {
expect(evCallback).toHaveBeenCalledWith(false);
});

it('should not fire the callback twice on beforeunload event', () => {
it('should not fire the callback twice on beforeunload event for IE11', () => {
(globalThis.navigator as any).userAgent =
'Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko';
const evCallback = jest.fn();
onPageLeave(evCallback);

Expand All @@ -106,6 +117,8 @@ describe('onPageLeave', () => {
});

it('should fire the callback only once if even multiple events are fired', () => {
(globalThis.navigator as any).userAgent =
'Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko';
const evCallback = jest.fn();
onPageLeave(evCallback);

Expand Down Expand Up @@ -166,7 +179,9 @@ describe('onPageLeave', () => {
expect(evCallback).toHaveBeenNthCalledWith(2, false);
});

it('should fire the callback on beforeunload event even after multiple visibility changes happen', () => {
it('should fire the callback on beforeunload event even after multiple visibility changes happen for IE11', () => {
(globalThis.navigator as any).userAgent =
'Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko';
const evCallback = jest.fn();
onPageLeave(evCallback);

Expand All @@ -184,7 +199,9 @@ describe('onPageLeave', () => {
expect(evCallback).toHaveBeenNthCalledWith(2, false);
});

it('should fire the callback on beforeunload event on the next tick even when the tab is inactive', () => {
it('should fire the callback on beforeunload event on the next tick even when the tab is inactive for IE11', () => {
(globalThis.navigator as any).userAgent =
'Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko';
const evCallback = jest.fn();
onPageLeave(evCallback);

Expand Down
8 changes: 7 additions & 1 deletion packages/analytics-js-common/src/utilities/detect.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { isString } from './checks';

/**
* Determines if the SDK is running inside a chrome extension
* @returns boolean
*/
const isSDKRunningInChromeExtension = (): boolean => !!(window as any).chrome?.runtime?.id;

export { isSDKRunningInChromeExtension };
const isIE11 = (): boolean =>
Comment thread
saikumarrs marked this conversation as resolved.
isString(globalThis.navigator.userAgent) &&
/Trident.*rv:11\./.test(globalThis.navigator.userAgent);

export { isSDKRunningInChromeExtension, isIE11 };
18 changes: 13 additions & 5 deletions packages/analytics-js-common/src/utilities/page.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isIE11 } from './detect';

const onPageLeave = (callback: (isAccessible: boolean) => void) => {
// To ensure the callback is only called once even if more than one events
// are fired at once.
Expand Down Expand Up @@ -25,12 +27,18 @@ const onPageLeave = (callback: (isAccessible: boolean) => void) => {
// Includes user actions like clicking a link, entering a new URL,
// refreshing the page, or closing the browser tab
// Note that 'pagehide' is not supported in IE.
// So, this is a fallback.
(globalThis as typeof window).addEventListener('beforeunload', () => {
isAccessible = false;
handleOnLeave();
});
// Registering this event conditionally for IE11 also because
// it affects bfcache optimization on modern browsers otherwise.
if (isIE11()) {
(globalThis as typeof window).addEventListener('beforeunload', () => {
isAccessible = false;
handleOnLeave();
});
}

// This is important for iOS Safari browser as it does not
// fire the regular pagehide and visibilitychange events
// when user goes to tablist view and closes the tab.
(globalThis as typeof window).addEventListener('blur', () => {
isAccessible = true;
handleOnLeave();
Expand Down
2 changes: 1 addition & 1 deletion packages/analytics-js/.size-limit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export default [
name: 'Core (Content Script) - Modern - NPM (CJS)',
path: 'dist/npm/modern/content-script/cjs/index.cjs',
import: '*',
limit: '41 KiB',
limit: '41.5 KiB',
},
{
name: 'Core (Content Script) - Modern - NPM (UMD)',
Expand Down
4 changes: 2 additions & 2 deletions packages/analytics-js/__tests__/app/RudderAnalytics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -801,8 +801,8 @@ describe('Core - Rudder Analytics Facade', () => {
const simulatePageBeingUnloadedAfterSDKLoad = (sdkLoaded = true) => {
// Simulate page being unloaded
state.lifecycle.loaded.value = sdkLoaded;
const event = new Event('beforeunload');
window.dispatchEvent(event);
const event = new Event('pagehide');
document.dispatchEvent(event);
};

it('should inherit properties of autoTrack for page lifecycle events', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { getTimezone } from '@rudderstack/analytics-js-common/utilities/timezone
import { isValidURL } from '@rudderstack/analytics-js-common/utilities/url';
import { isDefinedAndNotNull } from '@rudderstack/analytics-js-common/utilities/checks';
import type { IHttpClient } from '@rudderstack/analytics-js-common/types/HttpClient';
import { isIE11 } from '@rudderstack/analytics-js-common/utilities/detect';
Comment thread
saikumarrs marked this conversation as resolved.
import {
INVALID_POLYFILL_URL_WARNING,
POLYFILL_SCRIPT_LOAD_ERROR,
Expand All @@ -28,7 +29,6 @@ import {
hasBeacon,
hasCrypto,
hasUAClientHints,
isIE11,
isLegacyJSEngine,
isStorageAvailable,
} from './detection';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,4 @@ const hasBeacon = (): boolean =>
!isNullOrUndefined(globalThis.navigator.sendBeacon) &&
isFunction(globalThis.navigator.sendBeacon);

const isIE11 = (): boolean => Boolean(globalThis.navigator.userAgent.match(/Trident.*rv:11\./));

export { isBrowser, isNode, hasCrypto, hasUAClientHints, hasBeacon, isIE11 };
export { isBrowser, isNode, hasCrypto, hasUAClientHints, hasBeacon };
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { detectAdBlockers } from './adBlockers';
export { isBrowser, isNode, hasCrypto, hasUAClientHints, hasBeacon, isIE11 } from './browser';
export { isBrowser, isNode, hasCrypto, hasUAClientHints, hasBeacon } from './browser';
export { getUserAgentClientHint } from './clientHint';
export { isDatasetAvailable, legacyJSEngineRequiredPolyfills, isLegacyJSEngine } from './dom';
export { getScreenDetails } from './screen';
Expand Down
Loading