• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / 19836249188

01 Dec 2025 08:19PM UTC coverage: 96.234% (+0.004%) from 96.23%
19836249188

push

github

web-flow
clickioBidAdapter: add IAB GVL ID and TCFEU support (#14224)

53490 of 65500 branches covered (81.66%)

1 of 1 new or added line in 1 file covered. (100.0%)

87 existing lines in 10 files now uncovered.

204130 of 212118 relevant lines covered (96.23%)

72.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.44
/test/spec/modules/realTimeDataModule_spec.js
1
import * as rtdModule from 'modules/rtdModule/index.js';
2
import {config} from 'src/config.js';
3
import * as sinon from 'sinon';
4
import { EVENTS } from '../../../src/constants.js';
5
import * as events from '../../../src/events.js';
6
import 'src/prebid.js';
7
import {attachRealTimeDataProvider, onDataDeletionRequest} from 'modules/rtdModule/index.js';
8
import {GDPR_GVLIDS} from '../../../src/consentHandler.js';
9
import {MODULE_TYPE_RTD} from '../../../src/activities/modules.js';
10
import {registerActivityControl} from '../../../src/activities/rules.js';
11
import {ACTIVITY_ENRICH_UFPD, ACTIVITY_TRANSMIT_EIDS} from '../../../src/activities/activities.js';
12

13
describe('Real time module', function () {
1✔
14
  let eventHandlers;
15
  let sandbox;
16
  let validSM, validSMWait, invalidSM, failureSM, nonConfSM, conf;
17
  let getBidRequestDataStub;
18

19
  function mockEmitEvent(event, ...args) {
18✔
20
    (eventHandlers[event] || []).forEach((h) => h(...args));
9!
21
  }
22

23
  before(() => {
1✔
24
    eventHandlers = {};
1✔
25
    sandbox = sinon.createSandbox();
1✔
26
    getBidRequestDataStub = sinon.stub();
1✔
27

28
    sandbox.stub(events, 'on').callsFake((event, handler) => {
1✔
29
      if (!eventHandlers.hasOwnProperty(event)) {
5✔
30
        eventHandlers[event] = [];
5✔
31
      }
32
      eventHandlers[event].push(handler);
5✔
33
    });
34
  });
35

36
  after(() => {
1✔
37
    sandbox.restore();
1✔
38
  });
39

40
  beforeEach(() => {
1✔
41
    validSM = {
20✔
42
      name: 'validSM',
43
      init: () => { return true },
8✔
44
      getTargetingData: (adUnitsCodes) => {
45
        return {'ad2': {'key': 'validSM'}}
1✔
46
      },
47
      getBidRequestData: getBidRequestDataStub
48
    };
49

50
    validSMWait = {
20✔
51
      name: 'validSMWait',
52
      init: () => { return true },
36✔
53
      getTargetingData: (adUnitsCodes) => {
54
        return {'ad1': {'key': 'validSMWait'}}
2✔
55
      },
56
      getBidRequestData: getBidRequestDataStub
57
    };
58

59
    invalidSM = {
20✔
60
      name: 'invalidSM'
61
    };
62

63
    failureSM = {
20✔
64
      name: 'failureSM',
65
      init: () => { return false }
22✔
66
    };
67

68
    nonConfSM = {
20✔
69
      name: 'nonConfSM',
UNCOV
70
      init: () => { return true }
×
71
    };
72

73
    conf = {
20✔
74
      'realTimeData': {
75
        'auctionDelay': 100,
76
        dataProviders: [
77
          {
78
            'name': 'validSMWait',
79
            'waitForIt': true,
80
          },
81
          {
82
            'name': 'validSM',
83
            'waitForIt': false,
84
          },
85
          {
86
            'name': 'invalidSM'
87
          },
88
          {
89
            'name': 'failureSM'
90
          }]
91
      }
92
    };
93
  })
94

95
  describe('GVL IDs', () => {
1✔
96
    beforeEach(() => {
1✔
97
      sinon.stub(GDPR_GVLIDS, 'register');
1✔
98
    });
99

100
    afterEach(() => {
1✔
101
      GDPR_GVLIDS.register.restore();
1✔
102
    });
103

104
    it('are registered when RTD module is registered', () => {
1✔
105
      let mod;
106
      try {
1✔
107
        mod = attachRealTimeDataProvider({name: 'mockRtd', gvlid: 123});
1✔
108
        sinon.assert.calledWith(GDPR_GVLIDS.register, MODULE_TYPE_RTD, 'mockRtd', 123);
1✔
109
      } finally {
110
        if (mod) {
1✔
111
          mod();
1✔
112
        }
113
      }
114
    })
115
  })
116

117
  describe('', () => {
1✔
118
    let PROVIDERS, _detachers, rules;
119

120
    beforeEach(function () {
1✔
121
      PROVIDERS = [validSM, invalidSM, failureSM, nonConfSM, validSMWait];
7✔
122
      _detachers = PROVIDERS.map(rtdModule.attachRealTimeDataProvider);
7✔
123
      rtdModule.init(config);
7✔
124
      config.setConfig(conf);
7✔
125
      rules = [
7✔
126
        registerActivityControl(ACTIVITY_TRANSMIT_EIDS, 'test', (params) => {
127
          return {allow: false};
2✔
128
        }),
129
        registerActivityControl(ACTIVITY_ENRICH_UFPD, 'test', (params) => {
130
          return {allow: false};
4✔
131
        })
132
      ]
133
    });
134

135
    afterEach(function () {
1✔
136
      _detachers.forEach((f) => f());
35✔
137
      config.resetConfig();
7✔
138
      rules.forEach(rule => rule());
14✔
139
    });
140

141
    it('should use only valid modules', function () {
1✔
142
      expect(rtdModule.subModules).to.eql([validSMWait, validSM]);
1✔
143
    });
144

145
    it('should be able to modify bid request', function (done) {
1✔
146
      const request = {bidRequest: {}};
1✔
147
      getBidRequestDataStub.callsFake((req) => {
1✔
148
        req.foo = 'bar';
2✔
149
      });
150
      rtdModule.setBidRequestsData(() => {
1✔
151
        assert(getBidRequestDataStub.calledTwice);
1✔
152
        assert(getBidRequestDataStub.calledWith(sinon.match({bidRequest: {}})));
1✔
153
        expect(request.foo).to.eql('bar');
1✔
154
        done();
1✔
155
      }, request)
156
    });
157

158
    it('should apply guard to modules, but not affect ortb2Fragments otherwise', (done) => {
1✔
159
      const ortb2Fragments = {
1✔
160
        global: {
161
          user: {
162
            eids: ['id']
163
          }
164
        },
165
        bidder: {
166
          bidderA: {
167
            user: {
168
              eids: ['bid']
169
            }
170
          }
171
        }
172
      };
173
      const request = {ortb2Fragments};
1✔
174
      getBidRequestDataStub.callsFake((req) => {
1✔
175
        expect(req.ortb2Fragments.global.user.eids).to.not.exist;
2✔
176
        expect(req.ortb2Fragments.bidder.bidderA.eids).to.not.exist;
2✔
177
        req.ortb2Fragments.global.user.yob = 123;
2✔
178
        req.ortb2Fragments.bidder.bidderB = {
2✔
179
          user: {
180
            yob: 123
181
          }
182
        };
183
      });
184
      rtdModule.setBidRequestsData(() => {
1✔
185
        expect(request.ortb2Fragments.global.user.eids).to.eql(['id']);
1✔
186
        expect(request.ortb2Fragments.bidder.bidderB?.user).to.not.exist;
1!
187
        done();
1✔
188
      }, request);
189
    });
190

191
    it('sould place targeting on adUnits', function (done) {
1✔
192
      const auction = {
1✔
193
        adUnitCodes: ['ad1', 'ad2'],
194
        adUnits: [
195
          {
196
            code: 'ad1'
197
          },
198
          {
199
            code: 'ad2',
200
            adserverTargeting: {preKey: 'preValue'}
201
          }
202
        ]
203
      };
204

205
      const expectedAdUnits = [
1✔
206
        {
207
          code: 'ad1',
208
          adserverTargeting: {key: 'validSMWait'}
209
        },
210
        {
211
          code: 'ad2',
212
          adserverTargeting: {
213
            preKey: 'preValue',
214
            key: 'validSM'
215
          }
216
        }
217
      ];
218

219
      const adUnits = rtdModule.getAdUnitTargeting(auction);
1✔
220
      assert.deepEqual(expectedAdUnits, adUnits)
1✔
221
      done();
1✔
222
    });
223

224
    it('should isolate targeting from different submodules', () => {
1✔
225
      const auction = {
1✔
226
        adUnitCodes: ['ad1', 'ad2'],
227
        adUnits: [
228
          {
229
            code: 'ad1'
230
          },
231
          {
232
            code: 'ad2',
233
          }
234
        ]
235
      };
236
      validSM.getTargetingData = (adUnits) => {
1✔
237
        const targeting = {'module1': 'targeting'}
1✔
238
        return {
1✔
239
          ad1: targeting,
240
          ad2: targeting
241
        }
242
      }
243

244
      rtdModule.getAdUnitTargeting(auction);
1✔
245
      expect(auction.adUnits[0].adserverTargeting).to.eql({
1✔
246
        module1: 'targeting',
247
        key: 'validSMWait'
248
      });
249
      expect(auction.adUnits[1].adserverTargeting).to.eql({
1✔
250
        module1: 'targeting'
251
      })
252
    })
253

254
    describe('setBidRequestData', () => {
1✔
255
      let withWait, withoutWait;
256

257
      function runSetBidRequestData() {
258
        return new Promise((resolve) => {
2✔
259
          rtdModule.setBidRequestsData(resolve, {bidRequest: {}});
2✔
260
        });
261
      }
262

263
      beforeEach(() => {
1✔
264
        withWait = {
2✔
265
          submod: validSMWait,
266
          cbTime: 0,
267
          cbRan: false
268
        };
269
        withoutWait = {
2✔
270
          submod: validSM,
271
          cbTime: 0,
272
          cbRan: false
273
        };
274

275
        [withWait, withoutWait].forEach((c) => {
2✔
276
          c.submod.getBidRequestData = sinon.stub().callsFake((_, cb) => {
4✔
277
            setTimeout(() => {
4✔
278
              c.cbRan = true;
4✔
279
              cb();
4✔
280
            }, c.cbTime);
281
          });
282
        });
283
      });
284

285
      it('should allow non-priority submodules to run synchronously', () => {
1✔
286
        withWait.cbTime = withoutWait.cbTime = 0;
1✔
287
        return runSetBidRequestData().then(() => {
1✔
288
          expect(withWait.cbRan).to.be.true;
1✔
289
          expect(withoutWait.cbRan).to.be.true;
1✔
290
        })
291
      });
292

293
      it('should not wait for non-priority submodules if priority ones complete first', () => {
1✔
294
        withWait.cbTime = 10;
1✔
295
        withoutWait.cbTime = 100;
1✔
296
        return runSetBidRequestData().then(() => {
1✔
297
          expect(withWait.cbRan).to.be.true;
1✔
298
          expect(withoutWait.cbRan).to.be.false;
1✔
299
        });
300
      });
301
    });
302
  });
303

304
  describe('event', () => {
1✔
305
    const TEST_EVENTS = {
1✔
306
      [EVENTS.AUCTION_INIT]: 'onAuctionInitEvent',
307
      [EVENTS.AUCTION_END]: 'onAuctionEndEvent',
308
      [EVENTS.BID_RESPONSE]: 'onBidResponseEvent',
309
      [EVENTS.BID_REQUESTED]: 'onBidRequestEvent'
310
    }
311
    const conf = {
1✔
312
      'realTimeData': {
313
        dataProviders: [
314
          {
315
            'name': 'tp1',
316
          },
317
          {
318
            'name': 'tp2',
319
          }
320
        ]
321
      }
322
    };
323
    let providers;
324
    let _detachers;
325

326
    function eventHandlingProvider(name) {
327
      const provider = {
18✔
328
        name: name,
329
        init: () => true,
27✔
330
      }
331
      Object.values(TEST_EVENTS).forEach((ev) => provider[ev] = sinon.spy());
72✔
332
      return provider;
18✔
333
    }
334

335
    beforeEach(() => {
1✔
336
      providers = [eventHandlingProvider('tp1'), eventHandlingProvider('tp2')];
9✔
337
      _detachers = providers.map(rtdModule.attachRealTimeDataProvider);
9✔
338
      rtdModule.init(config);
9✔
339
      config.setConfig(conf);
9✔
340
    });
341

342
    afterEach(() => {
1✔
343
      _detachers.forEach((d) => d())
18✔
344
      config.resetConfig();
9✔
345
    });
346

347
    it('should set targeting for auctionEnd', () => {
1✔
348
      providers.forEach(p => p.getTargetingData = sinon.spy());
2✔
349
      const auction = {
1✔
350
        adUnitCodes: ['a1'],
351
        adUnits: [{code: 'a1'}]
352
      };
353
      mockEmitEvent(EVENTS.AUCTION_END, auction);
1✔
354
      providers.forEach(p => {
1✔
355
        expect(p.getTargetingData.calledWith(auction.adUnitCodes)).to.be.true;
2✔
356
      });
357
    });
358

359
    Object.entries(TEST_EVENTS).forEach(([event, hook]) => {
4✔
360
      it(`'${event}' should be propagated to providers through '${hook}'`, () => {
4✔
361
        const eventArg = {};
4✔
362
        mockEmitEvent(event, eventArg);
4✔
363
        providers.forEach((provider) => {
4✔
364
          const providerConf = conf.realTimeData.dataProviders.find((cfg) => cfg.name === provider.name);
12✔
365
          expect(provider[hook].called).to.be.true;
8✔
366
          expect(provider[hook].args).to.have.length(1);
8✔
367
          expect(provider[hook].args[0]).to.include.members([eventArg, providerConf])
8✔
368
        })
369
      });
370

371
      it(`${event} should not fail to propagate elsewhere if a provider throws in its event handler`, () => {
4✔
372
        providers[0][hook] = function () { throw new Error() };
4✔
373
        mockEmitEvent(event);
4✔
374
        expect(providers[1][hook].called).to.be.true;
4✔
375
      });
376
    });
377
  });
378

379
  describe('data deletion requests', () => {
1✔
380
    let detach = () => null;
3✔
381

382
    function mkRtdModule(name) {
383
      const mod = {
6✔
384
        name,
385
        init: () => true,
9✔
386
        onDataDeletionRequest: sinon.stub()
387
      };
388
      detach = ((orig) => {
6✔
389
        const smDetach = attachRealTimeDataProvider(mod);
6✔
390
        return function () {
6✔
391
          orig();
12✔
392
          smDetach();
12✔
393
        }
394
      })(detach);
395
      return mod;
6✔
396
    }
397
    let sm1, sm2, cfg1, cfg2;
398
    beforeEach(() => {
1✔
399
      sm1 = mkRtdModule('mockMod1');
3✔
400
      sm2 = mkRtdModule('mockMod2');
3✔
401
      cfg1 = {
3✔
402
        name: 'mockMod1',
403
        i: 0
404
      };
405
      cfg2 = {
3✔
406
        name: 'mockMod2',
407
        i: 1
408
      };
409
      rtdModule.init(config);
3✔
410
      config.setConfig({
3✔
411
        realTimeData: {
412
          dataProviders: [cfg1, cfg2],
413
        }
414
      });
415
    });
416
    afterEach(() => {
1✔
417
      detach();
3✔
418
      config.resetConfig();
3✔
419
    });
420

421
    it('calls onDataDeletionRequest on submodules', () => {
1✔
422
      const next = sinon.stub();
1✔
423
      onDataDeletionRequest(next, {a: 0});
1✔
424
      sinon.assert.calledWith(next, {a: 0});
1✔
425
      sinon.assert.calledWith(sm1.onDataDeletionRequest, cfg1);
1✔
426
      sinon.assert.calledWith(sm2.onDataDeletionRequest, cfg2);
1✔
427
    });
428

429
    describe('does not choke if onDataDeletionRequest', () => {
1✔
430
      Object.entries({
1✔
431
        'is missing': () => { delete sm1.onDataDeletionRequest },
1✔
432
        'throws': () => { sm1.onDataDeletionRequest.throws(new Error()) }
1✔
433
      }).forEach(([t, setup]) => {
2✔
434
        it(t, () => {
2✔
435
          setup();
2✔
436
          onDataDeletionRequest(sinon.stub());
2✔
437
          sinon.assert.calledWith(sm2.onDataDeletionRequest, cfg2);
2✔
438
        })
439
      })
440
    })
441
  });
442
});
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc