diff --git a/src/tracker.test.ts b/src/tracker.test.ts index c9ec6e4..ad9907f 100644 --- a/src/tracker.test.ts +++ b/src/tracker.test.ts @@ -53,6 +53,8 @@ describe('TESTING Tracker', () => { describe('WHEN user.identify is called', () => { beforeEach(async () => { + jest.useFakeTimers(); + await tracker.user.identify({ id: 'test-user-id', email: 'test@example.com', @@ -70,6 +72,10 @@ describe('TESTING Tracker', () => { }); }); + afterEach(() => { + jest.useRealTimers(); + }); + it('SHOULD send the request to the default trackingApiBaseUrl', async () => { expect(fetch).toHavePostedTimes( 1, @@ -89,6 +95,52 @@ describe('TESTING Tracker', () => { first_name: 'test', last_name: 'test', }, + extras: { + created_at: new Date().toISOString(), + }, + }, + }, + ); + }); + }); + + describe('WHEN user.identify is called with extras', () => { + beforeEach(async () => { + jest.useFakeTimers(); + + await tracker.user.identify({ + id: 'test-user-id', + email: 'test@example.com', + data: { + first_name: 'test', + }, + extras: { + createdAt: '2024-01-01T00:00:00.000Z', + updateOnly: true, + }, + }); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('SHOULD send extras as snake_case fields', async () => { + expect(fetch).toHavePostedTimes( + 1, + 'https://api.getvero.com/api/v2/users/track?tracking_api_key=test-api-key', + { + body: { + id: 'test-user-id', + email: 'test@example.com', + channels: [], + data: { + first_name: 'test', + }, + extras: { + created_at: '2024-01-01T00:00:00.000Z', + update_only: true, + }, }, }, ); @@ -263,6 +315,8 @@ describe('TESTING Tracker', () => { describe('WHEN user.identify is called', () => { beforeEach(async () => { + jest.useFakeTimers(); + await tracker.user.identify({ id: 'test-user-id', email: 'test@example.com', @@ -280,6 +334,10 @@ describe('TESTING Tracker', () => { }); }); + afterEach(() => { + jest.useRealTimers(); + }); + it('SHOULD send the request to the specified trackingApiBaseUrl', () => { expect(fetch).toHavePostedTimes( 1, @@ -299,6 +357,9 @@ describe('TESTING Tracker', () => { first_name: 'test', last_name: 'test', }, + extras: { + created_at: new Date().toISOString(), + }, }, }, ); @@ -765,6 +826,9 @@ describe('TESTING Tracker', () => { userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36', }, + extras: { + created_at: new Date().toISOString(), + }, }, }, ); @@ -815,6 +879,81 @@ describe('TESTING Tracker', () => { }); }); + describe('WHEN user.identify is called with extras', () => { + beforeEach(async () => { + jest.useFakeTimers(); + + await tracker.user.identify({ + id: 'test-user-id', + email: 'test@example.com', + data: { + first_name: 'test', + }, + extras: { + createdAt: '2024-01-01T00:00:00.000Z', + updateOnly: true, + }, + }); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('SHOULD send extras on the identify request only', async () => { + expect(fetch).toHavePostedTimes( + 1, + 'https://api.getvero.com/api/v2/users/track?tracking_api_key=test-api-key', + { + body: { + id: 'test-user-id', + email: 'test@example.com', + channels: [], + data: { + first_name: 'test', + language: 'en', + timezone: 10, + ianaTimezone: 'Australia/Sydney', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36', + }, + extras: { + created_at: '2024-01-01T00:00:00.000Z', + update_only: true, + }, + }, + }, + ); + }); + + it('SHOULD still track a "visited site" event with event extras', async () => { + expect(fetch).toHavePostedTimes( + 1, + 'https://api.getvero.com/api/v2/events/track?tracking_api_key=test-api-key', + { + body: { + identity: { + id: 'test-user-id', + email: 'test@example.com', + }, + event_name: 'Visited site', + data: { + language: 'en', + timezone: 10, + ianaTimezone: 'Australia/Sydney', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36', + }, + extras: { + source: 'hostname.example.com', + created_at: new Date().toISOString(), + }, + }, + }, + ); + }); + }); + describe('WHEN user.unidentify is called', () => { beforeEach(async () => { await tracker.user.unidentify(); diff --git a/src/tracker.ts b/src/tracker.ts index a41c44e..766ce5a 100644 --- a/src/tracker.ts +++ b/src/tracker.ts @@ -139,6 +139,22 @@ export interface UserIdentifyRequest extends NoSiteVisitEventRequest { * @example { first_name: "Damien", last_name: "Brzoska", age: 30 } */ data?: Record; + /** + * An object containing key value pairs that represent the reserved, + * Vero-specific `created_at` and `update_only` properties. + * + * @see {@link Tracker#user.identify} + */ + extras?: { + /** + * @example "2023-05-30T04:46:31+0000" + */ + createdAt?: string; + /** + * @example true + */ + updateOnly?: boolean; + }; } export interface OptionalUserIdRequest { @@ -318,6 +334,10 @@ class Tracker { email: request.email, channels: request.channels ?? [], data: { ...request.data, ...getDefaultReservedUserData() }, + extras: { + created_at: request.extras?.createdAt ?? new Date().toISOString(), + update_only: request.extras?.updateOnly, + }, }); this.identityStore?.save(request.id, request.email);