Skip to content

Commit f07f26c

Browse files
authored
fix: page view events lost due to cached session storage (#41)
1 parent 2483314 commit f07f26c

File tree

13 files changed

+106
-41
lines changed

13 files changed

+106
-41
lines changed

src/browser/BrowserInfo.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,17 @@ export class BrowserInfo {
7878
if (!BrowserInfo.isBrowser()) return '';
7979
return window.document.title ?? '';
8080
}
81+
82+
static isFromReload() {
83+
if (performance && performance.getEntriesByType) {
84+
const performanceEntries = performance.getEntriesByType('navigation');
85+
if (performanceEntries && performanceEntries.length > 0) {
86+
const type = (performanceEntries[0] as any)['type'];
87+
return type === 'reload';
88+
}
89+
} else {
90+
logger.warn('unsupported web environment for performance');
91+
}
92+
return false;
93+
}
8194
}

src/tracker/PageViewTracker.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,13 @@
1111
* and limitations under the License.
1212
*/
1313

14-
import { Logger } from '@aws-amplify/core';
1514
import { BaseTracker } from './BaseTracker';
1615
import { BrowserInfo } from '../browser';
1716
import { ClickstreamContext, ClickstreamProvider, Event } from '../provider';
1817
import { PageType } from '../types';
1918
import { MethodEmbed } from '../util/MethodEmbed';
2019
import { StorageUtil } from '../util/StorageUtil';
2120

