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

prebid / Prebid.js / #305

27 Jun 2025 12:10PM UTC coverage: 90.409% (-0.01%) from 90.422%
#305

push

travis-ci

prebidjs-release
Prebid 9.52.0 release

43449 of 54513 branches covered (79.7%)

64185 of 70994 relevant lines covered (90.41%)

174.59 hits per line

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

72.0
/modules/bidViewability.js
1
// This module, when included, will trigger a BID_VIEWABLE event which can be consumed by Bidders and Analytics adapters
1✔
2
// GPT API is used to find when a bid is viewable, https://developers.google.com/publisher-tag/reference#googletag.events.impressionviewableevent
3
// Does not work with other than GPT integration
4

5
import {config} from '../src/config.js';
6
import * as events from '../src/events.js';
7
import {EVENTS} from '../src/constants.js';
8
import {isFn, logWarn, triggerPixel} from '../src/utils.js';
9
import {getGlobal} from '../src/prebidGlobal.js';
10
import adapterManager, {gppDataHandler, uspDataHandler} from '../src/adapterManager.js';
11
import {gdprParams} from '../libraries/dfpUtils/dfpUtils.js';
12

13
const MODULE_NAME = 'bidViewability';
1✔
14
const CONFIG_ENABLED = 'enabled';
1✔
15
const CONFIG_FIRE_PIXELS = 'firePixels';
1✔
16
const CONFIG_CUSTOM_MATCH = 'customMatchFunction';
1✔
17
const BID_VURL_ARRAY = 'vurls';
1✔
18
const GPT_IMPRESSION_VIEWABLE_EVENT = 'impressionViewable';
1✔
19

20
export let isBidAdUnitCodeMatchingSlot = (bid, slot) => {
1✔
21
  return (slot.getAdUnitPath() === bid.adUnitCode || slot.getSlotElementId() === bid.adUnitCode);
6✔
22
}
23

24
export let getMatchingWinningBidForGPTSlot = (globalModuleConfig, slot) => {
1✔
25
  return getGlobal().getAllWinningBids().find(
6✔
26
    // supports custom match function from config
27
    bid => isFn(globalModuleConfig[CONFIG_CUSTOM_MATCH])
4✔
28
      ? globalModuleConfig[CONFIG_CUSTOM_MATCH](bid, slot)
29
      : isBidAdUnitCodeMatchingSlot(bid, slot)
30
  ) || null;
31
};
32

33
export let fireViewabilityPixels = (globalModuleConfig, bid) => {
1✔
34
  if (globalModuleConfig[CONFIG_FIRE_PIXELS] === true && bid.hasOwnProperty(BID_VURL_ARRAY)) {
9✔
35
    let queryParams = gdprParams();
7✔
36

37
    const uspConsent = uspDataHandler.getConsentData();
7✔
38
    if (uspConsent) { queryParams.us_privacy = uspConsent; }
7✔
39

40
    const gppConsent = gppDataHandler.getConsentData();
7✔
41
    if (gppConsent) {
7!
42
      // TODO - need to know what to set here for queryParams...
43
    }
44

45
    bid[BID_VURL_ARRAY].forEach(url => {
7✔
46
      // add '?' if not present in URL
47
      if (Object.keys(queryParams).length > 0 && url.indexOf('?') === -1) {
21✔
48
        url += '?';
3✔
49
      }
50
      // append all query params, `&key=urlEncoded(value)`
51
      url += Object.keys(queryParams).reduce((prev, key) => prev += `&${key}=${encodeURIComponent(queryParams[key])}`, '');
21✔
52
      triggerPixel(url)
21✔
53
    });
54
  }
55
};
56

57
export let logWinningBidNotFound = (slot) => {
1✔
58
  logWarn(`bid details could not be found for ${slot.getSlotElementId()}, probable reasons: a non-prebid bid is served OR check the prebid.AdUnit.code to GPT.AdSlot relation.`);
×
59
};
60

61
export let impressionViewableHandler = (globalModuleConfig, event) => {
1✔
62
  const slot = event.slot;
2✔
63
  let respectiveBid = getMatchingWinningBidForGPTSlot(globalModuleConfig, slot);
2✔
64

65
  if (respectiveBid === null) {
2!
66
    logWinningBidNotFound(slot);
×
67
  } else {
68
    // if config is enabled AND VURL array is present then execute each pixel
69
    fireViewabilityPixels(globalModuleConfig, respectiveBid);
2✔
70
    // trigger respective bidder's onBidViewable handler
71
    adapterManager.callBidViewableBidder(respectiveBid.adapterCode || respectiveBid.bidder, respectiveBid);
2✔
72

73
    if (respectiveBid.deferBilling) {
2✔
74
      adapterManager.triggerBilling(respectiveBid);
1✔
75
    }
76

77
    // emit the BID_VIEWABLE event with bid details, this event can be consumed by bidders and analytics pixels
78
    events.emit(EVENTS.BID_VIEWABLE, respectiveBid);
2✔
79
  }
80
};
81

82
const handleSetConfig = (config) => {
1✔
83
  const globalModuleConfig = config || {};
×
84
  window.googletag = window.googletag || {};
×
85
  window.googletag.cmd = window.googletag.cmd || [];
×
86

87
  // do nothing if module-config.enabled is not set to true
88
  // this way we are adding a way for bidders to know (using pbjs.getConfig('bidViewability').enabled === true) whether this module is added in build and is enabled
89
  const impressionViewableHandlerWrapper = (event) => {
×
90
    window.googletag.pubads().removeEventListener(GPT_IMPRESSION_VIEWABLE_EVENT, impressionViewableHandlerWrapper);
×
91
    impressionViewableHandler(globalModuleConfig, event);
×
92
  };
93

94
  if (globalModuleConfig[CONFIG_ENABLED] !== true) {
×
95
    window.googletag.cmd.push(() => {
×
96
      window.googletag.pubads().removeEventListener(GPT_IMPRESSION_VIEWABLE_EVENT, impressionViewableHandlerWrapper);
×
97
    });
98
    return;
×
99
  }
100
  // add the GPT event listener
101
  window.googletag.cmd.push(() => {
×
102
    window.googletag.pubads().addEventListener(GPT_IMPRESSION_VIEWABLE_EVENT, impressionViewableHandlerWrapper);
×
103
  });
104
}
105

106
config.getConfig(MODULE_NAME, config => handleSetConfig(config[MODULE_NAME]));
1✔
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