Skip to content
Closed
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
2 changes: 1 addition & 1 deletion src/Rokt-Kit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -884,7 +884,7 @@ class RoktKit implements KitInterface {
const pageView = buildPageEvent(event);
pageViews.push(pageView);

if (!writePageViews(pageViews)) {
if (writePageViews(pageViews) === 0) {
const reason = isLocalStorageAvailable() ? 'quota' : 'ls_unavailable';
this.loggingService?.log({
message: `Rokt Kit: Failed to persist page view for ${pageUrl} [reason: ${reason}]`,
Expand Down
11 changes: 9 additions & 2 deletions src/pageViewStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,15 @@ export function loadPageViews(loggingService: LoggingService | null): PageEvent[
return Array.isArray(stored) ? (stored as PageEvent[]) : [];
}

export function writePageViews(pageViews: PageEvent[]): boolean {
return writeNamespacedField(LS_NAMESPACE_KEY, LS_PAGE_VIEWS_FIELD, capPageViews(pageViews));
export function writePageViews(pageViews: PageEvent[]): number {
const views = capPageViews(pageViews);
for (let i = 0; i < views.length; i++) {
const toWrite = views.slice(i);
if (writeNamespacedField(LS_NAMESPACE_KEY, LS_PAGE_VIEWS_FIELD, toWrite)) {
return toWrite.length;
}
}
return 0;
}

export function clearPageViews(): void {
Expand Down
64 changes: 61 additions & 3 deletions test/src/pageViewStorage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,19 +112,77 @@ describe('pageViewStorage', () => {
});

describe('writePageViews', () => {
it('persists the page views and returns true', () => {
expect(writePageViews([pageView('home')])).toBe(true);
it('persists the page views and returns the stored count', () => {
expect(writePageViews([pageView('home')])).toBe(1);
expect(loadPageViews(null)).toEqual([pageView('home')]);
});

it('keeps only the 25 most-recent views when given more than 25', () => {
const views = Array.from({ length: 40 }, (_, i) => pageView('page-' + i));
expect(writePageViews(views)).toBe(true);
expect(writePageViews(views)).toBe(25);
const stored = loadPageViews(null);
expect(stored).toHaveLength(25);
expect(stored[0].sourceMessageId).toBe('page-15');
expect(stored[24].sourceMessageId).toBe('page-39');
});

it('evicts oldest records and retries when the initial write fails due to quota', () => {
const views = Array.from({ length: 5 }, (_, i) => pageView('page-' + i));
const original = Storage.prototype.setItem;
let calls = 0;
vi.spyOn(Storage.prototype, 'setItem').mockImplementation(function (this: Storage, key: string, value: string) {
if (++calls === 1) throw new DOMException('quota', 'QuotaExceededError');
original.call(this, key, value);
});

expect(writePageViews(views)).toBe(4);
const stored = loadPageViews(null);
expect(stored).toHaveLength(4);
expect(stored[0].sourceMessageId).toBe('page-1');
expect(stored[3].sourceMessageId).toBe('page-4');
});

it('evicts oldest records across multiple failed writes until one succeeds', () => {
const views = Array.from({ length: 5 }, (_, i) => pageView('page-' + i));
const original = Storage.prototype.setItem;
let calls = 0;
vi.spyOn(Storage.prototype, 'setItem').mockImplementation(function (this: Storage, key: string, value: string) {
if (++calls <= 3) throw new DOMException('quota', 'QuotaExceededError');
original.call(this, key, value);
});

expect(writePageViews(views)).toBe(2);
const stored = loadPageViews(null);
expect(stored).toHaveLength(2);
expect(stored[0].sourceMessageId).toBe('page-3');
expect(stored[1].sourceMessageId).toBe('page-4');
});

it('evicts the oldest record from pre-existing storage when quota is tight on the next write', () => {
const existing = Array.from({ length: 5 }, (_, i) => pageView('existing-' + i));
writePageViews(existing); // seed storage

const updated = [...existing, pageView('new')];
const original = Storage.prototype.setItem;
let calls = 0;
vi.spyOn(Storage.prototype, 'setItem').mockImplementation(function (this: Storage, key: string, value: string) {
if (++calls === 1) throw new DOMException('quota', 'QuotaExceededError');
original.call(this, key, value);
});

expect(writePageViews(updated)).toBe(5);
const stored = loadPageViews(null);
expect(stored).toHaveLength(5);
expect(stored[0].sourceMessageId).toBe('existing-1');
expect(stored[4].sourceMessageId).toBe('new');
});

it('returns false when every write attempt fails', () => {
vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {
throw new DOMException('quota', 'QuotaExceededError');
});
expect(writePageViews([pageView('home')])).toBe(0);
});
});

describe('buildPageEvents', () => {
Expand Down
Loading