prebid.js 5.20.2 → 5.20.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@
3
3
  # Check https://circleci.com/docs/2.0/language-javascript/ for more details
4
4
  #
5
5
 
6
- aliases:
6
+ aliases:
7
7
  - &environment
8
8
  docker:
9
9
  # specify the version you desire here
@@ -17,9 +17,8 @@ aliases:
17
17
 
18
18
  - &restore_dep_cache
19
19
  keys:
20
- - v1-dependencies-{{ checksum "package.json" }}
21
- # fallback to using the latest cache if no exact match is found
22
- - v1-dependencies-
20
+ - v5.20.x-legacy-dependencies-{{ checksum "package.json" }}
21
+ - v5.20.x-legacy-dependencies-
23
22
 
24
23
  - &save_dep_cache
25
24
  paths:
@@ -72,7 +71,7 @@ jobs:
72
71
  build:
73
72
  <<: *environment
74
73
  steps: *unit_test_steps
75
-
74
+
76
75
  e2etest:
77
76
  <<: *environment
78
77
  steps: *endtoend_test_steps
@@ -1078,7 +1078,7 @@ export const spec = {
1078
1078
  bid = deepClone(originalBid);
1079
1079
  bid.params.adSlot = bid.params.adSlot || '';
1080
1080
  _parseAdSlot(bid);
1081
- if (bid.params.hasOwnProperty('video')) {
1081
+ if ((bid.mediaTypes && bid.mediaTypes.hasOwnProperty('video')) || bid.params.hasOwnProperty('video')) {
1082
1082
  // Nothing to do
1083
1083
  } else {
1084
1084
  // If we have a native mediaType configured alongside banner, its ok if the banner size is not set in width and height
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prebid.js",
3
- "version": "5.20.2",
3
+ "version": "5.20.3",
4
4
  "description": "Header Bidding Management Library",
5
5
  "main": "src/prebid.js",
6
6
  "scripts": {
package/src/prebid.js CHANGED
@@ -403,10 +403,23 @@ function emitAdRenderSucceeded({ doc, bid, id }) {
403
403
  events.emit(AD_RENDER_SUCCEEDED, data);
404
404
  }
405
405
 
406
+ /**
407
+ * This function will check for presence of given node in given parent. If not present - will inject it.
408
+ * @param {Node} node node, whose existance is in question
409
+ * @param {Document} doc document element do look in
410
+ * @param {string} tagName tag name to look in
411
+ */
412
+ function reinjectNodeIfRemoved(node, doc, tagName) {
413
+ const injectionNode = doc.querySelector(tagName);
414
+ if (!node.parentNode || node.parentNode !== injectionNode) {
415
+ insertElement(node, doc, tagName);
416
+ }
417
+ }
418
+
406
419
  /**
407
420
  * This function will render the ad (based on params) in the given iframe document passed through.
408
421
  * Note that doc SHOULD NOT be the parent document page as we can't doc.write() asynchronously
409
- * @param {HTMLDocument} doc document
422
+ * @param {Document} doc document
410
423
  * @param {string} id bid id to locate the ad
411
424
  * @alias module:pbjs.renderAd
412
425
  */
@@ -450,10 +463,13 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) {
450
463
  const {height, width, ad, mediaType, adUrl, renderer} = bid;
451
464
 
452
465
  const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`);
466
+ // It is important that the comment with metadata is injected before the ad is actually rendered,
467
+ // so the creativeId can be used by e.g. ad quality scanners to check against blocking rules
468
+ insertElement(creativeComment, doc, 'html');
453
469
 
454
470
  if (isRendererRequired(renderer)) {
455
471
  executeRenderer(renderer, bid);
456
- insertElement(creativeComment, doc, 'html');
472
+ reinjectNodeIfRemoved(creativeComment, doc, 'html');
457
473
  emitAdRenderSucceeded({ doc, bid, id });
458
474
  } else if ((doc === document && !inIframe()) || mediaType === 'video') {
459
475
  const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`;
@@ -472,7 +488,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) {
472
488
  doc.write(ad);
473
489
  doc.close();
474
490
  setRenderSize(doc, width, height);
475
- insertElement(creativeComment, doc, 'html');
491
+ reinjectNodeIfRemoved(creativeComment, doc, 'html');
476
492
  callBurl(bid);
477
493
  emitAdRenderSucceeded({ doc, bid, id });
478
494
  } else if (adUrl) {
@@ -485,7 +501,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) {
485
501
 
486
502
  insertElement(iframe, doc, 'body');
487
503
  setRenderSize(doc, width, height);
488
- insertElement(creativeComment, doc, 'html');
504
+ reinjectNodeIfRemoved(creativeComment, doc, 'html');
489
505
  callBurl(bid);
490
506
  emitAdRenderSucceeded({ doc, bid, id });
491
507
  } else {
package/src/targeting.js CHANGED
@@ -416,7 +416,15 @@ export function newTargeting(auctionManager) {
416
416
  let bidsReceived = auctionManager.getBidsReceived();
417
417
 
418
418
  if (!config.getConfig('useBidCache')) {
419
+ // don't use bid cache (i.e. filter out bids not in the latest auction)
419
420
  bidsReceived = bidsReceived.filter(bid => latestAuctionForAdUnit[bid.adUnitCode] === bid.auctionId)
421
+ } else {
422
+ // if custom bid cache filter function exists, run for each bid from
423
+ // previous auctions. If it returns true, include bid in bid pool
424
+ const filterFunction = config.getConfig('bidCacheFilterFunction');
425
+ if (typeof filterFunction === 'function') {
426
+ bidsReceived = bidsReceived.filter(bid => latestAuctionForAdUnit[bid.adUnitCode] === bid.auctionId || !!filterFunction(bid))
427
+ }
420
428
  }
421
429
 
422
430
  bidsReceived = bidsReceived
@@ -1231,7 +1231,7 @@ export function getCurrencyRates() {
1231
1231
  };
1232
1232
  }
1233
1233
 
1234
- export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, adUnitCode, adId, status, ttl, requestId}) {
1234
+ export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, adUnitCode, adId, status, ttl, requestId, mediaType}) {
1235
1235
  let bid = {
1236
1236
  'bidderCode': bidder,
1237
1237
  'width': '300',
@@ -1259,6 +1259,7 @@ export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, ad
1259
1259
  'hb_pb': cpm,
1260
1260
  'foobar': '300x250'
1261
1261
  }),
1262
+ 'mediaType': mediaType,
1262
1263
  'netRevenue': true,
1263
1264
  'currency': 'USD',
1264
1265
  'ttl': (!ttl) ? 300 : ttl
@@ -3861,4 +3861,44 @@ describe('PubMatic adapter', function () {
3861
3861
  })
3862
3862
  });
