prebid.js 6.28.0 → 6.29.2

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.
Files changed (162) hide show
  1. package/dist/33acrossBidAdapter.js +1 -1
  2. package/dist/adagioBidAdapter.js +1 -1
  3. package/dist/adbookpspBidAdapter.js +1 -1
  4. package/dist/adgenerationBidAdapter.js +1 -1
  5. package/dist/adoceanBidAdapter.js +1 -1
  6. package/dist/adrelevantisBidAdapter.js +1 -1
  7. package/dist/adxcgBidAdapter.js +1 -1
  8. package/dist/adyoulikeBidAdapter.js +1 -1
  9. package/dist/ajaBidAdapter.js +1 -1
  10. package/dist/amxBidAdapter.js +1 -1
  11. package/dist/amxIdSystem.js +1 -1
  12. package/dist/appierAnalyticsAdapter.js +1 -1
  13. package/dist/appnexusBidAdapter.js +1 -1
  14. package/dist/asoBidAdapter.js +1 -1
  15. package/dist/audiencerunBidAdapter.js +1 -1
  16. package/dist/axonixBidAdapter.js +1 -1
  17. package/dist/bidglassBidAdapter.js +1 -1
  18. package/dist/big-richmediaBidAdapter.js +1 -1
  19. package/dist/bluebillywigBidAdapter.js +1 -1
  20. package/dist/bridgewellBidAdapter.js +1 -1
  21. package/dist/brightMountainMediaBidAdapter.js +1 -1
  22. package/dist/concertBidAdapter.js +1 -1
  23. package/dist/connectIdSystem.js +1 -1
  24. package/dist/connectadBidAdapter.js +1 -1
  25. package/dist/consentManagement.js +1 -1
  26. package/dist/consumableBidAdapter.js +1 -1
  27. package/dist/conversantBidAdapter.js +1 -1
  28. package/dist/craftBidAdapter.js +1 -1
  29. package/dist/criteoBidAdapter.js +1 -1
  30. package/dist/currency.js +1 -1
  31. package/dist/dspxBidAdapter.js +1 -1
  32. package/dist/eplanningBidAdapter.js +1 -1
  33. package/dist/gdprEnforcement.js +1 -1
  34. package/dist/glimpseBidAdapter.js +1 -1
  35. package/dist/gmosspBidAdapter.js +1 -1
  36. package/dist/goldbachBidAdapter.js +1 -1
  37. package/dist/gridBidAdapter.js +1 -1
  38. package/dist/gridNMBidAdapter.js +1 -1
  39. package/dist/gumgumBidAdapter.js +1 -1
  40. package/dist/h12mediaBidAdapter.js +1 -1
  41. package/dist/id5IdSystem.js +1 -1
  42. package/dist/impactifyBidAdapter.js +1 -1
  43. package/dist/improvedigitalBidAdapter.js +1 -1
  44. package/dist/inmarBidAdapter.js +1 -1
  45. package/dist/insticatorBidAdapter.js +1 -1
  46. package/dist/ixBidAdapter.js +1 -1
  47. package/dist/jixieBidAdapter.js +1 -1
  48. package/dist/justpremiumBidAdapter.js +1 -1
  49. package/dist/konduitAnalyticsAdapter.js +1 -1
  50. package/dist/lassoBidAdapter.js +1 -0
  51. package/dist/liveyieldAnalyticsAdapter.js +1 -1
  52. package/dist/logicadBidAdapter.js +1 -1
  53. package/dist/loglyliftBidAdapter.js +1 -1
  54. package/dist/malltvAnalyticsAdapter.js +1 -1
  55. package/dist/marsmediaBidAdapter.js +1 -1
  56. package/dist/mediafuseBidAdapter.js +1 -1
  57. package/dist/mediakeysBidAdapter.js +1 -1
  58. package/dist/mediasquareBidAdapter.js +1 -1
  59. package/dist/merkleIdSystem.js +1 -1
  60. package/dist/mgidBidAdapter.js +1 -1
  61. package/dist/minutemediaBidAdapter.js +1 -1
  62. package/dist/not-for-prod/prebid.js +120 -119
  63. package/dist/oguryBidAdapter.js +1 -1
  64. package/dist/oneVideoBidAdapter.js +1 -1
  65. package/dist/onetagBidAdapter.js +1 -1
  66. package/dist/ooloAnalyticsAdapter.js +1 -1
  67. package/dist/outbrainBidAdapter.js +1 -1
  68. package/dist/parrableIdSystem.js +1 -1
  69. package/dist/pixfutureBidAdapter.js +1 -1
  70. package/dist/prebid-core.js +3 -3
  71. package/dist/pubCommonId.js +1 -1
  72. package/dist/publinkIdSystem.js +1 -1
  73. package/dist/pubmaticAnalyticsAdapter.js +1 -1
  74. package/dist/pubmaticBidAdapter.js +1 -1
  75. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  76. package/dist/pxyzBidAdapter.js +1 -1
  77. package/dist/quantcastBidAdapter.js +1 -1
  78. package/dist/readpeakBidAdapter.js +1 -1
  79. package/dist/relaidoBidAdapter.js +1 -1
  80. package/dist/rhythmoneBidAdapter.js +1 -1
  81. package/dist/riseBidAdapter.js +1 -1
  82. package/dist/rubiconAnalyticsAdapter.js +1 -1
  83. package/dist/rubiconBidAdapter.js +1 -1
  84. package/dist/seedingAllianceBidAdapter.js +1 -1
  85. package/dist/seedtagBidAdapter.js +1 -1
  86. package/dist/sharedIdSystem.js +1 -1
  87. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  88. package/dist/sharethroughBidAdapter.js +1 -1
  89. package/dist/smaatoBidAdapter.js +1 -1
  90. package/dist/smartadserverBidAdapter.js +1 -1
  91. package/dist/smartxBidAdapter.js +1 -1
  92. package/dist/smilewantedBidAdapter.js +1 -1
  93. package/dist/sonobiBidAdapter.js +1 -1
  94. package/dist/sortableBidAdapter.js +1 -1
  95. package/dist/sovrnAnalyticsAdapter.js +1 -1
  96. package/dist/sovrnBidAdapter.js +1 -1
  97. package/dist/sspBCBidAdapter.js +1 -1
  98. package/dist/sublimeBidAdapter.js +1 -1
  99. package/dist/synacormediaBidAdapter.js +1 -1
  100. package/dist/taboolaBidAdapter.js +1 -1
  101. package/dist/targetVideoBidAdapter.js +1 -1
  102. package/dist/teadsBidAdapter.js +1 -1
  103. package/dist/trionBidAdapter.js +1 -1
  104. package/dist/tripleliftBidAdapter.js +1 -1
  105. package/dist/trustxBidAdapter.js +1 -1
  106. package/dist/ttdBidAdapter.js +1 -1
  107. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  108. package/dist/underdogmediaBidAdapter.js +1 -1
  109. package/dist/undertoneBidAdapter.js +1 -1
  110. package/dist/userId.js +1 -1
  111. package/dist/vidazooBidAdapter.js +1 -1
  112. package/dist/videobyteBidAdapter.js +1 -1
  113. package/dist/visxBidAdapter.js +1 -1
  114. package/dist/vuukleBidAdapter.js +1 -1
  115. package/dist/widespaceBidAdapter.js +1 -1
  116. package/dist/winrBidAdapter.js +1 -1
  117. package/dist/yahoosspBidAdapter.js +1 -1
  118. package/dist/yieldmoBidAdapter.js +1 -1
  119. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  120. package/modules/adoceanBidAdapter.js +29 -1
  121. package/modules/consentManagement.js +20 -10
  122. package/modules/currency.js +2 -2
  123. package/modules/gdprEnforcement.js +15 -9
  124. package/modules/insticatorBidAdapter.js +109 -42
  125. package/modules/ixBidAdapter.js +3 -2
  126. package/modules/ixBidAdapter.md +1 -1
  127. package/modules/lassoBidAdapter.js +133 -0
  128. package/modules/lassoBidAdapter.md +29 -0
  129. package/modules/merkleIdSystem.js +43 -31
  130. package/modules/pubCommonId.js +2 -1
  131. package/modules/pubmaticAnalyticsAdapter.js +49 -33
  132. package/modules/sharedIdSystem.js +5 -11
  133. package/modules/synacormediaBidAdapter.js +11 -2
  134. package/modules/taboolaBidAdapter.js +1 -1
  135. package/modules/userId/eids.js +25 -5
  136. package/modules/userId/index.js +39 -21
  137. package/package.json +1 -1
  138. package/src/auction.js +6 -5
  139. package/src/consentHandler.js +11 -11
  140. package/src/hook.js +2 -2
  141. package/src/storageManager.js +5 -4
  142. package/src/utils/promise.js +96 -21
  143. package/src/utils.js +3 -2
  144. package/test/helpers/consentData.js +2 -1
  145. package/test/spec/auctionmanager_spec.js +1 -6
  146. package/test/spec/modules/adoceanBidAdapter_spec.js +25 -1
  147. package/test/spec/modules/consentManagement_spec.js +75 -16
  148. package/test/spec/modules/eids_spec.js +48 -3
  149. package/test/spec/modules/gdprEnforcement_spec.js +100 -53
  150. package/test/spec/modules/idxIdSystem_spec.js +1 -1
  151. package/test/spec/modules/insticatorBidAdapter_spec.js +46 -1
  152. package/test/spec/modules/ixBidAdapter_spec.js +1 -1
  153. package/test/spec/modules/lassoBidAdapter_spec.js +177 -0
  154. package/test/spec/modules/merkleIdSystem_spec.js +58 -37
  155. package/test/spec/modules/parrableIdSystem_spec.js +2 -1
  156. package/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +117 -0
  157. package/test/spec/modules/synacormediaBidAdapter_spec.js +74 -0
  158. package/test/spec/modules/userId_spec.js +42 -3
  159. package/test/spec/modules/vidazooBidAdapter_spec.js +9 -4
  160. package/test/spec/unit/pbjs_api_spec.js +2 -6
  161. package/test/spec/unit/utils/promise_spec.js +283 -38
  162. package/test/helpers/syncPromise.js +0 -71
