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
117 changes: 116 additions & 1 deletion spec/src/modules/tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -5352,6 +5352,121 @@ describe(`ConstructorIO - Tracker${bundledDescriptionSuffix}`, () => {

expect(tracker.trackRecommendationClick(requiredParameters)).to.equal(true);
});

it('Should respond with a valid response when seedItemIds is provided as an array of strings', (done) => {
const { tracker } = new ConstructorIO({
apiKey: testApiKey,
fetch: fetchSpy,
...requestQueueOptions,
});
const parametersWithSeedItemIds = {
...requiredParameters,
seedItemIds: ['item-123', 'item-456', 'item-789'],
};
tracker.on('success', (responseParams) => {
const requestParams = helpers.extractBodyParamsFromFetch(fetchSpy);
// Request
expect(fetchSpy).to.have.been.called;
expect(requestParams).to.have.property('seed_item_ids').to.deep.equal(['item-123', 'item-456', 'item-789']);
// Response
expect(responseParams).to.have.property('method').to.equal('POST');
expect(responseParams).to.have.property('message');
done();
});
expect(tracker.trackRecommendationClick(parametersWithSeedItemIds)).to.equal(true);
});

it('Should respond with a valid response and convert seedItemIds to array when provided as string', (done) => {
const { tracker } = new ConstructorIO({
apiKey: testApiKey,
fetch: fetchSpy,
...requestQueueOptions,
});
const parametersWithSeedItemIds = {
...requiredParameters,
seedItemIds: 'item-123',
};
tracker.on('success', (responseParams) => {
const requestParams = helpers.extractBodyParamsFromFetch(fetchSpy);
// Request
expect(fetchSpy).to.have.been.called;
expect(requestParams).to.have.property('seed_item_ids').to.deep.equal(['item-123']);
// Response
expect(responseParams).to.have.property('method').to.equal('POST');
expect(responseParams).to.have.property('message');
done();
});
expect(tracker.trackRecommendationClick(parametersWithSeedItemIds)).to.equal(true);
});

it('Should respond with a valid response and convert seedItemIds to array when provided as number', (done) => {
const { tracker } = new ConstructorIO({
apiKey: testApiKey,
fetch: fetchSpy,
...requestQueueOptions,
});
const parametersWithSeedItemIds = {
...requiredParameters,
seedItemIds: 123,
};
tracker.on('success', (responseParams) => {
const requestParams = helpers.extractBodyParamsFromFetch(fetchSpy);
// Request
expect(fetchSpy).to.have.been.called;
expect(requestParams).to.have.property('seed_item_ids').to.deep.equal(['123']);
// Response
expect(responseParams).to.have.property('method').to.equal('POST');
expect(responseParams).to.have.property('message');
done();
});
expect(tracker.trackRecommendationClick(parametersWithSeedItemIds)).to.equal(true);
});

it('Should respond with a valid response and omit seed_item_ids when seedItemIds is empty string', (done) => {
const { tracker } = new ConstructorIO({
apiKey: testApiKey,
fetch: fetchSpy,
...requestQueueOptions,
});
const parametersWithSeedItemIds = {
...requiredParameters,
seedItemIds: '',
};
tracker.on('success', (responseParams) => {
const requestParams = helpers.extractBodyParamsFromFetch(fetchSpy);
// Request
expect(fetchSpy).to.have.been.called;
expect(requestParams).to.not.have.property('seed_item_ids');
// Response
expect(responseParams).to.have.property('method').to.equal('POST');
expect(responseParams).to.have.property('message');
done();
});
expect(tracker.trackRecommendationClick(parametersWithSeedItemIds)).to.equal(true);
});

it('Should respond with a valid response and omit seed_item_ids when seedItemIds is empty array', (done) => {
const { tracker } = new ConstructorIO({
apiKey: testApiKey,
fetch: fetchSpy,
...requestQueueOptions,
});
const parametersWithSeedItemIds = {
...requiredParameters,
seedItemIds: [],
};
tracker.on('success', (responseParams) => {
const requestParams = helpers.extractBodyParamsFromFetch(fetchSpy);
// Request
expect(fetchSpy).to.have.been.called;
expect(requestParams).to.not.have.property('seed_item_ids');
// Response
expect(responseParams).to.have.property('method').to.equal('POST');
expect(responseParams).to.have.property('message');
done();
});
expect(tracker.trackRecommendationClick(parametersWithSeedItemIds)).to.equal(true);
});
});

describe('trackBrowseResultsLoaded', () => {
Expand Down Expand Up @@ -8385,7 +8500,7 @@ describe(`ConstructorIO - Tracker${bundledDescriptionSuffix}`, () => {
it('Should throw an error when providing no messageType parameter', () => {
const { tracker } = new ConstructorIO({ apiKey: testApiKey });

expect(tracker.on(null, () => {})).to.be.an('error');
expect(tracker.on(null, () => { })).to.be.an('error');
});

it('Should throw an error when providing an invalid callback parameter', () => {
Expand Down
11 changes: 11 additions & 0 deletions src/modules/tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,7 @@ class Tracker {
* @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
* @param {string} [parameters.slCampaignId] - Pass campaign id of sponsored listing
* @param {string} [parameters.slCampaignOwner] - Pass campaign owner of sponsored listing
* @param {string[]|string|number} [parameters.seedItemIds] - Item ID(s) to be used as seed
Copy link
Contributor

@jjl014 jjl014 Aug 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we keep this as string[]|string to stay consistent with the results fn?

EDIT: Nvm, it looks like we did this for the view event as well. Feel free to ignore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually made it just string[] at first to have a tighter type but we already used this type in view event so I didn't want to make it inconsistent

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha. I was thinking of having it just be string[] as well when I first looked over things, but makes sense to leave it to keep things consistent with the other event

* @returns {(true|Error)}
* @description User clicked an item that appeared within a list of recommended results
* @example
Expand All @@ -1372,6 +1373,7 @@ class Tracker {
* strategyId: 'complimentary',
* itemId: 'KMH876',
* itemName: 'Socks',
* seedItemIds: ['item-123', 'item-456'],
* },
* );
*/
Expand Down Expand Up @@ -1405,6 +1407,7 @@ class Tracker {
analyticsTags,
slCampaignId,
slCampaignOwner,
seedItemIds,
} = parameters;

if (variationId) {
Expand Down Expand Up @@ -1463,6 +1466,14 @@ class Tracker {
bodyParams.sl_campaign_owner = slCampaignOwner;
}

if (typeof seedItemIds === 'number') {
bodyParams.seed_item_ids = [String(seedItemIds)];
} else if (seedItemIds?.length && typeof seedItemIds === 'string') {
bodyParams.seed_item_ids = [seedItemIds];
} else if (seedItemIds?.length && Array.isArray(seedItemIds)) {
bodyParams.seed_item_ids = seedItemIds;
}

const requestURL = `${requestPath}${applyParamsAsString({}, this.options)}`;
const requestMethod = 'POST';
const requestBody = applyParams(bodyParams, { ...this.options, requestMethod });
Expand Down
1 change: 1 addition & 0 deletions src/types/tracker.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ declare class Tracker {
analyticsTags?: Record<string, string>;
slCampaignId?: string;
slCampaignOwner?: string;
seedItemIds?: string[] | string | number;
},
networkParameters?: NetworkParameters
): true | Error;
Expand Down
Loading