3863
3863
  });
3864
+
3865
+ describe('Video request params', function() {
3866
+ let sandbox, utilsMock, newVideoRequest;
3867
+ beforeEach(() => {
3868
+ utilsMock = sinon.mock(utils);
3869
+ sandbox = sinon.sandbox.create();
3870
+ sandbox.spy(utils, 'logWarn');
3871
+ newVideoRequest = utils.deepClone(videoBidRequests)
3872
+ });
3873
+
3874
+ afterEach(() => {
3875
+ utilsMock.restore();
3876
+ sandbox.restore();
3877
+ })
3878
+
3879
+ it('Should log warning if video params from mediaTypes and params obj of bid are not present', function () {
3880
+ delete newVideoRequest[0].mediaTypes.video;
3881
+ delete newVideoRequest[0].params.video;
3882
+
3883
+ let request = spec.buildRequests(newVideoRequest, {
3884
+ auctionId: 'new-auction-id'
3885
+ });
3886
+
3887
+ sinon.assert.calledOnce(utils.logWarn);
3888
+ expect(request).to.equal(undefined);
3889
+ });
3890
+
3891
+ it('Should consider video params from mediaType object of bid', function () {
3892
+ delete newVideoRequest[0].params.video;
3893
+
3894
+ let request = spec.buildRequests(newVideoRequest, {
3895
+ auctionId: 'new-auction-id'
3896
+ });
3897
+ let data = JSON.parse(request.data);
3898
+ expect(data.imp[0].video).to.exist;
3899
+ expect(data.imp[0]['video']['w']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[0]);
3900
+ expect(data.imp[0]['video']['h']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[1]);
3901
+ expect(data.imp[0]['video']['battr']).to.equal(undefined);
3902
+ });
3903
+ });
3864
3904
  });