@@ -1,36 +1,111 @@
1
1
  const SUCCESS = 0;
2
2
  const FAIL = 1;
3
- const RESULT = 2;
4
3
 
5
4
  /**
6
- * @returns a {promise, resolve, reject} trio where `promise` is resolved by calling `resolve` or `reject`.
5
+ * A version of Promise that runs callbacks synchronously when it can (i.e. after it's been fulfilled or rejected).
7
6
  */
8
- export function promiseControls({promiseFactory = (resolver) => new Promise(resolver)} = {}) {
9
- const status = {};
7
+ export class GreedyPromise extends Promise {
8
+ #result;
9
+ #callbacks;
10
+ #parent = null;
11
+
12
+ /**
13
+ * Convenience wrapper for setTimeout; takes care of returning an already fulfilled GreedyPromise when the delay is zero.
14
+ *
15
+ * @param {Number} delayMs delay in milliseconds
16
+ * @returns {GreedyPromise} a promise that resolves (to undefined) in `delayMs` milliseconds
17
+ */
18
+ static timeout(delayMs = 0) {
19
+ return new GreedyPromise((resolve) => {
20
+ delayMs === 0 ? resolve() : setTimeout(resolve, delayMs);
21
+ });
22
+ }
10
23
 
11
- function finisher(slot) {
12
- return function (val) {
13
- if (typeof status[slot] === 'function') {
14
- status[slot](val);
15
- } else if (!status[slot]) {
16
- status[slot] = true;
17
- status[RESULT] = val;
24
+ constructor(resolver) {
25
+ const result = [];
26
+ const callbacks = [];
27
+ function handler(type, resolveFn) {
28
+ return function (value) {
29
+ if (!result.length) {
30
+ result.push(type, value);
31
+ while (callbacks.length) callbacks.shift()();
32
+ resolveFn(value);
33
+ }
34
+ }
35
+ }
36
+ super(
37
+ typeof resolver !== 'function'
38
+ ? resolver // let super throw an error
39
+ : (resolve, reject) => {
40
+ const rejectHandler = handler(FAIL, reject);
41
+ const resolveHandler = (() => {
42
+ const done = handler(SUCCESS, resolve);
43
+ return value =>
44
+ typeof value?.then === 'function' ? value.then(done, rejectHandler) : done(value);
45
+ })();
46
+ try {
47
+ resolver(resolveHandler, rejectHandler);
48
+ } catch (e) {
49
+ rejectHandler(e);
50
+ }
51
+ }
52
+ );
53
+ this.#result = result;
54
+ this.#callbacks = callbacks;
55
+ }
56
+ then(onSuccess, onError) {
57
+ if (typeof onError === 'function') {
58
+ // if an error handler is provided, attach a dummy error handler to super,
59
+ // and do the same for all promises without an error handler that precede this one in a chain.
60
+ // This is to avoid unhandled rejection events / warnings for errors that were, in fact, handled;
61
+ // since we are not using super's callback mechanisms we need to make it aware of this separately.
62
+ let node = this;
63
+ while (node) {
64
+ super.then.call(node, null, () => null);
65
+ const next = node.#parent;
66
+ node.#parent = null; // since we attached a handler already, we are no longer interested in what will happen later in the chain
67
+ node = next;
18
68
  }
19
69
  }
70
+ const result = this.#result;
71
+ const res = new GreedyPromise((resolve, reject) => {
72
+ const continuation = () => {
73
+ let value = result[1];
74
+ let [handler, resolveFn] = result[0] === SUCCESS ? [onSuccess, resolve] : [onError, reject];
75
+ if (typeof handler === 'function') {
76
+ try {
77
+ value = handler(value);
78
+ } catch (e) {
79
+ reject(e);
80
+ return;
81
+ }
82
+ resolveFn = resolve;
83
+ }
84
+ resolveFn(value);
85
+ }
86
+ result.length ? continuation() : this.#callbacks.push(continuation);
87
+ });
88
+ res.#parent = this;
89
+ return res;
20
90
  }
91
+ }
92
+
93
+ /**
94
+ * @returns a {promise, resolve, reject} trio where `promise` is resolved by calling `resolve` or `reject`.
95
+ */
96
+ export function defer({promiseFactory = (resolver) => new GreedyPromise(resolver)} = {}) {
97
+ function invoker(delegate) {
98
+ return (val) => delegate(val);
99
+ }
100
+
101
+ let resolveFn, rejectFn;
21
102
 
22
103
  return {
23
104
  promise: promiseFactory((resolve, reject) => {
24
- if (status[SUCCESS] != null) {
25
- resolve(status[RESULT]);
26
- } else if (status[FAIL] != null) {
27
- reject(status[RESULT]);
28
- } else {
29
- status[SUCCESS] = resolve;
30
- status[FAIL] = reject;
31
- }
105
+ resolveFn = resolve;
106
+ rejectFn = reject;
32
107
  }),
33
- resolve: finisher(SUCCESS),
34
- reject: finisher(FAIL)
108
+ resolve: invoker(resolveFn),
109
+ reject: invoker(rejectFn)
35
110
  }
36
111
  }
package/src/utils.js CHANGED
@@ -2,6 +2,7 @@ import { config } from './config.js';
2
2
  import clone from 'just-clone';
3
3
  import {find, includes} from './polyfill.js';
4
4
  import CONSTANTS from './constants.json';
5
+ import {GreedyPromise} from './utils/promise.js';
5
6
  export { default as deepAccess } from 'dlv/index.js';
6
7
  export { default as deepSetValue } from 'dset';
7
8
 
@@ -487,7 +488,7 @@ export function hasOwn(objectToCheck, propertyToCheckFor) {
487
488
  * @param {HTMLElement} [doc]
488
489
  * @param {HTMLElement} [target]
489
490
  * @param {Boolean} [asLastChildChild]
490
- * @return {HTMLElement}
491
+ * @return {HTML Element}
491
492
  */
492
493
  export function insertElement(elm, doc, target, asLastChildChild) {
493
494
  doc = doc || document;
@@ -517,7 +518,7 @@ export function insertElement(elm, doc, target, asLastChildChild) {
517
518
  */
518
519
  export function waitForElementToLoad(element, timeout) {
519
520
  let timer = null;
520
- return new Promise((resolve) => {
521
+ return new GreedyPromise((resolve) => {
521
522
  const onLoad = function() {
522
523
  element.removeEventListener('load', onLoad);
523
524
  element.removeEventListener('error', onLoad);
@@ -1,6 +1,7 @@
1
1
  import {gdprDataHandler} from 'src/adapterManager.js';
2
+ import {GreedyPromise} from '../../src/utils/promise.js';
2
3
 
3
4
  export function mockGdprConsent(sandbox, getConsentData = () => null) {
4
- sandbox.stub(gdprDataHandler, 'promise').get(() => Promise.resolve(getConsentData()));
5
+ sandbox.stub(gdprDataHandler, 'promise').get(() => GreedyPromise.resolve(getConsentData()));
5
6
  sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(getConsentData)
6
7
  }
@@ -21,7 +21,6 @@ import {auctionManager} from '../../src/auctionManager.js';
21
21
  import 'src/debugging.js' // some tests look for debugging side effects
22
22
  import {AuctionIndex} from '../../src/auctionIndex.js';
23
23
  import {expect} from 'chai';
24
- import {synchronizePromise} from '../helpers/syncPromise.js';
25
24
 
26
25
  var assert = require('assert');
27
26
 
@@ -134,7 +133,7 @@ function mockAjaxBuilder() {
134
133
  }
135
134
 
136
135
  describe('auctionmanager.js', function () {
137
- let indexAuctions, indexStub, promiseSandbox;
136
+ let indexAuctions, indexStub
138
137
 
139
138
  before(() => {
140
139
  // hooks are global and their side effects depend on what has been loaded
@@ -150,13 +149,10 @@ describe('auctionmanager.js', function () {
150
149
  indexAuctions = [];
151
150
  indexStub = sinon.stub(auctionManager, 'index');
152
151
  indexStub.get(() => new AuctionIndex(() => indexAuctions));
153
- promiseSandbox = sinon.createSandbox();
154
- synchronizePromise(promiseSandbox);
155
152
  });
156
153
 
157
154
  afterEach(() => {
158
155
  indexStub.restore();
159
- promiseSandbox.restore();
160
156
  });
161
157
 
162
158
  describe('getKeyValueTargetingPairs', function () {
@@ -1421,7 +1417,6 @@ describe('auctionmanager.js', function () {
1421
1417
  }
1422
1418
 
1423
1419
  beforeEach(() => {
1424
- promiseSandbox.restore();
1425
1420
  bids = [
1426
1421
  mockBid({bidderCode: BIDDER_CODE1}),
1427
1422
  mockBid({bidderCode: BIDDER_CODE})
@@ -83,6 +83,20 @@ describe('AdoceanAdapter', function () {
83
83
  'auctionId': '1d1a030790a475',
84
84
  }
85
85
  ];
86
+ const schainExample = {
87
+ 'schain': {
88
+ ver: '1.0',
89
+ complete: 1,
90
+ nodes: [
91
+ {
92
+ asi: 'directseller.com',
93
+ sid: '00001!,2',
94
+ rid: 'BidRequest1',
95
+ hp: 1
96
+ }
97
+ ]
98
+ }
99
+ };
86
100
 
87
101
  const bidderRequest = {
88
102
  gdprConsent: {
@@ -134,7 +148,17 @@ describe('AdoceanAdapter', function () {
134
148
  expect(requests[0].url).to.include('aosspsizes=myaozpniqismex~300x250_300x600-myaowafpdwlrks~300x200_600x250');
135
149
  expect((requests[0].url.match(/aosspsizes=/g) || []).length).to.equal(1);
136
150
  });
137
- })
151
+
152
+ it('should attach schain parameter if available', function() {
153
+ let requests = spec.buildRequests(bidRequests, bidderRequest);
154
+ expect(requests.some(e => e.url.includes('schain='))).to.be.false;
155
+
156
+ const bidsWithSchain = deepClone(bidRequests).map(e => ({...e, ...schainExample}));
157
+ requests = spec.buildRequests(bidsWithSchain, bidderRequest);
158
+ expect(requests.every(e => e.url.includes('schain=1.0,1!directseller.com,00001%21%2C2,1,BidRequest1,,,0')),
159
+ `One of urls does not contain valid schain param: ${requests.map(e => e.url).join('\n')}`).to.be.true;
160
+ });
161
+ });
138
162
 
139
163
  describe('interpretResponse', function () {
140
164
  const response = {
@@ -797,25 +797,84 @@ describe('consentManagement', function () {
797
797
  expect(gdprDataHandler.ready).to.be.true;
798
798
  });
799
799
 
800
- it('allows the auction when CMP is unresponsive', (done) => {
801
- setConsentConfig({
802
- cmpApi: 'iab',
803
- timeout: 10,
804
- defaultGdprScope: true
800
+ describe('when proper consent is not available', () => {
801
+ let tcfStub;
802
+
803
+ function runAuction() {
804
+ setConsentConfig({
805
+ cmpApi: 'iab',
806
+ timeout: 10,
807
+ defaultGdprScope: true
808
+ });
809
+ return new Promise((resolve, reject) => {
810
+ requestBidsHook(() => {
811
+ didHookReturn = true;
812
+ }, {});
813
+ setTimeout(() => didHookReturn ? resolve() : reject(new Error('Auction did not run')), 20);
814
+ })
815
+ }
816
+
817
+ function mockTcfEvent(tcdata) {
818
+ tcfStub.callsFake((api, version, cb) => {
819
+ if (api === 'addEventListener' && version === 2) {
820
+ // eslint-disable-next-line standard/no-callback-literal
821
+ cb(tcdata, true)
822
+ }
823
+ });
824
+ }
825
+
826
+ beforeEach(() => {
827
+ tcfStub = sinon.stub(window, '__tcfapi');
805
828
  });
806
829
 
807
- requestBidsHook(() => {
808
- didHookReturn = true;
809
- }, {});
830
+ afterEach(() => {
831
+ tcfStub.restore();
832
+ })
833
+
834
+ it('should continue auction with null consent when CMP is unresponsive', () => {
835
+ return runAuction().then(() => {
836
+ const consent = gdprDataHandler.getConsentData();
837
+ expect(consent.gdprApplies).to.be.true;
838
+ expect(consent.consentString).to.be.undefined;
839
+ expect(gdprDataHandler.ready).to.be.true;
840
+ });
841
+ });
810
842
 
811
- setTimeout(() => {
812
- expect(didHookReturn).to.be.true;
813
- const consent = gdprDataHandler.getConsentData();
814
- expect(consent.gdprApplies).to.be.true;
815
- expect(consent.consentString).to.be.undefined;
816
- expect(gdprDataHandler.ready).to.be.true;
817
- done();
818
- }, 20);
843
+ it('should use consent provided by events other than tcloaded', () => {
844
+ mockTcfEvent({
845
+ eventStatus: 'cmpuishown',
846
+ tcString: 'mock-consent-string',
847
+ vendorData: {}
848
+ });
849
+ return runAuction().then(() => {
850
+ const consent = gdprDataHandler.getConsentData();
851
+ expect(consent.gdprApplies).to.be.true;
852
+ expect(consent.consentString).to.equal('mock-consent-string');
853
+ expect(consent.vendorData.vendorData).to.eql({});
854
+ expect(gdprDataHandler.ready).to.be.true;
855
+ });
856
+ });
857
+
858
+ Object.entries({
859
+ 'null': null,
860
+ 'empty': '',
861
+ 'undefined': undefined
862
+ }).forEach(([t, cs]) => {
863
+ // some CMPs appear to reply with an empty consent string in 'cmpuishown' - make sure we don't use that
864
+ it(`should NOT use "default" consent if string is ${t}`, () => {
865
+ mockTcfEvent({
866
+ eventStatus: 'cmpuishown',
867
+ tcString: cs,
868
+ vendorData: {random: 'junk'}
869
+ });
870
+ return runAuction().then(() => {
871
+ const consent = gdprDataHandler.getConsentData();
872
+ expect(consent.gdprApplies).to.be.true;
873
+ expect(consent.consentString).to.be.undefined;
874
+ expect(consent.vendorData).to.be.undefined;
875
+ });
876
+ })
877
+ });
819
878
  });
820
879
 
821
880
  it('It still considers it a valid cmp response if gdprApplies is not a boolean', function () {
@@ -82,20 +82,65 @@ describe('eids array generation for known sub-modules', function() {
82
82
  });
83
83
  });
84
84
 
85
- it('merkleId', function() {
85
+ it('merkleId (legacy) - supports single id', function() {
86
86
  const userId = {
87
87
  merkleId: {
88
88
  id: 'some-random-id-value', keyID: 1
89
89
  }
90
90
  };
91
91
  const newEids = createEidsArray(userId);
92
+
92
93
  expect(newEids.length).to.equal(1);
93
94
  expect(newEids[0]).to.deep.equal({
94
95
  source: 'merkleinc.com',
96
+ uids: [{
97
+ id: 'some-random-id-value',
98
+ atype: 3,
99
+ ext: { keyID: 1 }
100
+ }]
101
+ });
102
+ });
103
+
104
+ it('merkleId supports multiple source providers', function() {
105
+ const userId = {
106
+ merkleId: [{
107
+ id: 'some-random-id-value', ext: { enc: 1, keyID: 16, idName: 'pamId', ssp: 'ssp1' }
108
+ }, {
109
+ id: 'another-random-id-value',
110
+ ext: {
111
+ enc: 1,
112
+ idName: 'pamId',
113
+ third: 4,
114
+ ssp: 'ssp2'
115
+ }
116
+ }]
117
+ }
118
+
119
+ const newEids = createEidsArray(userId);
120
+ expect(newEids.length).to.equal(2);
121
+ expect(newEids[0]).to.deep.equal({
122
+ source: 'ssp1.merkleinc.com',
95
123
  uids: [{id: 'some-random-id-value',
96
124
  atype: 3,
97
- ext: { keyID: 1
98
- }}]
125
+ ext: {
126
+ enc: 1,
127
+ keyID: 16,
128
+ idName: 'pamId',
129
+ ssp: 'ssp1'
130
+ }
131
+ }]
132
+ });
133
+ expect(newEids[1]).to.deep.equal({
134
+ source: 'ssp2.merkleinc.com',
135
+ uids: [{id: 'another-random-id-value',
136
+ atype: 3,
137
+ ext: {
138
+ third: 4,
139
+ enc: 1,
140
+ idName: 'pamId',
141
+ ssp: 'ssp2'
142
+ }
143
+ }]
99
144
  });
100
145
  });
101
146