Skip to content

Commit 8ee9def

Browse files
committed
[REM-2574] Add trackMediaImpressionClick function with tests and types
1 parent e8ec643 commit 8ee9def

File tree

3 files changed

+300
-0
lines changed

3 files changed

+300
-0
lines changed

spec/src/modules/tracker.js

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15772,4 +15772,227 @@ describe(`ConstructorIO - Tracker${bundledDescriptionSuffix}`, () => {
1577215772
).to.equal(true);
1577315773
});
1577415774
});
15775+
15776+
describe('trackMediaImpressionClick', () => {
15777+
const testApiKeyWithAdPlacements = 'u7PNVQx-prod-en-us';
15778+
const requiredParameters = {
15779+
bannerAdId: 'AszgOLr3pCZheI0Bx7rNtSraeaN6o3IgNNWvUgan/LqMsf0pTVFaHqjDjWNj1Gz5+IfGOQOs6XOYcWVykjsYSphHF3j04TsXVAlkd2VorLK3dg39SsLiv8mOEVA6TcuBSXAmGLXZCyTmCRjD8JG6QXNEr5qWC073V6CwJRT/XnUYJ/8fiosWNIpNiv5z9VtocQLqILszRllLEMpuGFdXu2HS',
15780+
placementId: 'home-page-top-banner',
15781+
};
15782+
15783+
const optionalParameters = {
15784+
analyticsTags: testAnalyticsTag,
15785+
};
15786+
15787+
it('Should respond with a valid response when required parameters are provided', (done) => {
15788+
const { tracker } = new ConstructorIO({
15789+
apiKey: testApiKeyWithAdPlacements,
15790+
fetch: fetchSpy,
15791+
mediaServiceUrl: 'https://dev-behavior.media-cnstrc.com',
15792+
...requestQueueOptions,
15793+
});
15794+
15795+
tracker.on('success', (responseParams) => {
15796+
const requestParams = helpers.extractBodyParamsFromFetch(fetchSpy);
15797+
// Request
15798+
expect(fetchSpy).to.have.been.called;
15799+
expect(requestParams).to.have.property('key');
15800+
expect(requestParams).to.have.property('i');
15801+
expect(requestParams).to.have.property('s');
15802+
expect(requestParams).to.have.property('c').to.equal(clientVersion);
15803+
expect(requestParams).to.have.property('_dt');
15804+
expect(requestParams).to.have.property('canonical_url').to.equal(canonicalUrl);
15805+
expect(requestParams).to.have.property('document_referrer').to.equal(referrer);
15806+
expect(requestParams)
15807+
.to.have.property('banner_ad_id')
15808+
.to.equal(requiredParameters.bannerAdId);
15809+
expect(requestParams)
15810+
.to.have.property('placement_id')
15811+
.to.equal(requiredParameters.placementId);
15812+
validateOriginReferrer(requestParams);
15813+
15814+
// Response
15815+
expect(responseParams).to.have.property('method').to.equal('POST');
15816+
expect(responseParams).to.have.property('message');
15817+
15818+
done();
15819+
});
15820+
15821+
expect(tracker.trackMediaImpressionClick(requiredParameters)).to.equal(
15822+
true,
15823+
);
15824+
});
15825+
15826+
it('Should respond with a valid response when required and optional parameters are provided', (done) => {
15827+
const { tracker } = new ConstructorIO({
15828+
apiKey: testApiKeyWithAdPlacements,
15829+
fetch: fetchSpy,
15830+
mediaServiceUrl: 'https://dev-behavior.media-cnstrc.com',
15831+
...requestQueueOptions,
15832+
});
15833+
15834+
tracker.on('success', (responseParams) => {
15835+
const requestParams = helpers.extractBodyParamsFromFetch(fetchSpy);
15836+
15837+
// Request
15838+
expect(fetchSpy).to.have.been.called;
15839+
expect(requestParams)
15840+
.to.have.property('analytics_tags')
15841+
.to.deep.equal(testAnalyticsTag);
15842+
15843+
// Response
15844+
expect(responseParams).to.have.property('method').to.equal('POST');
15845+
expect(responseParams).to.have.property('message');
15846+
15847+
done();
15848+
});
15849+
15850+
expect(
15851+
tracker.trackMediaImpressionClick(
15852+
Object.assign(requiredParameters, optionalParameters),
15853+
),
15854+
).to.equal(true);
15855+
});
15856+
15857+
it('Should throw an error when invalid parameters are provided', () => {
15858+
const { tracker } = new ConstructorIO({ apiKey: testApiKey });
15859+
15860+
expect(tracker.trackMediaImpressionClick([])).to.be.an('error');
15861+
});
15862+
15863+
it('Should throw an error when no parameters are provided', () => {
15864+
const { tracker } = new ConstructorIO({ apiKey: testApiKey });
15865+
15866+
expect(tracker.trackMediaImpressionClick()).to.be.an('error');
15867+
});
15868+
15869+
it('Should send along origin_referrer query param if sendReferrerWithTrackingEvents is true', (done) => {
15870+
const { tracker } = new ConstructorIO({
15871+
apiKey: testApiKeyWithAdPlacements,
15872+
fetch: fetchSpy,
15873+
sendReferrerWithTrackingEvents: true,
15874+
mediaServiceUrl: 'https://dev-behavior.media-cnstrc.com',
15875+
...requestQueueOptions,
15876+
});
15877+
15878+
tracker.on('success', (responseParams) => {
15879+
const requestParams = helpers.extractUrlParamsFromFetch(fetchSpy);
15880+
15881+
// Request
15882+
expect(fetchSpy).to.have.been.called;
15883+
validateOriginReferrer(requestParams);
15884+
15885+
// Response
15886+
expect(responseParams).to.have.property('method').to.equal('POST');
15887+
expect(responseParams).to.have.property('message').to.equal('ok');
15888+
15889+
done();
15890+
});
15891+
15892+
expect(tracker.trackMediaImpressionClick(requiredParameters)).to.equal(
15893+
true,
15894+
);
15895+
});
15896+
15897+
it('Should not send along origin_referrer query param if sendReferrerWithTrackingEvents is false', (done) => {
15898+
const { tracker } = new ConstructorIO({
15899+
apiKey: testApiKeyWithAdPlacements,
15900+
fetch: fetchSpy,
15901+
sendReferrerWithTrackingEvents: false,
15902+
mediaServiceUrl: 'https://dev-behavior.media-cnstrc.com',
15903+
...requestQueueOptions,
15904+
});
15905+
15906+
tracker.on('success', (responseParams) => {
15907+
const requestParams = helpers.extractUrlParamsFromFetch(fetchSpy);
15908+
15909+
// Request
15910+
expect(fetchSpy).to.have.been.called;
15911+
expect(requestParams).to.not.have.property('origin_referrer');
15912+
15913+
// Response
15914+
expect(responseParams).to.have.property('method').to.equal('POST');
15915+
expect(responseParams).to.have.property('message').to.equal('ok');
15916+
15917+
done();
15918+
});
15919+
15920+
expect(tracker.trackMediaImpressionClick(requiredParameters)).to.equal(
15921+
true,
15922+
);
15923+
});
15924+
15925+
if (!skipNetworkTimeoutTests) {
15926+
it('Should be rejected when network request timeout is provided and reached', (done) => {
15927+
const { tracker } = new ConstructorIO({
15928+
apiKey: testApiKeyWithAdPlacements,
15929+
mediaServiceUrl: 'https://dev-behavior.media-cnstrc.com',
15930+
...requestQueueOptions,
15931+
});
15932+
15933+
tracker.on('error', ({ message }) => {
15934+
expect(message).to.equal(timeoutRejectionMessage);
15935+
done();
15936+
});
15937+
15938+
expect(
15939+
tracker.trackMediaImpressionClick(requiredParameters, { timeout: 10 }),
15940+
).to.equal(true);
15941+
});
15942+
15943+
it('Should be rejected when global network request timeout is provided and reached', (done) => {
15944+
const { tracker } = new ConstructorIO({
15945+
apiKey: testApiKeyWithAdPlacements,
15946+
mediaServiceUrl: 'https://dev-behavior.media-cnstrc.com',
15947+
networkParameters: {
15948+
timeout: 20,
15949+
},
15950+
...requestQueueOptions,
15951+
});
15952+
15953+
tracker.on('error', ({ message }) => {
15954+
expect(message).to.equal(timeoutRejectionMessage);
15955+
done();
15956+
});
15957+
15958+
expect(tracker.trackMediaImpressionClick(requiredParameters)).to.equal(
15959+
true,
15960+
);
15961+
});
15962+
}
15963+
15964+
it('Should properly transform non-breaking spaces in parameters', (done) => {
15965+
const breakingSpaces = '   ';
15966+
const userId = `user-id ${breakingSpaces} user-id`;
15967+
const userIdExpected = 'user-id user-id';
15968+
const { tracker } = new ConstructorIO({
15969+
apiKey: testApiKeyWithAdPlacements,
15970+
userId,
15971+
mediaServiceUrl: 'https://dev-behavior.media-cnstrc.com',
15972+
fetch: fetchSpy,
15973+
...requestQueueOptions,
15974+
});
15975+
15976+
tracker.on('success', (responseParams) => {
15977+
const requestParams = helpers.extractBodyParamsFromFetch(fetchSpy);
15978+
15979+
// Request
15980+
expect(fetchSpy).to.have.been.called;
15981+
expect(requestParams).to.have.property('ui').to.equal(userIdExpected);
15982+
15983+
// Response
15984+
expect(responseParams).to.have.property('method').to.equal('POST');
15985+
expect(responseParams).to.have.property('message').to.equal('ok');
15986+
15987+
done();
15988+
});
15989+
15990+
expect(
15991+
tracker.trackMediaImpressionClick({
15992+
...requiredParameters,
15993+
userId,
15994+
}),
15995+
).to.equal(true);
15996+
});
15997+
});
1577515998
});

