mk9-prebid
Version:
Header Bidding Management Library
678 lines (582 loc) • 21.8 kB
JavaScript
import {expect} from 'chai';
import {spec, storage} from 'modules/teadsBidAdapter.js';
import {newBidder} from 'src/adapters/bidderFactory.js';
import {getStorageManager} from 'src/storageManager';
const ENDPOINT = 'https://a.teads.tv/hb/bid-request';
const AD_SCRIPT = '<script type="text/javascript" class="teads" async="true" src="https://a.teads.tv/hb/getAdSettings"></script>"';
describe('teadsBidAdapter', () => {
const adapter = newBidder(spec);
let cookiesAreEnabledStub, getCookieStub;
beforeEach(function () {
cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled');
getCookieStub = sinon.stub(storage, 'getCookie');
});
afterEach(function () {
cookiesAreEnabledStub.restore();
getCookieStub.restore();
});
describe('inherited functions', () => {
it('exists and is a function', () => {
expect(adapter.callBids).to.exist.and.to.be.a('function');
});
});
describe('isBidRequestValid', function() {
let bid = {
'bidder': 'teads',
'params': {
'placementId': 10433394,
'pageId': 1234
},
'adUnitCode': 'adunit-code',
'sizes': [[300, 250], [300, 600]],
'bidId': '30b31c1838de1e',
'bidderRequestId': '22edbae2733bf6',
'auctionId': '1d1a030790a475',
'creativeId': 'er2ee'
};
it('should return true when required params found', function() {
expect(spec.isBidRequestValid(bid)).to.equal(true);
});
it('should return false when pageId is not valid (letters)', function() {
let bid = Object.assign({}, bid);
delete bid.params;
bid.params = {
'placementId': 1234,
'pageId': 'ABCD'
};
expect(spec.isBidRequestValid(bid)).to.equal(false);
});
it('should return false when placementId is not valid (letters)', function() {
let bid = Object.assign({}, bid);
delete bid.params;
bid.params = {
'placementId': 'FCP',
'pageId': 1234
};
expect(spec.isBidRequestValid(bid)).to.equal(false);
});
it('should return false when placementId < 0 or pageId < 0', function() {
let bid = Object.assign({}, bid);
delete bid.params;
bid.params = {
'placementId': -1,
'pageId': -1
};
expect(spec.isBidRequestValid(bid)).to.equal(false);
});
it('should return false when required params are not passed', function() {
let bid = Object.assign({}, bid);
delete bid.params;
bid.params = {
'placementId': 0
};
expect(spec.isBidRequestValid(bid)).to.equal(false);
});
});
describe('buildRequests', function() {
let bidRequests = [
{
'bidder': 'teads',
'params': {
'placementId': 10433394,
'pageId': 1234
},
'adUnitCode': 'adunit-code',
'sizes': [[300, 250], [300, 600]],
'bidId': '30b31c1838de1e',
'bidderRequestId': '22edbae2733bf6',
'auctionId': '1d1a030790a475',
'creativeId': 'er2ee',
'deviceWidth': 1680
}
];
let bidderResquestDefault = {
'auctionId': '1d1a030790a475',
'bidderRequestId': '22edbae2733bf6',
'timeout': 3000
};
it('should send bid request to ENDPOINT via POST', function() {
const request = spec.buildRequests(bidRequests, bidderResquestDefault);
expect(request.url).to.equal(ENDPOINT);
expect(request.method).to.equal('POST');
});
it('should send US Privacy to endpoint', function() {
let usPrivacy = 'OHHHFCP1'
let bidderRequest = {
'auctionId': '1d1a030790a475',
'bidderRequestId': '22edbae2733bf6',
'timeout': 3000,
'uspConsent': usPrivacy
};
const request = spec.buildRequests(bidRequests, bidderRequest);
const payload = JSON.parse(request.data);
expect(payload.us_privacy).to.exist;
expect(payload.us_privacy).to.equal(usPrivacy);
});
it('should send GDPR to endpoint', function() {
let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A==';
let bidderRequest = {
'auctionId': '1d1a030790a475',
'bidderRequestId': '22edbae2733bf6',
'timeout': 3000,
'gdprConsent': {
'consentString': consentString,
'gdprApplies': true,
'vendorData': {
'hasGlobalConsent': false
},
'apiVersion': 1
}
};
const request = spec.buildRequests(bidRequests, bidderRequest);
const payload = JSON.parse(request.data);
expect(payload.gdpr_iab).to.exist;
expect(payload.gdpr_iab.consent).to.equal(consentString);
expect(payload.gdpr_iab.status).to.equal(12);
expect(payload.gdpr_iab.apiVersion).to.equal(1);
});
it('should add referer info to payload', function () {
const bidRequest = Object.assign({}, bidRequests[0])
const bidderRequest = {
refererInfo: {
referer: 'https://example.com/page.html',
reachedTop: true,
numIframes: 2
}
}
const request = spec.buildRequests([bidRequest], bidderRequest);
const payload = JSON.parse(request.data);
expect(payload.referrer).to.exist;
expect(payload.referrer).to.deep.equal('https://example.com/page.html')
});
it('should add networkBandwidth info to payload', function () {
const request = spec.buildRequests(bidRequests, bidderResquestDefault);
const payload = JSON.parse(request.data);
const bandwidth = window.navigator && window.navigator.connection && window.navigator.connection.downlink;
expect(payload.networkBandwidth).to.exist;
if (bandwidth) {
expect(payload.networkBandwidth).to.deep.equal(bandwidth.toString());
} else {
expect(payload.networkBandwidth).to.deep.equal('');
}
});
it('should add pageReferrer info to payload', function () {
const request = spec.buildRequests(bidRequests, bidderResquestDefault);
const payload = JSON.parse(request.data);
expect(payload.pageReferrer).to.exist;
expect(payload.pageReferrer).to.deep.equal(document.referrer);
});
it('should add timeToFirstByte info to payload', function () {
const request = spec.buildRequests(bidRequests, bidderResquestDefault);
const payload = JSON.parse(request.data);
const performance = window.performance || window.webkitPerformance || window.msPerformance || window.mozPerformance;
const ttfbExpectedV2 = performance &&
typeof performance.getEntriesByType === 'function' &&
Object.prototype.toString.call(performance.getEntriesByType) === '[object Function]' &&
performance.getEntriesByType('navigation')[0] &&
performance.getEntriesByType('navigation')[0].responseStart &&
performance.getEntriesByType('navigation')[0].requestStart &&
performance.getEntriesByType('navigation')[0].responseStart > 0 &&
performance.getEntriesByType('navigation')[0].requestStart > 0 &&
Math.round(
performance.getEntriesByType('navigation')[0].responseStart - performance.getEntriesByType('navigation')[0].requestStart
);
expect(payload.timeToFirstByte).to.exist;
if (ttfbExpectedV2) {
expect(payload.timeToFirstByte).to.deep.equal(ttfbExpectedV2.toString());
} else {
const ttfbWithTimingV1 = performance &&
performance.timing.responseStart &&
performance.timing.requestStart &&
performance.timing.responseStart > 0 &&
performance.timing.requestStart > 0 &&
performance.timing.responseStart - performance.timing.requestStart;
const ttfbExpectedV1 = ttfbWithTimingV1 ? ttfbWithTimingV1.toString() : '';
expect(payload.timeToFirstByte).to.deep.equal(ttfbExpectedV1);
}
});
it('should send GDPR to endpoint with 11 status', function() {
let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A==';
let bidderRequest = {
'auctionId': '1d1a030790a475',
'bidderRequestId': '22edbae2733bf6',
'timeout': 3000,
'gdprConsent': {
'consentString': consentString,
'gdprApplies': true,
'vendorData': {
'hasGlobalScope': true
},
'apiVersion': 1
}
};
const request = spec.buildRequests(bidRequests, bidderRequest);
const payload = JSON.parse(request.data);
expect(payload.gdpr_iab).to.exist;
expect(payload.gdpr_iab.consent).to.equal(consentString);
expect(payload.gdpr_iab.status).to.equal(11);
expect(payload.gdpr_iab.apiVersion).to.equal(1);
});
it('should send GDPR TCF2 to endpoint with 12 status', function() {
let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A==';
let bidderRequest = {
'auctionId': '1d1a030790a475',
'bidderRequestId': '22edbae2733bf6',
'timeout': 3000,
'gdprConsent': {
'consentString': consentString,
'gdprApplies': true,
'vendorData': {
'isServiceSpecific': true
},
'apiVersion': 2
}
};
const request = spec.buildRequests(bidRequests, bidderRequest);
const payload = JSON.parse(request.data);
expect(payload.gdpr_iab).to.exist;
expect(payload.gdpr_iab.consent).to.equal(consentString);
expect(payload.gdpr_iab.status).to.equal(12);
expect(payload.gdpr_iab.apiVersion).to.equal(2);
});
it('should send GDPR to endpoint with 22 status', function() {
let bidderRequest = {
'auctionId': '1d1a030790a475',
'bidderRequestId': '22edbae2733bf6',
'timeout': 3000,
'gdprConsent': {
'consentString': undefined,
'gdprApplies': undefined,
'vendorData': undefined,
'apiVersion': 1
}
};
const request = spec.buildRequests(bidRequests, bidderRequest);
const payload = JSON.parse(request.data);
expect(payload.gdpr_iab).to.exist;
expect(payload.gdpr_iab.consent).to.equal('');
expect(payload.gdpr_iab.status).to.equal(22);
expect(payload.gdpr_iab.apiVersion).to.equal(1);
});
it('should send GDPR to endpoint with 0 status', function() {
let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A==';
let bidderRequest = {
'auctionId': '1d1a030790a475',
'bidderRequestId': '22edbae2733bf6',
'timeout': 3000,
'gdprConsent': {
'consentString': consentString,
'gdprApplies': false,
'vendorData': {
'hasGlobalScope': false
},
'apiVersion': 1
}
};
const request = spec.buildRequests(bidRequests, bidderRequest);
const payload = JSON.parse(request.data);
expect(payload.gdpr_iab).to.exist;
expect(payload.gdpr_iab.consent).to.equal(consentString);
expect(payload.gdpr_iab.status).to.equal(0);
expect(payload.gdpr_iab.apiVersion).to.equal(1);
});
it('should send GDPR to endpoint with 0 status when gdprApplies = false (vendorData = undefined)', function() {
let bidderRequest = {
'auctionId': '1d1a030790a475',
'bidderRequestId': '22edbae2733bf6',
'timeout': 3000,
'gdprConsent': {
'consentString': undefined,
'gdprApplies': false,
'vendorData': undefined,
'apiVersion': 1
}
};
const request = spec.buildRequests(bidRequests, bidderRequest);
const payload = JSON.parse(request.data);
expect(payload.gdpr_iab).to.exist;
expect(payload.gdpr_iab.consent).to.equal('');
expect(payload.gdpr_iab.status).to.equal(0);
expect(payload.gdpr_iab.apiVersion).to.equal(1);
});
it('should send GDPR to endpoint with 12 status when apiVersion = 0', function() {
let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A==';
let bidderRequest = {
'auctionId': '1d1a030790a475',
'bidderRequestId': '22edbae2733bf6',
'timeout': 3000,
'gdprConsent': {
'consentString': consentString,
'gdprApplies': true,
'vendorData': {
'hasGlobalScope': false
},
'apiVersion': 0
}
};
const request = spec.buildRequests(bidRequests, bidderRequest);
const payload = JSON.parse(request.data);
expect(payload.gdpr_iab).to.exist;
expect(payload.gdpr_iab.consent).to.equal(consentString);
expect(payload.gdpr_iab.status).to.equal(12);
expect(payload.gdpr_iab.apiVersion).to.equal(0);
});
it('should use good mediaTypes video playerSizes', function() {
const mediaTypesPlayerSize = {
'mediaTypes': {
'video': {
'playerSize': [32, 34]
}
}
};
checkMediaTypesSizes(mediaTypesPlayerSize, '32x34');
});
it('should add schain info to payload if available', function () {
const bidRequest = Object.assign({}, bidRequests[0], {
schain: {
ver: '1.0',
complete: 1,
nodes: [{
asi: 'example.com',
sid: '00001',
hp: 1
}]
}
});
const request = spec.buildRequests([bidRequest], bidderResquestDefault);
const payload = JSON.parse(request.data);
expect(payload.schain).to.exist;
expect(payload.schain).to.deep.equal({
ver: '1.0',
complete: 1,
nodes: [{
asi: 'example.com',
sid: '00001',
hp: 1
}]
});
});
it('should use good mediaTypes video sizes', function() {
const mediaTypesVideoSizes = {
'mediaTypes': {
'video': {
'sizes': [12, 14]
}
}
};
checkMediaTypesSizes(mediaTypesVideoSizes, '12x14');
});
it('should use good mediaTypes banner sizes', function() {
const mediaTypesBannerSize = {
'mediaTypes': {
'banner': {
'sizes': [46, 48]
}
}
};
checkMediaTypesSizes(mediaTypesBannerSize, '46x48');
});
it('should use good mediaTypes for both video and banner sizes', function() {
const hybridMediaTypes = {
'mediaTypes': {
'banner': {
'sizes': [46, 48]
},
'video': {
'sizes': [[50, 34], [45, 45]]
}
}
};
checkMediaTypesSizes(hybridMediaTypes, ['46x48', '50x34', '45x45']);
});
describe('User IDs', function () {
const baseBidRequest = {
'bidder': 'teads',
'params': {
'placementId': 10433394,
'pageId': 1234
},
'adUnitCode': 'adunit-code',
'sizes': [[300, 250], [300, 600]],
'bidId': '30b31c1838de1e',
'bidderRequestId': '22edbae2733bf6',
'auctionId': '1d1a030790a475',
'creativeId': 'er2ee',
'deviceWidth': 1680
};
describe('FLoC ID', function () {
it('should not add cohortId and cohortVersion params to payload if FLoC ID system is not enabled', function () {
const bidRequest = {
...baseBidRequest,
userId: {} // no "flocId" property -> assumption that the FLoC ID system is disabled
};
const request = spec.buildRequests([bidRequest], bidderResquestDefault);
const payload = JSON.parse(request.data);
expect(payload).not.to.have.property('cohortId');
expect(payload).not.to.have.property('cohortVersion');
});
it('should add cohortId param to payload if FLoC ID system is enabled and ID available, but not version', function () {
const bidRequest = {
...baseBidRequest,
userId: {
flocId: {
id: 'my-floc-id'
}
}
};
const request = spec.buildRequests([bidRequest], bidderResquestDefault);
const payload = JSON.parse(request.data);
expect(payload.cohortId).to.equal('my-floc-id');
expect(payload).not.to.have.property('cohortVersion');
});
it('should add cohortId and cohortVersion params to payload if FLoC ID system is enabled', function () {
const bidRequest = {
...baseBidRequest,
userId: {
flocId: {
id: 'my-floc-id',
version: 'chrome.1.1'
}
}
};
const request = spec.buildRequests([bidRequest], bidderResquestDefault);
const payload = JSON.parse(request.data);
expect(payload.cohortId).to.equal('my-floc-id');
expect(payload.cohortVersion).to.equal('chrome.1.1');
});
});
describe('Unified ID v2', function () {
it('should not add unifiedId2 param to payload if uid2 system is not enabled', function () {
const bidRequest = {
...baseBidRequest,
userId: {} // no "uid2" property -> assumption that the Unified ID v2 system is disabled
};
const request = spec.buildRequests([bidRequest], bidderResquestDefault);
const payload = JSON.parse(request.data);
expect(payload).not.to.have.property('unifiedId2');
});
it('should add unifiedId2 param to payload if uid2 system is enabled', function () {
const bidRequest = {
...baseBidRequest,
userId: {
uid2: {
id: 'my-unified-id-2'
}
}
};
const request = spec.buildRequests([bidRequest], bidderResquestDefault);
const payload = JSON.parse(request.data);
expect(payload.unifiedId2).to.equal('my-unified-id-2');
})
});
describe('First-party cookie Teads ID', function () {
it('should not add firstPartyCookieTeadsId param to payload if cookies are not enabled', function () {
cookiesAreEnabledStub.returns(false);
const request = spec.buildRequests([baseBidRequest], bidderResquestDefault);
const payload = JSON.parse(request.data);
expect(payload).not.to.have.property('firstPartyCookieTeadsId');
});
it('should not add firstPartyCookieTeadsId param to payload if first-party cookie is not available', function () {
cookiesAreEnabledStub.returns(true);
getCookieStub.withArgs('_tfpvi').returns(undefined);
const request = spec.buildRequests([baseBidRequest], bidderResquestDefault);
const payload = JSON.parse(request.data);
expect(payload).not.to.have.property('firstPartyCookieTeadsId');
});
it('should add firstPartyCookieTeadsId param to payload if first-party cookie is available', function () {
cookiesAreEnabledStub.returns(true);
getCookieStub.withArgs('_tfpvi').returns('my-teads-id');
const request = spec.buildRequests([baseBidRequest], bidderResquestDefault);
const payload = JSON.parse(request.data);
expect(payload.firstPartyCookieTeadsId).to.equal('my-teads-id');
});
});
});
function checkMediaTypesSizes(mediaTypes, expectedSizes) {
const bidRequestWithBannerSizes = Object.assign(bidRequests[0], mediaTypes);
const requestWithBannerSizes = spec.buildRequests([bidRequestWithBannerSizes], bidderResquestDefault);
const payloadWithBannerSizes = JSON.parse(requestWithBannerSizes.data);
return payloadWithBannerSizes.data.forEach(bid => {
if (Array.isArray(expectedSizes)) {
expect(JSON.stringify(bid.sizes)).to.equal(JSON.stringify(expectedSizes));
} else {
expect(bid.sizes[0]).to.equal(expectedSizes);
}
});
}
});
describe('interpretResponse', function() {
it('should get correct bid responses', function() {
let bids = {
'body': {
'responses': [{
'ad': AD_SCRIPT,
'cpm': 0.5,
'currency': 'USD',
'height': 250,
'bidId': '3ede2a3fa0db94',
'ttl': 360,
'width': 300,
'creativeId': 'er2ee',
'placementId': 34
}, {
'ad': AD_SCRIPT,
'cpm': 0.5,
'currency': 'USD',
'height': 200,
'bidId': '4fef3b4gb1ec15',
'ttl': 360,
'width': 350,
'creativeId': 'fs3ff',
'placementId': 34,
'dealId': 'ABC_123'
}]
}
};
let expectedResponse = [
{
'cpm': 0.5,
'width': 300,
'height': 250,
'currency': 'USD',
'netRevenue': true,
'meta': {
advertiserDomains: []
},
'ttl': 360,
'ad': AD_SCRIPT,
'requestId': '3ede2a3fa0db94',
'creativeId': 'er2ee',
'placementId': 34
}, {
'cpm': 0.5,
'width': 350,
'height': 200,
'currency': 'USD',
'netRevenue': true,
'meta': {
advertiserDomains: []
},
'ttl': 360,
'ad': AD_SCRIPT,
'requestId': '4fef3b4gb1ec15',
'creativeId': 'fs3ff',
'placementId': 34,
'dealId': 'ABC_123'
}
]
;
let result = spec.interpretResponse(bids);
expect(result).to.eql(expectedResponse);
});
it('handles nobid responses', function() {
let bids = {
'body': {
'responses': []
}
};
let result = spec.interpretResponse(bids);
expect(result.length).to.equal(0);
});
});
});