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

prebid / Prebid.js / 16809528788

07 Aug 2025 03:57PM UTC coverage: 96.254%. Remained the same
16809528788

push

github

web-flow
Risemediatech Bid Adapter : New Bidder Adapter (#13600)

* RM-845 : Initial implementation for risemediatech bid adapter

* RM-845 : Added bidder parameter documentation for risemediatech Bid Adapter

* RM-845 : minor modifications

* RM-845 : Handled es lint errors

* RM-847 : Unit Test for Risemediatech Bid Adapter

* Updated unit tests

* Modified the bid adapter code and unit tests

* Modified prebid js code to remove validations and also added bidfloor to the request.

* added the vastxml field in the response for the video media type

* Fixed incorrect media type issue

* Added test mode impressions support

* Added test mode for video ad units

* Added bidfloor for example video ad unit

* Updated default TTL

* Minro fixes

* Update docs

* Minor changes

* Minor changes

* Code cleanup

* Changes as per review

* Semantic changes

* Added support for Http Status 204 No Bids Scenarios

* Updated failing unit tests.

* Modified the check for no bids

* Reverted the status check

* linter modifications

* Updated the documentation for the adapter and formatted adapter

* Modified the documentation as per discussion

* Resolved linter errors from upstream repo PR

39576 of 48646 branches covered (81.36%)

237 of 244 new or added lines in 2 files covered. (97.13%)

148 existing lines in 17 files now uncovered.

195874 of 203497 relevant lines covered (96.25%)

123.67 hits per line

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

99.49
/test/spec/modules/debugging_mod_spec.js
1
import {expect} from 'chai';
2
import {makebidInterceptor} from '../../../modules/debugging/bidInterceptor.js';
3
import {
4
  makeBidderBidInterceptor,
5
  disableDebugging,
6
  getConfig,
7
  sessionLoader,
8
} from '../../../modules/debugging/debugging.js';
9
import '../../../modules/debugging/index.js';
10
import {makePbsInterceptor} from '../../../modules/debugging/pbsInterceptor.js';
11
import {config} from '../../../src/config.js';
12
import {hook} from '../../../src/hook.js';
13
import {
14
  addBidderRequestsBound,
15
  addBidderRequestsHook,
16
  addBidResponseBound,
17
  addBidResponseHook,
18
} from '../../../modules/debugging/legacy.js';
19
import * as utils from '../../../src/utils.js';
20
import {addBidderRequests, addBidResponse} from '../../../src/auction.js';
21
import {prefixLog} from '../../../src/utils.js';
22
import {createBid} from '../../../src/bidfactory.js';
23
import {VIDEO, BANNER, NATIVE} from '../../../src/mediaTypes.js';
24
import {Renderer} from '../../../src/Renderer.js';
25

26
describe('bid interceptor', () => {
1✔
27
  let interceptor, mockSetTimeout;
28
  beforeEach(() => {
1✔
29
    mockSetTimeout = sinon.stub().callsFake((fn) => fn());
49✔
30
    const BidInterceptor = makebidInterceptor({utils, VIDEO, BANNER, NATIVE, Renderer})
49✔
31
    interceptor = new BidInterceptor({setTimeout: mockSetTimeout, logger: prefixLog('TEST')});
49✔
32
  });
33

34
  function setRules(...rules) {
82✔
35
    interceptor.updateConfig({
41✔
36
      intercept: rules
37
    });
38
  }
39

40
  describe('serializeConfig', () => {
1✔
41
    Object.entries({
1✔
42
      regexes: /pat/,
UNCOV
43
      functions: () => ({}),
×
44
      'undefined': undefined,
45
      date: new Date(),
46
      symbol: Symbol('test'),
47
      map: new Map(),
48
      set: new Set(),
49
    }).forEach(([test, arg]) => {
7✔
50
      it(`should filter out ${test}`, () => {
7✔
51
        const valid = [{key1: 'value'}, {key2: 'value'}];
7✔
52
        const ser = interceptor.serializeConfig([...valid, {outer: {inner: arg}}]);
7✔
53
        expect(ser).to.eql(valid);
7✔
54
      });
55
    });
56
  });
57

58
  describe('match()', () => {
1✔
59
    Object.entries({
1✔
60
      value: {key: 'value'},
61
      regex: {key: /^value$/},
62
      'function': (o) => o.key === 'value'
4✔
63
    }).forEach(([test, matcher]) => {
3✔
64
      describe(`by ${test}`, () => {
3✔
65
        it('should work on matching top-level properties', () => {
3✔
66
          setRules({when: matcher});
3✔
67
          const rule = interceptor.match({key: 'value'});
3✔
68
          expect(rule).to.not.eql(null);
3✔
69
        });
70

71
        it('should work on matching nested properties', () => {
3✔
72
          setRules({when: {outer: {inner: matcher}}});
3✔
73
          const rule = interceptor.match({outer: {inner: {key: 'value'}}});
3✔
74
          expect(rule).to.not.eql(null);
3✔
75
        });
76

77
        it('should not work on non-matching inputs', () => {
3✔
78
          setRules({when: matcher});
3✔
79
          expect(interceptor.match({key: 'different-value'})).to.not.be.ok;
3✔
80
          expect(interceptor.match({differentKey: 'value'})).to.not.be.ok;
3✔
81
        });
82
      });
83
    });
84

85
    it('should respect rule order', () => {
1✔
86
      setRules({when: {key: 'value'}}, {when: {}}, {when: {}});
1✔
87
      const rule = interceptor.match({});
1✔
88
      expect(rule.no).to.equal(2);
1✔
89
    });
90

91
    it('should pass extra arguments to property function matchers', () => {
1✔
92
      const matchDef = {
1✔
93
        key: sinon.stub(),
94
        outer: {inner: {key: sinon.stub()}}
95
      };
96
      const extraArgs = [{}, {}];
1✔
97
      setRules({when: matchDef});
1✔
98
      interceptor.match({key: {}, outer: {inner: {key: {}}}}, ...extraArgs);
1✔
99
      [matchDef.key, matchDef.outer.inner.key].forEach((fn) => {
1✔
100
        expect(fn.calledOnceWith(sinon.match.any, ...extraArgs.map(sinon.match.same))).to.be.true;
2✔
101
      });
102
    });
103

104
    it('should pass extra arguments to single-function matcher', () => {
1✔
105
      const matchDef = sinon.stub();
1✔
106
      setRules({when: matchDef});
1✔
107
      const args = [{}, {}, {}];
1✔
108
      interceptor.match(...args);
1✔
109
      expect(matchDef.calledOnceWith(...args.map(sinon.match.same))).to.be.true;
1✔
110
    });
111
  });
112

113
  describe('rule', () => {
1✔
114
    function matchingRule({replace, options, paapi}) {
21✔
115
      setRules({when: {}, then: replace, options: options, paapi});
21✔
116
      return interceptor.match({});
21✔
117
    }
118

119
    describe('.replace()', () => {
1✔
120
      const REQUIRED_KEYS = [
1✔
121
        // https://docs.prebid.org/dev-docs/bidder-adaptor.html#bidder-adaptor-Interpreting-the-Response
122
        'requestId', 'cpm', 'currency', 'width', 'height', 'ttl',
123
        'creativeId', 'netRevenue', 'meta', 'ad'
124
      ];
125
      it('should include required bid response keys by default', () => {
1✔
126
        expect(matchingRule({}).replace({})).to.include.keys(REQUIRED_KEYS);
1✔
127
      });
128

129
      Object.entries({
1✔
130
        value: {key: 'value'},
131
        'function': () => ({key: 'value'})
3✔
132
      }).forEach(([test, replDef]) => {
2✔
133
        describe(`by ${test}`, () => {
2✔
134
          it('should merge top-level properties with replace definition', () => {
2✔
135
            const result = matchingRule({replace: replDef}).replace({});
2✔
136
            expect(result).to.include.keys(REQUIRED_KEYS);
2✔
137
            expect(result.key).to.equal('value');
2✔
138
          });
139

140
          it('should merge nested properties with replace definition', () => {
2✔
141
            const result = matchingRule({replace: {outer: {inner: replDef}}}).replace({});
2✔
142
            expect(result).to.include.keys(REQUIRED_KEYS);
2✔
143
            expect(result.outer.inner).to.eql({key: 'value'});
2✔
144
          });
145

146
          it('should respect array vs object definitions', () => {
2✔
147
            const result = matchingRule({replace: {item: [replDef]}}).replace({});
2✔
148
            expect(result.item).to.be.an('array');
2✔
149
            expect(result.item.length).to.equal(1);
2✔
150
            expect(result.item[0]).to.eql({key: 'value'});
2✔
151
          });
152
        });
153
      });
154

155
      it('should pass extra arguments to single function replacer', () => {
1✔
156
        const replDef = sinon.stub();
1✔
157
        const args = [{}, {}, {}];
1✔
158
        matchingRule({replace: replDef}).replace(...args);
1✔
159
        expect(replDef.calledOnceWith(...args.map(sinon.match.same))).to.be.true;
1✔
160
      });
161

162
      it('should pass extra arguments to function property replacers', () => {
1✔
163
        const replDef = {
1✔
164
          key: sinon.stub(),
165
          outer: {inner: {key: sinon.stub()}}
166
        };
167
        const args = [{}, {}, {}];
1✔
168
        matchingRule({replace: replDef}).replace(...args);
1✔
169
        [replDef.key, replDef.outer.inner.key].forEach((repl) => {
1✔
170
          expect(repl.calledOnceWith(...args.map(sinon.match.same))).to.be.true;
2✔
171
        });
172
      });
173
    });
174

175
    describe('paapi', () => {
1✔
176
      it('should accept literals', () => {
1✔
177
        const mockConfig = [
1✔
178
          {config: {paapi: 1}},
179
          {config: {paapi: 2}}
180
        ]
181
        const paapi = matchingRule({paapi: mockConfig}).paapi({});
1✔
182
        expect(paapi).to.eql(mockConfig);
1✔
183
      });
184

185
      it('should accept a function and pass extra args to it', () => {
1✔
186
        const paapiDef = sinon.stub();
1✔
187
        const args = [{}, {}, {}];
1✔
188
        matchingRule({paapi: paapiDef}).paapi(...args);
1✔
189
        expect(paapiDef.calledOnceWith(...args.map(sinon.match.same))).to.be.true;
1✔
190
      });
191

192
      Object.entries({
1✔
193
        'literal': (cfg) => [cfg],
4✔
194
        'function': (cfg) => () => [cfg]
4✔
195
      }).forEach(([t, makeConfigs]) => {
2✔
196
        describe(`when paapi is defined as a ${t}`, () => {
2✔
197
          it('should wrap top-level configs in "config"', () => {
2✔
198
            const cfg = {decisionLogicURL: 'example'};
2✔
199
            expect(matchingRule({paapi: makeConfigs(cfg)}).paapi({})).to.eql([{
2✔
200
              config: cfg
201
            }])
202
          });
203

204
          Object.entries({
2✔
205
            'config': {config: 1},
206
            'igb': {igb: 1},
207
            'config and igb': {config: 1, igb: 2}
208
          }).forEach(([t, cfg]) => {
6✔
209
            it(`should not wrap configs that define top-level ${t}`, () => {
6✔
210
              expect(matchingRule({paapi: makeConfigs(cfg)}).paapi({})).to.eql([cfg]);
6✔
211
            })
212
          })
213
        })
214
      })
215
    })
216

217
    describe('.options', () => {
1✔
218
      it('should include default rule options', () => {
1✔
219
        const optDef = {someOption: 'value'};
1✔
220
        const ruleOptions = matchingRule({options: optDef}).options;
1✔
221
        expect(ruleOptions).to.include(optDef);
1✔
222
        expect(ruleOptions).to.include(interceptor.DEFAULT_RULE_OPTIONS);
1✔
223
      });
224

225
      it('should override defaults', () => {
1✔
226
        const optDef = {delay: 123};
1✔
227
        const ruleOptions = matchingRule({options: optDef}).options;
1✔
228
        expect(ruleOptions).to.eql(optDef);
1✔
229
      });
230
    });
231
  });
232

233
  describe('intercept()', () => {
1✔
234
    let done, addBid, addPaapiConfig;
235

236
    function intercept(args = {}) {
9✔
237
      const bidRequest = {bids: args.bids || []};
9✔
238
      return interceptor.intercept(Object.assign({bidRequest, done, addBid, addPaapiConfig}, args));
9✔
239
    }
240

241
    beforeEach(() => {
1✔
242
      done = sinon.spy();
9✔
243
      addBid = sinon.spy();
9✔
244
      addPaapiConfig = sinon.spy();
9✔
245
    });
246

247
    describe('on no match', () => {
1✔
248
      it('should return untouched bids and bidRequest', () => {
1✔
249
        const bids = [{}, {}];
1✔
250
        const bidRequest = {};
1✔
251
        const result = intercept({bids, bidRequest});
1✔
252
        expect(result.bids).to.equal(bids);
1✔
253
        expect(result.bidRequest).to.equal(bidRequest);
1✔
254
      });
255

256
      it('should call done() immediately', () => {
1✔
257
        intercept();
1✔
258
        expect(done.calledOnce).to.be.true;
1✔
259
        expect(mockSetTimeout.args[0][1]).to.equal(0);
1✔
260
      });
261

262
      it('should not call addBid', () => {
1✔
263
        intercept();
1✔
264
        expect(addBid.called).to.not.be.ok;
1✔
265
      });
266
    });
267

268
    describe('on match', () => {
1✔
269
      let match1, match2, repl1, repl2;
270
      const DELAY_1 = 123;
1✔
271
      const DELAY_2 = 321;
1✔
272
      const REQUEST = {
1✔
273
        bids: [
274
          {id: 1, match: false},
275
          {id: 2, match: 1},
276
          {id: 3, match: 2}
277
        ]
278
      };
279

280
      beforeEach(() => {
1✔
281
        match1 = sinon.stub().callsFake((bid) => bid.match === 1);
12✔
282
        match2 = sinon.stub().callsFake((bid) => bid.match === 2);
8✔
283
        repl1 = sinon.stub().returns({replace: 1});
6✔
284
        repl2 = sinon.stub().returns({replace: 2});
6✔
285
        setRules(
6✔
286
          {when: match1, then: repl1, options: {delay: DELAY_1}},
287
          {when: match2, then: repl2, options: {delay: DELAY_2}},
288
        );
289
      });
290

291
      it('should return only non-matching bids', () => {
1✔
292
        const {bids, bidRequest} = intercept({bidRequest: REQUEST});
1✔
293
        expect(bids).to.eql([REQUEST.bids[0]]);
1✔
294
        expect(bidRequest.bids).to.eql([REQUEST.bids[0]]);
1✔
295
      });
296

297
      it('should call addBid for each matching bid', () => {
1✔
298
        intercept({bidRequest: REQUEST});
1✔
299
        expect(addBid.callCount).to.equal(2);
1✔
300
        expect(addBid.calledWith(sinon.match({replace: 1, isDebug: true}), REQUEST.bids[1])).to.be.true;
1✔
301
        expect(addBid.calledWith(sinon.match({replace: 2, isDebug: true}), REQUEST.bids[2])).to.be.true;
1✔
302
        [DELAY_1, DELAY_2].forEach((delay) => {
1✔
303
          expect(mockSetTimeout.calledWith(sinon.match.any, delay)).to.be.true;
2✔
304
        });
305
      });
306

307
      it('should call addPaapiConfigs when provided', () => {
1✔
308
        const mockPaapiConfigs = [
1✔
309
          {config: {paapi: 1}},
310
          {config: {paapi: 2}}
311
        ]
312
        setRules({
1✔
313
          when: {id: 2},
314
          paapi: mockPaapiConfigs,
315
        });
316
        intercept({bidRequest: REQUEST});
1✔
317
        expect(addPaapiConfig.callCount).to.eql(2);
1✔
318
        mockPaapiConfigs.forEach(cfg => sinon.assert.calledWith(addPaapiConfig, cfg))
2✔
319
      })
320

321
      it('should not call onBid when then is null', () => {
1✔
322
        setRules({
1✔
323
          when: {id: 2},
324
          then: null
325
        });
326
        intercept({bidRequest: REQUEST});
1✔
327
        sinon.assert.notCalled(addBid);
1✔
328
      })
329

330
      it('should call done()', () => {
1✔
331
        intercept({bidRequest: REQUEST});
1✔
332
        expect(done.calledOnce).to.be.true;
1✔
333
      });
334

335
      it('should pass bid and bidRequest to match and replace functions', () => {
1✔
336
        intercept({bidRequest: REQUEST});
1✔
337
        Object.entries({
1✔
338
          1: [match1, repl1],
339
          2: [match2, repl2]
340
        }).forEach(([index, fns]) => {
2✔
341
          fns.forEach((fn) => {
2✔
342
            expect(fn.calledWith(REQUEST.bids[index], REQUEST)).to.be.true;
4✔
343
          });
344
        });
345
      });
346
    });
347
  });
348
});
349

350
describe('Debugging config', () => {
1✔
351
  it('should behave gracefully when sessionStorage throws', () => {
1✔
352
    const logError = sinon.stub();
1✔
353
    const getStorage = () => { throw new Error() };
1✔
354
    getConfig({enabled: false}, {getStorage, logger: {logError}, hook, utils});
1✔
355
    expect(logError.called).to.be.true;
1✔
356
  });
357
});
358

359
describe('bidderBidInterceptor', () => {
1✔
360
  let next, interceptBids, onCompletion, interceptResult, done, addBid, wrapCallback, addPaapiConfig, wrapped, bidderBidInterceptor;
361

362
  function interceptorArgs({spec = {}, bids = [], bidRequest = {}, ajax = {}, cbs = {}} = {}) {
7✔
363
    return [next, interceptBids, spec, bids, bidRequest, ajax, wrapCallback, Object.assign({onCompletion}, cbs)];
7✔
364
  }
365

366
  beforeEach(() => {
1✔
367
    bidderBidInterceptor = makeBidderBidInterceptor({utils});
7✔
368
    next = sinon.spy();
7✔
369
    wrapped = false;
7✔
370
    wrapCallback = sinon.stub().callsFake(cb => {
7✔
371
      if (cb == null) return cb;
14✔
372
      return function () {
9✔
373
        wrapped = true;
2✔
374
        try {
2✔
375
          return cb.apply(this, arguments)
2✔
376
        } finally {
377
          wrapped = false;
2✔
378
        }
379
      }
380
    });
381
    interceptBids = sinon.stub().callsFake((opts) => {
7✔
382
      done = opts.done;
7✔
383
      addBid = opts.addBid;
7✔
384
      addPaapiConfig = opts.addPaapiConfig;
7✔
385
      return interceptResult;
7✔
386
    });
387
    onCompletion = sinon.spy();
7✔
388
    interceptResult = {bids: [], bidRequest: {}};
7✔
389
  });
390

391
  it('should pass to interceptBid an addBid that triggers onBid', () => {
1✔
392
    const onBid = sinon.stub().callsFake(() => {
1✔
393
      expect(wrapped).to.be.true;
1✔
394
    });
395
    bidderBidInterceptor(...interceptorArgs({cbs: {onBid}}));
1✔
396
    const bid = {
1✔
397
      bidder: 'bidder'
398
    };
399
    addBid(bid);
1✔
400
    expect(onBid.calledWith(sinon.match.same(bid))).to.be.true;
1✔
401
  });
402

403
  it('should pass addPaapiConfig that triggers onPaapi', () => {
1✔
404
    const onPaapi = sinon.stub().callsFake(() => {
1✔
405
      expect(wrapped).to.be.true;
1✔
406
    });
407
    bidderBidInterceptor(...interceptorArgs({cbs: {onPaapi}}));
1✔
408
    addPaapiConfig({paapi: 'config'}, {bidId: 'bidId'});
1✔
409
    sinon.assert.calledWith(onPaapi, {paapi: 'config', bidId: 'bidId'})
1✔
410
  })
411

412
  describe('with no remaining bids', () => {
1✔
413
    it('should pass a done callback that triggers onCompletion', () => {
1✔
414
      bidderBidInterceptor(...interceptorArgs());
1✔
415
      expect(onCompletion.calledOnce).to.be.false;
1✔
416
      interceptBids.args[0][0].done();
1✔
417
      expect(onCompletion.calledOnce).to.be.true;
1✔
418
    });
419

420
    it('should call onResponse', () => {
1✔
421
      const onResponse = sinon.stub();
1✔
422
      bidderBidInterceptor(...interceptorArgs({cbs: {onResponse}}));
1✔
423
      sinon.assert.called(onResponse);
1✔
424
    })
425

426
    it('should not call next()', () => {
1✔
427
      bidderBidInterceptor(...interceptorArgs());
1✔
428
      expect(next.called).to.be.false;
1✔
429
    });
430
  });
431

432
  describe('with remaining bids', () => {
1✔
433
    const REMAINING_BIDS = [{id: 1}, {id: 2}];
1✔
434
    beforeEach(() => {
1✔
435
      interceptResult = {bids: REMAINING_BIDS, bidRequest: {bids: REMAINING_BIDS}};
2✔
436
    });
437

438
    it('should call next', () => {
1✔
439
      const callbacks = {
1✔
440
        onResponse: {},
441
        onRequest: {},
442
        onBid: {}
443
      };
444
      const args = interceptorArgs({cbs: callbacks});
1✔
445
      const expectedNextArgs = [
1✔
446
        args[2],
447
        interceptResult.bids,
448
        interceptResult.bidRequest,
449
        ...args.slice(5, args.length - 1),
450
      ].map(sinon.match.same)
451
        .concat([sinon.match({
452
          onResponse: sinon.match.same(callbacks.onResponse),
453
          onRequest: sinon.match.same(callbacks.onRequest),
454
          onBid: sinon.match.same(callbacks.onBid)
455
        })]);
456
      bidderBidInterceptor(...args);
1✔
457
      expect(next.calledOnceWith(...expectedNextArgs)).to.be.true;
1✔
458
    });
459

460
    it('should trigger onCompletion once both interceptBids.done and next.cbs.onCompletion are called ', () => {
1✔
461
      bidderBidInterceptor(...interceptorArgs());
1✔
462
      expect(onCompletion.calledOnce).to.be.false;
1✔
463
      next.args[0][next.args[0].length - 1].onCompletion();
1✔
464
      expect(onCompletion.calledOnce).to.be.false;
1✔
465
      done();
1✔
466
      expect(onCompletion.calledOnce).to.be.true;
1✔
467
    });
468
  });
469
});
470

471
describe('pbsBidInterceptor', () => {
1✔
472
  const EMPTY_INT_RES = {bids: [], bidRequest: {bids: []}};
1✔
473
  let next, interceptBids, s2sBidRequest, bidRequests, ajax, onResponse, onError, onBid, interceptResults,
474
    addBids, dones, reqIdx;
475

476
  beforeEach(() => {
1✔
477
    reqIdx = 0;
7✔
478
    [addBids, dones] = [[], []];
7✔
479
    next = sinon.spy();
7✔
480
    ajax = sinon.spy();
7✔
481
    onResponse = sinon.spy();
7✔
482
    onError = sinon.spy();
7✔
483
    onBid = sinon.spy();
7✔
484
    interceptBids = sinon.stub().callsFake((opts) => {
7✔
485
      addBids.push(opts.addBid);
18✔
486
      dones.push(opts.done);
18✔
487
      return interceptResults[reqIdx++];
18✔
488
    });
489
    s2sBidRequest = {};
7✔
490
    bidRequests = [{bids: []}, {bids: []}];
7✔
491
    interceptResults = [EMPTY_INT_RES, EMPTY_INT_RES];
7✔
492
  });
493

494
  const pbsBidInterceptor = makePbsInterceptor({createBid, utils});
1✔
495
  function callInterceptor() {
496
    return pbsBidInterceptor(next, interceptBids, s2sBidRequest, bidRequests, ajax, {onResponse, onError, onBid});
7✔
497
  }
498

499
  it('passes addBids that trigger onBid', () => {
1✔
500
    callInterceptor();
1✔
501
    bidRequests.forEach((_, i) => {
1✔
502
      const bid = {adUnitCode: i, prop: i};
2✔
503
      const bidRequest = {req: i};
2✔
504
      addBids[i](bid, bidRequest);
2✔
505
      expect(onBid.calledWith({adUnit: i, bid: sinon.match(bid)}));
2✔
506
    });
507
  });
508

509
  describe('on no match', () => {
1✔
510
    it('should not call next', () => {
1✔
511
      callInterceptor();
1✔
512
      expect(next.called).to.be.false;
1✔
513
    });
514

515
    it('should pass done callbacks that trigger a dummy onResponse once they all run', () => {
1✔
516
      callInterceptor();
1✔
517
      expect(onResponse.called).to.be.false;
1✔
518
      bidRequests.forEach((_, i) => {
1✔
519
        dones[i]();
2✔
520
        expect(onResponse.called).to.equal(i === bidRequests.length - 1);
2✔
521
      });
522
      expect(onResponse.calledWith(true, [])).to.be.true;
1✔
523
    });
524
  });
525

526
  describe('on match', () => {
1✔
527
    let matchingBids;
528
    beforeEach(() => {
1✔
529
      matchingBids = [
4✔
530
        [{bidId: 1, matching: true}, {bidId: 2, matching: true}],
531
        [],
532
        [{bidId: 3, matching: true}]
533
      ];
534
      interceptResults = matchingBids.map((bids) => ({bids, bidRequest: {bids}}));
12✔
535
      s2sBidRequest = {
4✔
536
        ad_units: [
537
          {bids: [{bid_id: 1, matching: true}, {bid_id: 3, matching: true}, {bid_id: 100}, {bid_id: 101}]},
538
          {bids: [{bid_id: 2, matching: true}, {bid_id: 110}, {bid_id: 111}]},
539
          {bids: [{bid_id: 120}]}
540
        ]
541
      };
542
      bidRequests = matchingBids.map((mBids, i) => [
12✔
543
        {bidId: 100 + (i * 10)},
544
        {bidId: 101 + (i * 10)},
545
        ...mBids
546
      ]);
547
    });
548

549
    it('should call next', () => {
1✔
550
      callInterceptor();
1✔
551
      expect(next.calledOnceWith(
1✔
552
        sinon.match.any,
553
        sinon.match.any,
554
        ajax,
555
        sinon.match({
556
          onError,
557
          onBid
558
        })
559
      )).to.be.true;
560
    });
561

562
    it('should filter out intercepted bids from s2sBidRequest', () => {
1✔
563
      callInterceptor();
1✔
564
      const interceptedS2SReq = next.args[0][0];
1✔
565
      const allMatching = interceptedS2SReq.ad_units.every((u) => u.bids.length > 0 && u.bids.every((b) => b.matching));
3✔
566
      expect(allMatching).to.be.true;
1✔
567
    });
568

569
    it('should pass bidRequests as returned by interceptBids', () => {
1✔
570
      callInterceptor();
1✔
571
      const passedBidReqs = next.args[0][1];
1✔
572
      interceptResults
1✔
573
        .filter((r) => r.bids.length > 0)
3✔
574
        .forEach(({bidRequest}, i) => {
2✔
575
          expect(passedBidReqs[i]).to.equal(bidRequest);
2✔
576
        });
577
    });
578

579
    it('should pass an onResponse that triggers original onResponse only once all intercept dones are called', () => {
1✔
580
      callInterceptor();
1✔
581
      const interceptedOnResponse = next.args[0][next.args[0].length - 1].onResponse;
1✔
582
      expect(onResponse.called).to.be.false;
1✔
583
      const responseArgs = ['dummy', 'args'];
1✔
584
      interceptedOnResponse(...responseArgs);
1✔
585
      expect(onResponse.called).to.be.false;
1✔
586
      dones.forEach((f, i) => {
1✔
587
        f();
3✔
588
        expect(onResponse.called).to.equal(i === dones.length - 1);
3✔
589
      });
590
      expect(onResponse.calledOnceWith(...responseArgs)).to.be.true;
1✔
591
    });
592
  });
593
});
594

595
describe('bid overrides', function () {
1✔
596
  let sandbox;
597
  const logger = prefixLog('TEST');
1✔
598

599
  beforeEach(function () {
1✔
600
    sandbox = sinon.createSandbox();
8✔
601
  });
602

603
  afterEach(function () {
1✔
604
    window.sessionStorage.clear();
8✔
605
    config.resetConfig();
8✔
606
    sandbox.restore();
8✔
607
  });
608

609
  describe('initialization', function () {
1✔
610
    beforeEach(function () {
1✔
611
      sandbox.stub(config, 'setConfig');
3✔
612
    });
613

614
    afterEach(function () {
1✔
615
      disableDebugging({hook, logger});
3✔
616
    });
617

618
    it('should happen when enabled with setConfig', function () {
1✔
619
      getConfig({
1✔
620
        enabled: true
621
      }, {config, hook, logger, utils});
622

623
      expect(addBidResponse.getHooks().some(hook => hook.hook === addBidResponseBound)).to.equal(true);
4✔
624
      expect(addBidderRequests.getHooks().some(hook => hook.hook === addBidderRequestsBound)).to.equal(true);
2✔
625
    });
626
    it('should happen when configuration found in sessionStorage', function () {
1✔
627
      sessionLoader({
1✔
628
        storage: {getItem: () => ('{"enabled": true}')},
1✔
629
        config,
630
        hook,
631
        logger
632
      });
633
      expect(addBidResponse.getHooks().some(hook => hook.hook === addBidResponseBound)).to.equal(true);
4✔
634
      expect(addBidderRequests.getHooks().some(hook => hook.hook === addBidderRequestsBound)).to.equal(true);
2✔
635
    });
636

637
    it('should not throw if sessionStorage is inaccessible', function () {
1✔
638
      expect(() => {
1✔
639
        sessionLoader({
1✔
640
          getItem() {
UNCOV
641
            throw new Error('test');
×
642
          }
643
        });
644
      }).not.to.throw();
645
    });
646
  });
647

648
  describe('bidResponse hook', function () {
1✔
649
    let mockBids;
650
    let bids;
651

652
    beforeEach(function () {
1✔
653
      const baseBid = {
4✔
654
        'bidderCode': 'rubicon',
655
        'width': 970,
656
        'height': 250,
657
        'statusMessage': 'Bid available',
658
        'mediaType': 'banner',
659
        'source': 'client',
660
        'currency': 'USD',
661
        'cpm': 0.5,
662
        'ttl': 300,
663
        'netRevenue': false,
664
        'adUnitCode': '/19968336/header-bid-tag-0'
665
      };
666
      mockBids = [];
4✔
667
      mockBids.push(baseBid);
4✔
668
      mockBids.push(Object.assign({}, baseBid, {
4✔
669
        bidderCode: 'appnexus'
670
      }));
671

672
      bids = [];
4✔
673
    });
674

675
    function run(overrides) {
676
      mockBids.forEach(bid => {
4✔
677
        const next = (adUnitCode, bid) => {
8✔
678
          bids.push(bid);
7✔
679
        };
680
        addBidResponseHook.bind({overrides, logger})(next, bid.adUnitCode, bid);
8✔
681
      });
682
    }
683

684
    it('should allow us to exclude bidders', function () {
1✔
685
      run({
1✔
686
        enabled: true,
687
        bidders: ['appnexus']
688
      });
689

690
      expect(bids.length).to.equal(1);
1✔
691
      expect(bids[0].bidderCode).to.equal('appnexus');
1✔
692
    });
693

694
    it('should allow us to override all bids', function () {
1✔
695
      run({
1✔
696
        enabled: true,
697
        bids: [{
698
          cpm: 2
699
        }]
700
      });
701

702
      expect(bids.length).to.equal(2);
1✔
703
      sinon.assert.match(bids[0], {
1✔
704
        cpm: 2,
705
        isDebug: true,
706
      });
707
      sinon.assert.match(bids[1], {
1✔
708
        cpm: 2,
709
        isDebug: true,
710
      });
711
    });
712

713
    it('should allow us to override bids by bidder', function () {
1✔
714
      run({
1✔
715
        enabled: true,
716
        bids: [{
717
          bidder: 'rubicon',
718
          cpm: 2
719
        }]
720
      });
721

722
      expect(bids.length).to.equal(2);
1✔
723
      sinon.assert.match(bids[0], {
1✔
724
        cpm: 2,
725
        isDebug: true
726
      });
727
      sinon.assert.match(bids[1], {
1✔
728
        cpm: 0.5,
729
        isDebug: sinon.match.falsy
730
      });
731
    });
732

733
    it('should allow us to override bids by adUnitCode', function () {
1✔
734
      mockBids[1].adUnitCode = 'test';
1✔
735

736
      run({
1✔
737
        enabled: true,
738
        bids: [{
739
          adUnitCode: 'test',
740
          cpm: 2
741
        }]
742
      });
743

744
      expect(bids.length).to.equal(2);
1✔
745
      sinon.assert.match(bids[0], {
1✔
746
        cpm: 0.5,
747
        isDebug: sinon.match.falsy,
748
      });
749
      sinon.assert.match(bids[1], {
1✔
750
        cpm: 2,
751
        isDebug: true,
752
      });
753
    });
754
  });
755

756
  describe('bidRequests hook', function () {
1✔
757
    let mockBidRequests;
758
    let bidderRequests;
759

760
    beforeEach(function () {
1✔
761
      const baseBidderRequest = {
1✔
762
        'bidderCode': 'rubicon',
763
        'bids': [{
764
          'width': 970,
765
          'height': 250,
766
          'statusMessage': 'Bid available',
767
          'mediaType': 'banner',
768
          'source': 'client',
769
          'currency': 'USD',
770
          'cpm': 0.5,
771
          'ttl': 300,
772
          'netRevenue': false,
773
          'adUnitCode': '/19968336/header-bid-tag-0'
774
        }]
775
      };
776
      mockBidRequests = [];
1✔
777
      mockBidRequests.push(baseBidderRequest);
1✔
778
      mockBidRequests.push(Object.assign({}, baseBidderRequest, {
1✔
779
        bidderCode: 'appnexus'
780
      }));
781

782
      bidderRequests = [];
1✔
783
    });
784

785
    function run(overrides) {
786
      const next = (b) => {
1✔
787
        bidderRequests = b;
1✔
788
      };
789
      addBidderRequestsHook.bind({overrides, logger})(next, mockBidRequests);
1✔
790
    }
791

792
    it('should allow us to exclude bidders', function () {
1✔
793
      run({
1✔
794
        enabled: true,
795
        bidders: ['appnexus']
796
      });
797

798
      expect(bidderRequests.length).to.equal(1);
1✔
799
      expect(bidderRequests[0].bidderCode).to.equal('appnexus');
1✔
800
    });
801
  });
802
});
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