src/modules/tracker.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,76 @@ class Tracker {
14161416
return new Error('parameters are required of type object');
14171417
}
14181418

1419+
/**
1420+
* Send media impression click event to API
1421+
*
1422+
* @function trackMediaImpressionClick
1423+
* @param {object} parameters - Additional parameters to be sent with request
1424+
* @param {string} parameters.bannerAdId - Banner ad identifier
1425+
* @param {string} parameters.placementId - Placement identifier
1426+
* @param {object} [parameters.analyticsTags] - Pass additional analytics data
1427+
* @param {object} [networkParameters] - Parameters relevant to the network request
1428+
* @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
1429+
* @returns {(true|Error)}
1430+
* @description User clicked a media banner
1431+
* @example
1432+
* constructorio.tracker.trackMediaImpressionClick(
1433+
* {
1434+
* bannerAdId: 'banner_ad_id',
1435+
* placementId: 'placement_id',
1436+
* },
1437+
* );
1438+
*/
1439+
trackMediaImpressionClick(parameters, networkParameters = {}) {
1440+
// Ensure required parameters are provided
1441+
if (parameters && typeof parameters === 'object' && !Array.isArray(parameters)) {
1442+
const baseUrl = new URL(this.options.mediaServiceUrl);
1443+
1444+
if (!baseUrl.hostname.startsWith('behavior') && !baseUrl.hostname.startsWith('dev-behavior')) {
1445+
baseUrl.hostname = `behavior.${baseUrl.hostname}`;
1446+
}
1447+
1448+
const requestPath = `${baseUrl.toString()}v2/ad_behavioral_action/display_ad_click?`;
1449+
1450+
const bodyParams = {};
1451+
const {
1452+
bannerAdId,
1453+
placementId,
1454+
analyticsTags,
1455+
} = parameters;
1456+
1457+
if (!helpers.isNil(bannerAdId)) {
1458+
bodyParams.banner_ad_id = bannerAdId;
1459+
}
1460+
1461+
if (!helpers.isNil(placementId)) {
1462+
bodyParams.placement_id = placementId;
1463+
}
1464+
1465+
if (!helpers.isNil(analyticsTags)) {
1466+
bodyParams.analytics_tags = analyticsTags;
1467+
}
1468+
1469+
const requestURL = `${requestPath}${applyParamsAsString({}, this.options)}`;
1470+
const requestMethod = 'POST';
1471+
const requestBody = applyParams(bodyParams, { ...this.options, requestMethod });
1472+
1473+
this.requests.queue(
1474+
requestURL,
1475+
requestMethod,
1476+
requestBody,
1477+
networkParameters,
1478+
);
1479+
this.requests.send();
1480+
1481+
return true;
1482+
}
1483+
1484+
this.requests.send();
1485+
1486+
return new Error('parameters are required of type object');
1487+
}
1488+
14191489
/**
14201490
* Send recommendation click event to API
14211491
*

src/types/tracker.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,5 +444,12 @@ declare class Tracker {
444444
}, networkParameters?: NetworkParameters
445445
): true | Error;
446446

447+
trackMediaImpressionClick(parameters: {
448+
bannerAdId: string;
449+
placementId: string;
450+
analyticsTags?: Record<string, string>;
451+
}, networkParameters?: NetworkParameters
452+
): true | Error;
453+
447454
on(messageType: string, callback: Function): true | Error;
448455
}

0 commit comments

Comments
 (0)