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

Allow for usage of snowplow callback without this (close #1085) #1408

Merged
merged 1 commit into from
Jan 24, 2025
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
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@snowplow/javascript-tracker",
"comment": "Allow usage of Snowplow callback without 'this' keyword",
"type": "none"
}
],
"packageName": "@snowplow/javascript-tracker"
}
10 changes: 9 additions & 1 deletion trackers/javascript-tracker/src/in_queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,15 @@ export function InQueueManager(functionName: string, asyncQueue: Array<unknown>)
// Strip GlobalSnowplowNamespace from ID
fnTrackers[tracker.id.replace(`${functionName}_`, '')] = tracker;
}
input.apply(fnTrackers, parameterArray);

// Create a new array from `parameterArray` to avoid mutating the original
const parameterArrayCopy = Array.prototype.slice.call(parameterArray);

// Add the `fnTrackers` object as the last element of the new array to allow it to be accessed in the callback
// as the final argument, useful in environments that don't support `this` (GTM)
const args = Array.prototype.concat.apply(parameterArrayCopy, [fnTrackers]);

input.apply(fnTrackers, args);
} catch (ex) {
LOG.error('Tracker callback failed', ex);
} finally {
Expand Down
61 changes: 61 additions & 0 deletions trackers/javascript-tracker/test/unit/in_queue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,34 @@ describe('InQueueManager', () => {
asyncQueue.push(['updatePageActivity:firstTracker;secondTracker']);
expect(output).toEqual(29);
});
});

describe('Snowplow callback', () => {
const asyncQueue = InQueueManager('callback', []);
const mockTrackers: Record<string, any> = {};

let userId: string | null | undefined;

const newTracker = (trackerId: string): any => {
return {
id: trackerId,
setUserId: function (s?: string | null) {
userId = s;
},
getUserId: function () {
return userId;
},
};
};

beforeEach(() => {
const tracker = newTracker('sp1');
mockTrackers.sp1 = tracker;
});

afterEach(() => {
delete mockTrackers.sp1;
});

it('Execute a user-defined custom callback', () => {
let callbackExecuted = false;
Expand All @@ -183,4 +211,37 @@ describe('InQueueManager', () => {
]);
}).not.toThrow();
});

it('A custom callback with arguments provided will pass those arguments into the callback parameters', () => {
asyncQueue.push([
function (a: number, b: number) {
expect(a).toEqual(1);
expect(b).toEqual(2);
},
1,
2,
]);
});

it('The callback will be passed the tracker dictionary as the argument if there is only one parameter', () => {
asyncQueue.push([
function (trackers: Record<string, any>) {
const tracker = trackers.sp1;
expect(tracker).toEqual(mockTrackers.callback_sp1);
},
]);
});

it('The callback can access the tracker dictionary using both `this` and the last argument, along with arguments', () => {
asyncQueue.push([
function (this: any, a: number, b: number, trackers: Record<string, any>) {
expect(this.sp1).toEqual(mockTrackers.callback_sp1);
expect(a).toEqual(1);
expect(b).toEqual(2);
expect(trackers.sp1).toEqual(mockTrackers.callback_sp1);
},
1,
2,
]);
});
});
Loading