22-
const logger = new Logger('PageViewTracker');
23-
2421
export class PageViewTracker extends BaseTracker {
2522
provider: ClickstreamProvider;
2623
context: ClickstreamContext;
@@ -36,22 +33,22 @@ export class PageViewTracker extends BaseTracker {
3633
if (this.context.configuration.pageType === PageType.SPA) {
3734
this.trackPageViewForSPA();
3835
} else {
39-
this.onPageChange();
36+
if (!BrowserInfo.isFromReload()) {
37+
this.onPageChange();
38+
}
4039
}
4140
}
4241

4342
trackPageViewForSPA() {
4443
MethodEmbed.add(history, 'pushState', this.onPageChange);
4544
MethodEmbed.add(history, 'replaceState', this.onPageChange);
4645
window.addEventListener('popstate', this.onPageChange);
47-
this.onPageChange();
46+
if (!BrowserInfo.isFromReload()) {
47+
this.onPageChange();
48+
}
4849
}
4950

5051
onPageChange() {
51-
if (!window.sessionStorage) {
52-
logger.warn('unsupported web environment for sessionStorage');
53-
return;
54-
}
5552
if (this.context.configuration.isTrackPageViewEvents) {
5653
const previousPageUrl = StorageUtil.getPreviousPageUrl();
5754
const previousPageTitle = StorageUtil.getPreviousPageTitle();

src/tracker/SessionTracker.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { BaseTracker } from './BaseTracker';
1515
import { Session } from './Session';
1616
import { BrowserInfo } from '../browser';
1717
import { Event } from '../provider';
18-
import { PageType } from '../types';
1918
import { StorageUtil } from '../util/StorageUtil';
2019

2120
const logger = new Logger('SessionTracker');
@@ -53,6 +52,7 @@ export class SessionTracker extends BaseTracker {
5352

5453
handleInit() {
5554
this.session = Session.getCurrentSession(this.context);
55+
StorageUtil.clearPageInfo();
5656
if (StorageUtil.getIsFirstOpen()) {
5757
this.provider.record({
5858
name: Event.PresetEvent.FIRST_OPEN,
@@ -71,8 +71,8 @@ export class SessionTracker extends BaseTracker {
7171
pageViewTracker.setIsEntrances();
7272
this.provider.record({ name: Event.PresetEvent.SESSION_START });
7373
}
74-
if (isFirstTime && this.isMultiPageApp() && this.isFromCurrentHost())
75-
return;
74+
if (isFirstTime && this.isFromCurrentHost()) return;
75+
if (isFirstTime && BrowserInfo.isFromReload()) return;
7676
this.provider.record({
7777
name: Event.PresetEvent.APP_START,
7878
attributes: {
@@ -85,10 +85,6 @@ export class SessionTracker extends BaseTracker {
8585
return window.location.host === this.context.browserInfo.latestReferrerHost;
8686
}
8787

88-
isMultiPageApp() {
89-
return this.context.configuration.pageType === PageType.multiPageApp;
90-
}
91-
9288
onPageHide() {
9389
logger.debug('page hide');
9490
this.storeSession();

src/types/Analytics.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export interface Item {
7272
category3?: string;
7373
category4?: string;
7474
category5?: string;
75+
7576
[key: string]: string | number | boolean | null;
7677
}
7778

src/util/StorageUtil.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,20 +255,25 @@ export class StorageUtil {
255255
localStorage.setItem(StorageUtil.isFirstOpenKey, '0');
256256
}
257257

258+
static clearPageInfo() {
259+
localStorage.setItem(StorageUtil.previousPageUrlKey, '');
260+
localStorage.setItem(StorageUtil.previousPageTitleKey, '');
261+
}
262+
258263
static getPreviousPageUrl(): string {
259-
return sessionStorage.getItem(StorageUtil.previousPageUrlKey) ?? '';
264+
return localStorage.getItem(StorageUtil.previousPageUrlKey) ?? '';
260265
}
261266

262267
static savePreviousPageUrl(url: string) {
263-
sessionStorage.setItem(StorageUtil.previousPageUrlKey, url);
268+
localStorage.setItem(StorageUtil.previousPageUrlKey, url);
264269
}
265270

266271
static getPreviousPageTitle(): string {
267-
return sessionStorage.getItem(StorageUtil.previousPageTitleKey) ?? '';
272+
return localStorage.getItem(StorageUtil.previousPageTitleKey) ?? '';
268273
}
269274

270275
static savePreviousPageTitle(title: string) {
271-
sessionStorage.setItem(StorageUtil.previousPageTitleKey, title);
276+
localStorage.setItem(StorageUtil.previousPageTitleKey, title);
272277
}
273278

274279
static getPreviousPageStartTime(): number {

test/browser/BrowserInfo.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
1111
* and limitations under the License.
1212
*/
13+
import { setPerformanceEntries } from './BrowserUtil';
14+
import { MockObserver } from './MockObserver';
1315
import { BrowserInfo } from '../../src/browser';
1416

1517
describe('BrowserInfo test', () => {
@@ -93,4 +95,20 @@ describe('BrowserInfo test', () => {
9395
const isFirefox = BrowserInfo.isFirefox();
9496
expect(isFirefox).toBeFalsy();
9597
});
98+
99+
test('test unsupported web environment for performance', () => {
100+
expect(BrowserInfo.isFromReload()).toBeFalsy();
101+
});
102+
103+
test('test web page from reload', () => {
104+
(global as any).PerformanceObserver = MockObserver;
105+
setPerformanceEntries();
106+
expect(BrowserInfo.isFromReload()).toBeFalsy();
107+
});
108+
109+
test('test web page not from reload', () => {
110+
(global as any).PerformanceObserver = MockObserver;
111+
setPerformanceEntries(true, true);
112+
expect(BrowserInfo.isFromReload()).toBeTruthy();
113+
});
96114
});

test/browser/BrowserUtil.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,18 @@ export function setUpBrowserPerformance() {
1717
setPerformanceEntries(false);
1818
}
1919

20-
export function setPerformanceEntries(isLoaded = true) {
20+
export function setPerformanceEntries(isLoaded = true, isReload = false) {
2121
Object.defineProperty(window, 'performance', {
2222
writable: true,
2323
value: {
2424
getEntriesByType: jest
2525
.fn()
2626
.mockImplementation(
27-
isLoaded ? getEntriesByType : getEntriesByTypeUnload
27+
isLoaded
28+
? isReload
29+
? getEntriesByTypeForReload
30+
: getEntriesByType
31+
: getEntriesByTypeUnload
2832
),
2933
},
3034
});
@@ -78,14 +82,22 @@ function getEntriesByType(): PerformanceEntryList {
7882
domComplete: 3440.4000000059605,
7983
loadEventStart: 3444.2000000178814,
8084
loadEventEnd: 3444.4000000059605,
81-
type: 'reload',
85+
type: 'navigate',
8286
redirectCount: 0,
8387
activationStart: 0,
8488
criticalCHRestart: 0,
8589
},
8690
]);
8791
}
8892

93+
function getEntriesByTypeForReload(): PerformanceEntryList {
94+
return <PerformanceEntry[]>(<unknown>[
95+
{
96+
type: 'reload',
97+
},
98+
]);
99+
}
100+
89101
function getEntriesByTypeUnload(): PerformanceEntryList {
90102
return <PerformanceEntry[]>(<unknown>[
91103
{

test/tracker/ClickTracker.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ describe('ClickTracker test', () => {
2828
let recordMethodMock: any;
2929

3030
beforeEach(() => {
31-
sessionStorage.clear();
3231
localStorage.clear();
3332
provider = new ClickstreamProvider();
3433
Object.assign(provider.configuration, {

test/tracker/PageLoadTracker.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ describe('PageLoadTracker test', () => {
2929
let recordMethodMock: any;
3030

3131
beforeEach(() => {
32-
sessionStorage.clear();
3332
localStorage.clear();
3433
provider = new ClickstreamProvider();
3534
Object.assign(provider.configuration, {

test/tracker/PageViewTracker.test.ts

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import {
2828
import { PageViewTracker, Session, SessionTracker } from '../../src/tracker';
2929
import { MethodEmbed } from '../../src/util/MethodEmbed';
3030
import { StorageUtil } from '../../src/util/StorageUtil';
31+
import { setPerformanceEntries } from "../browser/BrowserUtil";
32+
import { MockObserver } from "../browser/MockObserver";
3133

3234
global.TextEncoder = TextEncoder;
3335
global.TextDecoder = TextDecoder;
@@ -45,7 +47,6 @@ describe('PageViewTracker test', () => {
4547

4648
beforeEach(() => {
4749
localStorage.clear();
48-
sessionStorage.clear();
4950
provider = new ClickstreamProvider();
5051

5152
Object.assign(provider.configuration, {
@@ -74,6 +75,8 @@ describe('PageViewTracker test', () => {
7475
writable: true,
7576
value: 'index',
7677
});
78+
(global as any).PerformanceObserver = MockObserver;
79+
setPerformanceEntries()
7780
});
7881

7982
afterEach(() => {
@@ -149,20 +152,6 @@ describe('PageViewTracker test', () => {
149152
expect(userEngagementMock).not.toBeCalled();
150153
});
151154

152-
test('test environment is not supported for sessionStorage', () => {
153-
const sessionStorage = window.sessionStorage;
154-
Object.defineProperty(window, 'sessionStorage', {
155-
writable: true,
156-
value: undefined,
157-
});
158-
pageViewTracker.setUp();
159-
expect(recordEventMethodMock).not.toBeCalled();
160-
Object.defineProperty(window, 'sessionStorage', {
161-
writable: true,
162-
value: sessionStorage,
163-
});
164-
});
165-
166155
test('test two page view in SPA mode', async () => {
167156
(context.configuration as any).pageType = PageType.SPA;
168157
pageViewTracker.setUp();

0 commit comments

Comments
 (0)