@@ -227,6 +227,8 @@ describe('targeting tests', function () {
227
227
  let sandbox;
228
228
  let enableSendAllBids = false;
229
229
  let useBidCache;
230
+ let bidCacheFilterFunction;
231
+ let undef;
230
232
 
231
233
  beforeEach(function() {
232
234
  sandbox = sinon.sandbox.create();
@@ -241,12 +243,16 @@ describe('targeting tests', function () {
241
243
  if (key === 'useBidCache') {
242
244
  return useBidCache;
243
245
  }
246
+ if (key === 'bidCacheFilterFunction') {
247
+ return bidCacheFilterFunction;
248
+ }
244
249
  return origGetConfig.apply(config, arguments);
245
250
  });
246
251
  });
247
252
 
248
253
  afterEach(function () {
249
254
  sandbox.restore();
255
+ bidCacheFilterFunction = undef;
250
256
  });
251
257
 
252
258
  describe('getAllTargeting', function () {
@@ -785,6 +791,93 @@ describe('targeting tests', function () {
785
791
  expect(bids[0].adId).to.equal('adid-2');
786
792
  });
787
793
 
794
+ it('should use bidCacheFilterFunction', function() {
795
+ auctionManagerStub.returns([
796
+ createBidReceived({bidder: 'appnexus', cpm: 7, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1', mediaType: 'banner'}),
797
+ createBidReceived({bidder: 'appnexus', cpm: 5, auctionId: 2, responseTimestamp: 102, adUnitCode: 'code-0', adId: 'adid-2', mediaType: 'banner'}),
798
+ createBidReceived({bidder: 'appnexus', cpm: 6, auctionId: 1, responseTimestamp: 101, adUnitCode: 'code-1', adId: 'adid-3', mediaType: 'banner'}),
799
+ createBidReceived({bidder: 'appnexus', cpm: 8, auctionId: 2, responseTimestamp: 103, adUnitCode: 'code-1', adId: 'adid-4', mediaType: 'banner'}),
800
+ createBidReceived({bidder: 'appnexus', cpm: 27, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-2', adId: 'adid-5', mediaType: 'video'}),
801
+ createBidReceived({bidder: 'appnexus', cpm: 25, auctionId: 2, responseTimestamp: 102, adUnitCode: 'code-2', adId: 'adid-6', mediaType: 'video'}),
802
+ createBidReceived({bidder: 'appnexus', cpm: 26, auctionId: 1, responseTimestamp: 101, adUnitCode: 'code-3', adId: 'adid-7', mediaType: 'video'}),
803
+ createBidReceived({bidder: 'appnexus', cpm: 28, auctionId: 2, responseTimestamp: 103, adUnitCode: 'code-3', adId: 'adid-8', mediaType: 'video'}),
804
+ ]);
805
+
806
+ let adUnitCodes = ['code-0', 'code-1', 'code-2', 'code-3'];
807
+ targetingInstance.setLatestAuctionForAdUnit('code-0', 2);
808
+ targetingInstance.setLatestAuctionForAdUnit('code-1', 2);
809
+ targetingInstance.setLatestAuctionForAdUnit('code-2', 2);
810
+ targetingInstance.setLatestAuctionForAdUnit('code-3', 2);
811
+
812
+ // Bid Caching On, No Filter Function
813
+ useBidCache = true;
814
+ bidCacheFilterFunction = undef;
815
+ let bids = targetingInstance.getWinningBids(adUnitCodes);
816
+
817
+ expect(bids.length).to.equal(4);
818
+ expect(bids[0].adId).to.equal('adid-1');
819
+ expect(bids[1].adId).to.equal('adid-4');
820
+ expect(bids[2].adId).to.equal('adid-5');
821
+ expect(bids[3].adId).to.equal('adid-8');
822
+
823
+ // Bid Caching Off, No Filter Function
824
+ useBidCache = false;
825
+ bidCacheFilterFunction = undef;
826
+ bids = targetingInstance.getWinningBids(adUnitCodes);
827
+
828
+ expect(bids.length).to.equal(4);
829
+ expect(bids[0].adId).to.equal('adid-2');
830
+ expect(bids[1].adId).to.equal('adid-4');
831
+ expect(bids[2].adId).to.equal('adid-6');
832
+ expect(bids[3].adId).to.equal('adid-8');
833
+
834
+ // Bid Caching On AGAIN, No Filter Function (should be same as first time)
835
+ useBidCache = true;
836
+ bidCacheFilterFunction = undef;
837
+ bids = targetingInstance.getWinningBids(adUnitCodes);
838
+
839
+ expect(bids.length).to.equal(4);
840
+ expect(bids[0].adId).to.equal('adid-1');
841
+ expect(bids[1].adId).to.equal('adid-4');
842
+ expect(bids[2].adId).to.equal('adid-5');
843
+ expect(bids[3].adId).to.equal('adid-8');
844
+
845
+ // Bid Caching On, with Filter Function to Exclude video
846
+ useBidCache = true;
847
+ let bcffCalled = 0;
848
+ bidCacheFilterFunction = bid => {
849
+ bcffCalled++;
850
+ return bid.mediaType !== 'video';
851
+ }
852
+ bids = targetingInstance.getWinningBids(adUnitCodes);
853
+
854
+ expect(bids.length).to.equal(4);
855
+ expect(bids[0].adId).to.equal('adid-1');
856
+ expect(bids[1].adId).to.equal('adid-4');
857
+ expect(bids[2].adId).to.equal('adid-6');
858
+ expect(bids[3].adId).to.equal('adid-8');
859
+ // filter function should have been called for each cached bid (4 times)
860
+ expect(bcffCalled).to.equal(4);
861
+
862
+ // Bid Caching Off, with Filter Function to Exclude video
863
+ // - should not use cached bids or call the filter function
864
+ useBidCache = false;
865
+ bcffCalled = 0;
866
+ bidCacheFilterFunction = bid => {
867
+ bcffCalled++;
868
+ return bid.mediaType !== 'video';
869
+ }
870
+ bids = targetingInstance.getWinningBids(adUnitCodes);
871
+
872
+ expect(bids.length).to.equal(4);
873
+ expect(bids[0].adId).to.equal('adid-2');
874
+ expect(bids[1].adId).to.equal('adid-4');
875
+ expect(bids[2].adId).to.equal('adid-6');
876
+ expect(bids[3].adId).to.equal('adid-8');
877
+ // filter function should not have been called
878
+ expect(bcffCalled).to.equal(0);
879
+ });
880
+
788
881
  it('should not use rendered bid to get winning bid', function () {
789
882
  let bidsReceived = [
790
883
  createBidReceived({bidder: 'appnexus', cpm: 8, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1', status: 'rendered'}),
@@ -1135,13 +1135,15 @@ describe('Unit: Prebid Module', function () {
1135
1135
  height: 0
1136
1136
  }
1137
1137
  },
1138
- getElementsByTagName: sinon.stub()
1138
+ getElementsByTagName: sinon.stub(),
1139
+ querySelector: sinon.stub()
1139
1140
  };
1140
1141
 
1141
1142
  elStub = {
1142
1143
  insertBefore: sinon.stub()
1143
1144
  };
1144
1145
  doc.getElementsByTagName.returns([elStub]);
1146
+ doc.querySelector.returns(elStub);
1145
1147
 
1146
1148
  spyLogError = sinon.spy(utils, 'logError');
1147
1149
  spyLogMessage = sinon.spy(utils, 'logMessage');