prebid.js 9.53.3 → 9.53.4
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.
- package/dist/33acrossAnalyticsAdapter.js +1 -1
- package/dist/33acrossBidAdapter.js +1 -1
- package/dist/33acrossIdSystem.js +1 -1
- package/dist/BTBidAdapter.js +1 -1
- package/dist/adagioAnalyticsAdapter.js +1 -1
- package/dist/adagioBidAdapter.js +1 -1
- package/dist/adagioUtils.js +1 -1
- package/dist/addefendBidAdapter.js +1 -1
- package/dist/adgenerationBidAdapter.js +1 -1
- package/dist/adlooxRtdProvider.js +1 -1
- package/dist/adqueryBidAdapter.js +1 -1
- package/dist/adrelevantisBidAdapter.js +1 -1
- package/dist/adstirBidAdapter.js +1 -1
- package/dist/adtrgtmeBidAdapter.js +1 -1
- package/dist/adxcgAnalyticsAdapter.js +1 -1
- package/dist/adxcgBidAdapter.js +1 -1
- package/dist/adyoulikeBidAdapter.js +1 -1
- package/dist/agmaAnalyticsAdapter.js +1 -1
- package/dist/ajaBidAdapter.js +1 -1
- package/dist/amxBidAdapter.js +1 -1
- package/dist/amxIdSystem.js +1 -1
- package/dist/aniviewBidAdapter.js +1 -1
- package/dist/appierAnalyticsAdapter.js +1 -1
- package/dist/appnexusBidAdapter.js +1 -1
- package/dist/asoBidAdapter.js +1 -1
- package/dist/axonixBidAdapter.js +1 -1
- package/dist/beopBidAdapter.js +1 -1
- package/dist/bidderTimeoutUtils.js +1 -0
- package/dist/bidglassBidAdapter.js +1 -1
- package/dist/big-richmediaBidAdapter.js +1 -1
- package/dist/bitmediaBidAdapter.js +1 -1
- package/dist/bridBidAdapter.js +1 -1
- package/dist/bridgeuppBidAdapter.js +1 -1
- package/dist/bridgewellBidAdapter.js +1 -1
- package/dist/brightMountainMediaBidAdapter.js +1 -1
- package/dist/carodaBidAdapter.js +1 -1
- package/dist/chtnwBidAdapter.js +1 -1
- package/dist/chunk-core.js +1 -1
- package/dist/concertBidAdapter.js +1 -1
- package/dist/connectadBidAdapter.js +1 -1
- package/dist/consumableBidAdapter.js +1 -1
- package/dist/contxtfulBidAdapter.js +1 -1
- package/dist/conversantAnalyticsAdapter.js +1 -1
- package/dist/conversantBidAdapter.js +1 -1
- package/dist/craftBidAdapter.js +1 -1
- package/dist/criteoBidAdapter.js +1 -1
- package/dist/cwireBidAdapter.js +1 -1
- package/dist/dailymotionBidAdapter.js +1 -1
- package/dist/dependencies.json +10 -1
- package/dist/dspxBidAdapter.js +1 -1
- package/dist/dxkultureBidAdapter.js +1 -1
- package/dist/eplanningBidAdapter.js +1 -1
- package/dist/equativBidAdapter.js +1 -1
- package/dist/eskimiBidAdapter.js +1 -1
- package/dist/euidIdSystem.js +1 -1
- package/dist/exadsBidAdapter.js +1 -1
- package/dist/excoBidAdapter.js +1 -1
- package/dist/feedadBidAdapter.js +1 -1
- package/dist/finativeBidAdapter.js +1 -1
- package/dist/freewheel-sspBidAdapter.js +1 -1
- package/dist/fwsspBidAdapter.js +1 -1
- package/dist/gmosspBidAdapter.js +1 -1
- package/dist/greenbidsAnalyticsAdapter.js +1 -1
- package/dist/greenbidsBidAdapter.js +1 -1
- package/dist/greenbidsRtdProvider.js +1 -1
- package/dist/gridBidAdapter.js +1 -1
- package/dist/gumgumBidAdapter.js +1 -1
- package/dist/h12mediaBidAdapter.js +1 -1
- package/dist/hypelabBidAdapter.js +1 -1
- package/dist/id5AnalyticsAdapter.js +1 -1
- package/dist/id5IdSystem.js +1 -1
- package/dist/imdsBidAdapter.js +1 -1
- package/dist/improvedigitalBidAdapter.js +1 -1
- package/dist/inmobiBidAdapter.js +1 -1
- package/dist/insticatorBidAdapter.js +1 -1
- package/dist/intentIqAnalyticsAdapter.js +1 -1
- package/dist/ixBidAdapter.js +1 -1
- package/dist/jixieBidAdapter.js +1 -1
- package/dist/justpremiumBidAdapter.js +1 -1
- package/dist/kargoBidAdapter.js +1 -1
- package/dist/kimberliteBidAdapter.js +1 -1
- package/dist/konduitAnalyticsAdapter.js +1 -1
- package/dist/kueezBidAdapter.js +1 -1
- package/dist/lassoBidAdapter.js +1 -1
- package/dist/lifestreetBidAdapter.js +1 -1
- package/dist/liveIntentId.js +1 -1
- package/dist/logicadBidAdapter.js +1 -1
- package/dist/loglyliftBidAdapter.js +1 -1
- package/dist/luceadBidAdapter.js +1 -1
- package/dist/mabidderBidAdapter.js +1 -1
- package/dist/madsenseBidAdapter.js +1 -1
- package/dist/magniteAnalyticsAdapter.js +1 -1
- package/dist/malltvAnalyticsAdapter.js +1 -1
- package/dist/marsmediaBidAdapter.js +1 -1
- package/dist/mediafuseBidAdapter.js +1 -1
- package/dist/medianetBidAdapter.js +1 -1
- package/dist/medianetUtils.js +1 -1
- package/dist/mediasquareBidAdapter.js +1 -1
- package/dist/mgidBidAdapter.js +1 -1
- package/dist/missenaBidAdapter.js +1 -1
- package/dist/mobilefuseBidAdapter.js +1 -1
- package/dist/nextMillenniumBidAdapter.js +1 -1
- package/dist/nexx360Utils.js +1 -1
- package/dist/nobidAnalyticsAdapter.js +1 -1
- package/dist/nobidBidAdapter.js +1 -1
- package/dist/nodalsAiRtdProvider.js +1 -1
- package/dist/not-for-prod/prebid.js +175 -172
- package/dist/objectGuard.js +1 -1
- package/dist/oguryBidAdapter.js +1 -1
- package/dist/onetagBidAdapter.js +1 -1
- package/dist/ooloAnalyticsAdapter.js +1 -1
- package/dist/openxBidAdapter.js +1 -1
- package/dist/optableRtdProvider.js +1 -1
- package/dist/optidigitalBidAdapter.js +1 -1
- package/dist/orbidderBidAdapter.js +1 -1
- package/dist/outbrainBidAdapter.js +1 -1
- package/dist/pixfutureBidAdapter.js +1 -1
- package/dist/publinkIdSystem.js +1 -1
- package/dist/pubmaticAnalyticsAdapter.js +1 -1
- package/dist/pubmaticBidAdapter.js +1 -1
- package/dist/pubmaticRtdProvider.js +1 -1
- package/dist/pubmaticUtils.js +1 -0
- package/dist/pubwiseAnalyticsAdapter.js +1 -1
- package/dist/pubxaiAnalyticsAdapter.js +1 -1
- package/dist/pxyzBidAdapter.js +1 -1
- package/dist/quantcastBidAdapter.js +1 -1
- package/dist/readpeakBidAdapter.js +1 -1
- package/dist/relaidoBidAdapter.js +1 -1
- package/dist/retailspotBidAdapter.js +1 -1
- package/dist/rhythmoneBidAdapter.js +1 -1
- package/dist/riseUtils.js +1 -1
- package/dist/rtdModule.js +1 -1
- package/dist/rubiconBidAdapter.js +1 -1
- package/dist/seedingAllianceBidAdapter.js +1 -1
- package/dist/seedtagBidAdapter.js +1 -1
- package/dist/sevioBidAdapter.js +1 -0
- package/dist/sharethroughAnalyticsAdapter.js +1 -1
- package/dist/sharethroughBidAdapter.js +1 -1
- package/dist/showheroes-bsBidAdapter.js +1 -1
- package/dist/smaatoBidAdapter.js +1 -1
- package/dist/smartadserverBidAdapter.js +1 -1
- package/dist/smartxBidAdapter.js +1 -1
- package/dist/smilewantedBidAdapter.js +1 -1
- package/dist/snigelBidAdapter.js +1 -1
- package/dist/sonobiBidAdapter.js +1 -1
- package/dist/sovrnBidAdapter.js +1 -1
- package/dist/sparteoBidAdapter.js +1 -1
- package/dist/sspBCBidAdapter.js +1 -1
- package/dist/stvBidAdapter.js +1 -1
- package/dist/sublimeBidAdapter.js +1 -1
- package/dist/taboolaBidAdapter.js +1 -1
- package/dist/tappxBidAdapter.js +1 -1
- package/dist/targetVideoBidAdapter.js +1 -1
- package/dist/teadsBidAdapter.js +1 -1
- package/dist/terceptAnalyticsAdapter.js +1 -1
- package/dist/themoneytizerBidAdapter.js +1 -1
- package/dist/timeoutRtdProvider.js +1 -1
- package/dist/trionBidAdapter.js +1 -1
- package/dist/tripleliftBidAdapter.js +1 -1
- package/dist/ttdBidAdapter.js +1 -1
- package/dist/ucfunnelAnalyticsAdapter.js +1 -1
- package/dist/uid2IdSystem.js +1 -1
- package/dist/underdogmediaBidAdapter.js +1 -1
- package/dist/undertoneBidAdapter.js +1 -1
- package/dist/unrulyBidAdapter.js +1 -1
- package/dist/vidazooUtils.js +1 -1
- package/dist/videobyteBidAdapter.js +1 -1
- package/dist/visxBidAdapter.js +1 -1
- package/dist/vuukleBidAdapter.js +1 -1
- package/dist/widespaceBidAdapter.js +1 -1
- package/dist/winrBidAdapter.js +1 -1
- package/dist/yahooAdsBidAdapter.js +1 -1
- package/dist/yandexBidAdapter.js +1 -1
- package/dist/yieldmoBidAdapter.js +1 -1
- package/dist/yieldoneAnalyticsAdapter.js +1 -1
- package/integrationExamples/gpt/pubmaticRtdProvider_Example.html +161 -0
- package/libraries/bidderTimeoutUtils/bidderTimeoutUtils.js +119 -0
- package/libraries/objectGuard/objectGuard.js +36 -15
- package/libraries/pubmaticUtils/plugins/dynamicTimeout.js +209 -0
- package/libraries/pubmaticUtils/plugins/floorProvider.js +168 -0
- package/libraries/pubmaticUtils/plugins/pluginManager.js +106 -0
- package/libraries/pubmaticUtils/plugins/unifiedPricingRule.js +375 -0
- package/libraries/pubmaticUtils/pubmaticUtils.js +76 -0
- package/modules/fwsspBidAdapter.js +134 -69
- package/modules/fwsspBidAdapter.md +121 -26
- package/modules/optableRtdProvider.js +33 -12
- package/modules/pubmaticAnalyticsAdapter.js +5 -1
- package/modules/pubmaticRtdProvider.js +105 -565
- package/modules/rtdModule/index.js +23 -2
- package/modules/sevioBidAdapter.js +413 -0
- package/modules/sevioBidAdapter.md +29 -0
- package/modules/sparteoBidAdapter.js +122 -10
- package/modules/timeoutRtdProvider.js +2 -105
- package/package.json +1 -1
- package/test/spec/activities/objectGuard_spec.js +49 -16
- package/test/spec/libraries/bidderTimeoutUtils/bidderTimeoutUtils_spec.js +213 -0
- package/test/spec/libraries/pubmaticUtils/plugins/dynamicTimeout_spec.js +746 -0
- package/test/spec/libraries/pubmaticUtils/plugins/floorProvider_spec.js +184 -0
- package/test/spec/libraries/pubmaticUtils/plugins/pluginManager_spec.js +489 -0
- package/test/spec/libraries/pubmaticUtils/plugins/unifiedPricingRule_spec.js +359 -0
- package/test/spec/libraries/pubmaticUtils/pubmaticUtils_spec.js +236 -0
- package/test/spec/modules/fwsspBidAdapter_spec.js +513 -78
- package/test/spec/modules/optableRtdProvider_spec.js +55 -5
- package/test/spec/modules/pubmaticRtdProvider_spec.js +252 -1183
- package/test/spec/modules/realTimeDataModule_spec.js +58 -8
- package/test/spec/modules/sevioBidAdapter_spec.js +513 -0
- package/test/spec/modules/sparteoBidAdapter_spec.js +528 -43
- package/test/spec/modules/timeoutRtdProvider_spec.js +1 -201
|
@@ -1,1241 +1,310 @@
|
|
|
1
1
|
import { expect } from 'chai';
|
|
2
|
-
import * as priceFloors from '../../../modules/priceFloors';
|
|
3
|
-
import * as utils from '../../../src/utils.js';
|
|
4
|
-
import * as suaModule from '../../../src/fpd/sua.js';
|
|
5
|
-
import { config as conf } from '../../../src/config';
|
|
6
|
-
import * as hook from '../../../src/hook.js';
|
|
7
|
-
import * as prebidGlobal from '../../../src/prebidGlobal.js';
|
|
8
|
-
import {
|
|
9
|
-
registerSubModule, pubmaticSubmodule, getFloorsConfig, fetchData,
|
|
10
|
-
getCurrentTimeOfDay, getBrowserType, getOs, getDeviceType, getCountry, getUtm, getBidder, _country,
|
|
11
|
-
_profileConfigs, _floorsData, defaultValueTemplate, withTimeout, configMerged,
|
|
12
|
-
getProfileConfigs, setProfileConfigs, getTargetingData
|
|
13
|
-
} from '../../../modules/pubmaticRtdProvider.js';
|
|
14
2
|
import sinon from 'sinon';
|
|
3
|
+
import * as utils from '../../../src/utils.js';
|
|
4
|
+
import * as pubmaticRtdProvider from '../../../modules/pubmaticRtdProvider.js';
|
|
5
|
+
import { FloorProvider } from '../../../libraries/pubmaticUtils/plugins/floorProvider.js';
|
|
6
|
+
import { UnifiedPricingRule } from '../../../libraries/pubmaticUtils/plugins/unifiedPricingRule.js';
|
|
15
7
|
|
|
16
8
|
describe('Pubmatic RTD Provider', () => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
9
|
+
let sandbox;
|
|
10
|
+
let fetchStub;
|
|
11
|
+
let logErrorStub;
|
|
12
|
+
let originalPluginManager;
|
|
13
|
+
let originalConfigJsonManager;
|
|
14
|
+
let pluginManagerStub;
|
|
15
|
+
let configJsonManagerStub;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
sandbox = sinon.createSandbox();
|
|
19
|
+
fetchStub = sandbox.stub(window, 'fetch');
|
|
20
|
+
logErrorStub = sinon.stub(utils, 'logError');
|
|
21
|
+
|
|
22
|
+
// Store original implementations
|
|
23
|
+
originalPluginManager = Object.assign({}, pubmaticRtdProvider.pluginManager);
|
|
24
|
+
originalConfigJsonManager = Object.assign({}, pubmaticRtdProvider.configJsonManager);
|
|
25
|
+
|
|
26
|
+
// Create stubs
|
|
27
|
+
pluginManagerStub = {
|
|
28
|
+
initialize: sinon.stub().resolves(),
|
|
29
|
+
executeHook: sinon.stub().resolves(),
|
|
30
|
+
register: sinon.stub()
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
configJsonManagerStub = {
|
|
34
|
+
fetchConfig: sinon.stub().resolves(true),
|
|
35
|
+
getYMConfig: sinon.stub(),
|
|
36
|
+
getConfigByName: sinon.stub(),
|
|
37
|
+
country: 'IN'
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Replace exported objects with stubs
|
|
41
|
+
Object.keys(pluginManagerStub).forEach(key => {
|
|
42
|
+
pubmaticRtdProvider.pluginManager[key] = pluginManagerStub[key];
|
|
38
43
|
});
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
assert(submoduleStub.calledOnceWith('realTimeData', pubmaticSubmodule));
|
|
45
|
-
submoduleStub.restore();
|
|
45
|
+
Object.keys(configJsonManagerStub).forEach(key => {
|
|
46
|
+
if (key === 'country') {
|
|
47
|
+
Object.defineProperty(pubmaticRtdProvider.configJsonManager, key, {
|
|
48
|
+
get: () => configJsonManagerStub[key]
|
|
46
49
|
});
|
|
50
|
+
} else {
|
|
51
|
+
pubmaticRtdProvider.configJsonManager[key] = configJsonManagerStub[key];
|
|
52
|
+
}
|
|
47
53
|
});
|
|
48
54
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
expect(pubmaticSubmodule.name).to.equal('pubmatic');
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe('init', () => {
|
|
58
|
-
let logErrorStub;
|
|
59
|
-
let continueAuctionStub;
|
|
60
|
-
|
|
61
|
-
const getConfig = () => ({
|
|
62
|
-
params: {
|
|
63
|
-
publisherId: 'test-publisher-id',
|
|
64
|
-
profileId: 'test-profile-id'
|
|
65
|
-
},
|
|
66
|
-
});
|
|
55
|
+
// Reset _ymConfigPromise for each test
|
|
56
|
+
pubmaticRtdProvider.setYmConfigPromise(Promise.resolve());
|
|
57
|
+
});
|
|
67
58
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should return false if publisherId is missing', () => {
|
|
74
|
-
const config = {
|
|
75
|
-
params: {
|
|
76
|
-
profileId: 'test-profile-id'
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
expect(pubmaticSubmodule.init(config)).to.be.false;
|
|
80
|
-
});
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
sandbox.restore();
|
|
61
|
+
logErrorStub.restore();
|
|
81
62
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
publisherId: 'test-publisher-id'
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
expect(pubmaticSubmodule.init(config)).to.be.false;
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('should accept numeric publisherId and convert to string', () => {
|
|
92
|
-
const config = {
|
|
93
|
-
params: {
|
|
94
|
-
publisherId: 123,
|
|
95
|
-
profileId: 'test-profile-id'
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
expect(pubmaticSubmodule.init(config)).to.be.true;
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('should accept numeric profileId and convert to string', () => {
|
|
102
|
-
const config = {
|
|
103
|
-
params: {
|
|
104
|
-
publisherId: 'test-publisher-id',
|
|
105
|
-
profileId: 345
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
expect(pubmaticSubmodule.init(config)).to.be.true;
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('should initialize successfully with valid config', () => {
|
|
112
|
-
expect(pubmaticSubmodule.init(getConfig())).to.be.true;
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should handle empty config object', () => {
|
|
116
|
-
expect(pubmaticSubmodule.init({})).to.be.false;
|
|
117
|
-
expect(logErrorStub.calledWith(sinon.match(/Missing publisher Id/))).to.be.true;
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should return false if continueAuction is not a function', () => {
|
|
121
|
-
continueAuctionStub.value(undefined);
|
|
122
|
-
expect(pubmaticSubmodule.init(getConfig())).to.be.false;
|
|
123
|
-
expect(logErrorStub.calledWith(sinon.match(/continueAuction is not a function/))).to.be.true;
|
|
124
|
-
});
|
|
63
|
+
// Restore original implementations
|
|
64
|
+
Object.keys(originalPluginManager).forEach(key => {
|
|
65
|
+
pubmaticRtdProvider.pluginManager[key] = originalPluginManager[key];
|
|
125
66
|
});
|
|
126
67
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
clock = sandbox.useFakeTimers(new Date('2024-01-01T12:00:00')); // Set fixed time for testing
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
afterEach(() => {
|
|
135
|
-
clock.restore();
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
const testTimes = [
|
|
139
|
-
{ hour: 6, expected: 'morning' },
|
|
140
|
-
{ hour: 13, expected: 'afternoon' },
|
|
141
|
-
{ hour: 18, expected: 'evening' },
|
|
142
|
-
{ hour: 22, expected: 'night' },
|
|
143
|
-
{ hour: 4, expected: 'night' }
|
|
144
|
-
];
|
|
145
|
-
|
|
146
|
-
testTimes.forEach(({ hour, expected }) => {
|
|
147
|
-
it(`should return ${expected} at ${hour}:00`, () => {
|
|
148
|
-
clock.setSystemTime(new Date().setHours(hour));
|
|
149
|
-
const result = getCurrentTimeOfDay();
|
|
150
|
-
expect(result).to.equal(expected);
|
|
151
|
-
});
|
|
68
|
+
Object.keys(originalConfigJsonManager).forEach(key => {
|
|
69
|
+
if (key === 'country') {
|
|
70
|
+
Object.defineProperty(pubmaticRtdProvider.configJsonManager, 'country', {
|
|
71
|
+
get: () => originalConfigJsonManager[key]
|
|
152
72
|
});
|
|
73
|
+
} else {
|
|
74
|
+
pubmaticRtdProvider.configJsonManager[key] = originalConfigJsonManager[key];
|
|
75
|
+
}
|
|
153
76
|
});
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
userAgentStub.restore();
|
|
175
|
-
getLowEntropySUAStub.restore();
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('should detect Chrome', () => {
|
|
179
|
-
userAgentStub.value(USER_AGENTS.chrome);
|
|
180
|
-
expect(getBrowserType()).to.equal('9');
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('should detect Firefox', () => {
|
|
184
|
-
userAgentStub.value(USER_AGENTS.firefox);
|
|
185
|
-
expect(getBrowserType()).to.equal('12');
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('should detect Edge', () => {
|
|
189
|
-
userAgentStub.value(USER_AGENTS.edge);
|
|
190
|
-
expect(getBrowserType()).to.equal('2');
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('should detect Internet Explorer', () => {
|
|
194
|
-
userAgentStub.value(USER_AGENTS.ie);
|
|
195
|
-
expect(getBrowserType()).to.equal('4');
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it('should detect Opera', () => {
|
|
199
|
-
userAgentStub.value(USER_AGENTS.opera);
|
|
200
|
-
expect(getBrowserType()).to.equal('3');
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('should return 0 for unknown browser', () => {
|
|
204
|
-
userAgentStub.value(USER_AGENTS.unknown);
|
|
205
|
-
expect(getBrowserType()).to.equal('0');
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('should return -1 when userAgent is null', () => {
|
|
209
|
-
userAgentStub.value(null);
|
|
210
|
-
expect(getBrowserType()).to.equal('-1');
|
|
211
|
-
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('init', () => {
|
|
80
|
+
const validConfig = {
|
|
81
|
+
params: {
|
|
82
|
+
publisherId: 'test-publisher-id',
|
|
83
|
+
profileId: 'test-profile-id'
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
it('should return false if publisherId is missing', () => {
|
|
88
|
+
const config = {
|
|
89
|
+
params: {
|
|
90
|
+
profileId: 'test-profile-id'
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const result = pubmaticRtdProvider.pubmaticSubmodule.init(config);
|
|
94
|
+
expect(result).to.be.false;
|
|
95
|
+
expect(logErrorStub.calledOnce).to.be.true;
|
|
96
|
+
expect(logErrorStub.firstCall.args[0]).to.equal(`${pubmaticRtdProvider.CONSTANTS.LOG_PRE_FIX} Missing publisher Id.`);
|
|
212
97
|
});
|
|
213
98
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
it('should set time of day correctly', () => {
|
|
228
|
-
expect(getCurrentTimeOfDay()).to.be.a('string');
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
it('should set country correctly', () => {
|
|
232
|
-
expect(getCountry()).to.satisfy(value => typeof value === 'string' || value === undefined);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it('should set UTM correctly', () => {
|
|
236
|
-
expect(getUtm()).to.be.a('string');
|
|
237
|
-
expect(getUtm()).to.be.oneOf(['0', '1']);
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it('should extract bidder correctly', () => {
|
|
241
|
-
expect(getBidder({ bidder: 'pubmatic' })).to.equal('pubmatic');
|
|
242
|
-
expect(getBidder({})).to.be.undefined;
|
|
243
|
-
expect(getBidder(null)).to.be.undefined;
|
|
244
|
-
expect(getBidder(undefined)).to.be.undefined;
|
|
245
|
-
});
|
|
99
|
+
it('should return false if publisherId is not a string', () => {
|
|
100
|
+
const config = {
|
|
101
|
+
params: {
|
|
102
|
+
publisherId: 123,
|
|
103
|
+
profileId: 'test-profile-id'
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const result = pubmaticRtdProvider.pubmaticSubmodule.init(config);
|
|
107
|
+
expect(result).to.be.false;
|
|
108
|
+
expect(logErrorStub.calledOnce).to.be.true;
|
|
109
|
+
expect(logErrorStub.firstCall.args[0]).to.equal(`${pubmaticRtdProvider.CONSTANTS.LOG_PRE_FIX} Publisher Id should be a string.`);
|
|
246
110
|
});
|
|
247
111
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
"floorProvider": "PM",
|
|
259
|
-
"floorsSchemaVersion": 2,
|
|
260
|
-
"modelGroups": [
|
|
261
|
-
{
|
|
262
|
-
"modelVersion": "M_1",
|
|
263
|
-
"modelWeight": 100,
|
|
264
|
-
"schema": {
|
|
265
|
-
"fields": [
|
|
266
|
-
"domain"
|
|
267
|
-
]
|
|
268
|
-
},
|
|
269
|
-
"values": {
|
|
270
|
-
"*": 2.00
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
],
|
|
274
|
-
"skipRate": 0
|
|
275
|
-
};
|
|
276
|
-
profileConfigs = {
|
|
277
|
-
'plugins': {
|
|
278
|
-
'dynamicFloors': {
|
|
279
|
-
'enabled': true,
|
|
280
|
-
'config': {
|
|
281
|
-
'enforcement': {
|
|
282
|
-
'floorDeals': false,
|
|
283
|
-
'enforceJS': false
|
|
284
|
-
},
|
|
285
|
-
'floorMin': 0.1111,
|
|
286
|
-
'skipRate': 11,
|
|
287
|
-
'defaultValues': {
|
|
288
|
-
"*|*": 0.2
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
afterEach(() => {
|
|
297
|
-
sandbox.restore();
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
it('should return correct config structure', () => {
|
|
301
|
-
const result = getFloorsConfig(floorsData, profileConfigs);
|
|
302
|
-
|
|
303
|
-
expect(result.floors).to.be.an('object');
|
|
304
|
-
expect(result.floors).to.be.an('object');
|
|
305
|
-
expect(result.floors).to.have.property('enforcement');
|
|
306
|
-
expect(result.floors.enforcement).to.have.property('floorDeals', false);
|
|
307
|
-
expect(result.floors.enforcement).to.have.property('enforceJS', false);
|
|
308
|
-
expect(result.floors).to.have.property('floorMin', 0.1111);
|
|
309
|
-
|
|
310
|
-
// Verify the additionalSchemaFields structure
|
|
311
|
-
expect(result.floors.additionalSchemaFields).to.have.all.keys([
|
|
312
|
-
'deviceType',
|
|
313
|
-
'timeOfDay',
|
|
314
|
-
'browser',
|
|
315
|
-
'os',
|
|
316
|
-
'country',
|
|
317
|
-
'utm',
|
|
318
|
-
'bidder'
|
|
319
|
-
]);
|
|
320
|
-
|
|
321
|
-
Object.values(result.floors.additionalSchemaFields).forEach(field => {
|
|
322
|
-
expect(field).to.be.a('function');
|
|
323
|
-
});
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
it('should return undefined when plugin is disabled', () => {
|
|
327
|
-
profileConfigs.plugins.dynamicFloors.enabled = false;
|
|
328
|
-
const result = getFloorsConfig(floorsData, profileConfigs);
|
|
329
|
-
|
|
330
|
-
expect(result).to.equal(undefined);
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it('should initialise default values to empty object when not available', () => {
|
|
334
|
-
profileConfigs.plugins.dynamicFloors.config.defaultValues = undefined;
|
|
335
|
-
floorsData = undefined;
|
|
336
|
-
const result = getFloorsConfig(floorsData, profileConfigs);
|
|
337
|
-
|
|
338
|
-
expect(result.floors.data).to.have.property('currency', 'USD');
|
|
339
|
-
expect(result.floors.data).to.have.property('skipRate', 11);
|
|
340
|
-
expect(result.floors.data.schema).to.deep.equal(defaultValueTemplate.schema);
|
|
341
|
-
expect(result.floors.data.value).to.deep.equal(defaultValueTemplate.value);
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
it('should replace skipRate from config to data when avaialble', () => {
|
|
345
|
-
const result = getFloorsConfig(floorsData, profileConfigs);
|
|
346
|
-
|
|
347
|
-
expect(result.floors.data).to.have.property('skipRate', 11);
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
it('should not replace skipRate from config to data when not avaialble', () => {
|
|
351
|
-
delete profileConfigs.plugins.dynamicFloors.config.skipRate;
|
|
352
|
-
const result = getFloorsConfig(floorsData, profileConfigs);
|
|
353
|
-
|
|
354
|
-
expect(result.floors.data).to.have.property('skipRate', 0);
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
it('should maintain correct function references', () => {
|
|
358
|
-
const result = getFloorsConfig(floorsData, profileConfigs);
|
|
359
|
-
|
|
360
|
-
expect(result.floors.additionalSchemaFields.deviceType).to.equal(getDeviceType);
|
|
361
|
-
expect(result.floors.additionalSchemaFields.timeOfDay).to.equal(getCurrentTimeOfDay);
|
|
362
|
-
expect(result.floors.additionalSchemaFields.browser).to.equal(getBrowserType);
|
|
363
|
-
expect(result.floors.additionalSchemaFields.os).to.equal(getOs);
|
|
364
|
-
expect(result.floors.additionalSchemaFields.country).to.equal(getCountry);
|
|
365
|
-
expect(result.floors.additionalSchemaFields.utm).to.equal(getUtm);
|
|
366
|
-
expect(result.floors.additionalSchemaFields.bidder).to.equal(getBidder);
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
it('should log error when profileConfigs is not an object', () => {
|
|
370
|
-
profileConfigs = 'invalid';
|
|
371
|
-
const result = getFloorsConfig(floorsData, profileConfigs);
|
|
372
|
-
expect(result).to.be.undefined;
|
|
373
|
-
expect(logErrorStub.calledWith(sinon.match(/profileConfigs is not an object or is empty/))).to.be.true;
|
|
374
|
-
});
|
|
112
|
+
it('should return false if profileId is missing', () => {
|
|
113
|
+
const config = {
|
|
114
|
+
params: {
|
|
115
|
+
publisherId: 'test-publisher-id'
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
const result = pubmaticRtdProvider.pubmaticSubmodule.init(config);
|
|
119
|
+
expect(result).to.be.false;
|
|
120
|
+
expect(logErrorStub.calledOnce).to.be.true;
|
|
121
|
+
expect(logErrorStub.firstCall.args[0]).to.equal(`${pubmaticRtdProvider.CONSTANTS.LOG_PRE_FIX} Missing profile Id.`);
|
|
375
122
|
});
|
|
376
123
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
afterEach(() => {
|
|
389
|
-
sandbox.restore();
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
it('should successfully fetch profile configs', async () => {
|
|
393
|
-
const mockApiResponse = {
|
|
394
|
-
"profileName": "profie name",
|
|
395
|
-
"desc": "description",
|
|
396
|
-
"plugins": {
|
|
397
|
-
"dynamicFloors": {
|
|
398
|
-
"enabled": false
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
fetchStub.resolves(new Response(JSON.stringify(mockApiResponse), { status: 200 }));
|
|
404
|
-
|
|
405
|
-
const result = await fetchData('1234', '123', 'CONFIGS');
|
|
406
|
-
expect(result).to.deep.equal(mockApiResponse);
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
it('should log error when JSON parsing fails', async () => {
|
|
410
|
-
fetchStub.resolves(new Response('Invalid JSON', { status: 200 }));
|
|
411
|
-
|
|
412
|
-
await fetchData('1234', '123', 'CONFIGS');
|
|
413
|
-
expect(logErrorStub.calledWith(sinon.match(/Error while fetching\s*CONFIGS/))).to.be.true;
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
it('should log error when response is not ok', async () => {
|
|
417
|
-
fetchStub.resolves(new Response(null, { status: 500 }));
|
|
418
|
-
|
|
419
|
-
await fetchData('1234', '123', 'CONFIGS');
|
|
420
|
-
expect(logErrorStub.calledWith(sinon.match(/Error while fetching\s*CONFIGS/))).to.be.true;
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
it('should log error on network failure', async () => {
|
|
424
|
-
fetchStub.rejects(new Error('Network Error'));
|
|
425
|
-
|
|
426
|
-
await fetchData('1234', '123', 'CONFIGS');
|
|
427
|
-
expect(logErrorStub.called).to.be.true;
|
|
428
|
-
expect(logErrorStub.calledWith(sinon.match(/Error while fetching\s*CONFIGS/))).to.be.true;
|
|
429
|
-
});
|
|
124
|
+
it('should return false if profileId is not a string', () => {
|
|
125
|
+
const config = {
|
|
126
|
+
params: {
|
|
127
|
+
publisherId: 'test-publisher-id',
|
|
128
|
+
profileId: 345
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
const result = pubmaticRtdProvider.pubmaticSubmodule.init(config);
|
|
132
|
+
expect(result).to.be.false;
|
|
133
|
+
expect(logErrorStub.calledOnce).to.be.true;
|
|
134
|
+
expect(logErrorStub.firstCall.args[0]).to.equal(`${pubmaticRtdProvider.CONSTANTS.LOG_PRE_FIX} Profile Id should be a string.`);
|
|
430
135
|
});
|
|
431
136
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
let confStub;
|
|
436
|
-
|
|
437
|
-
beforeEach(() => {
|
|
438
|
-
logErrorStub = sandbox.stub(utils, 'logError');
|
|
439
|
-
fetchStub = sandbox.stub(window, 'fetch');
|
|
440
|
-
confStub = sandbox.stub(conf, 'setConfig');
|
|
441
|
-
global._country = undefined;
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
afterEach(() => {
|
|
445
|
-
sandbox.restore();
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
it('should successfully fetch and parse floor rules', async () => {
|
|
449
|
-
const mockApiResponse = {
|
|
450
|
-
data: {
|
|
451
|
-
currency: 'USD',
|
|
452
|
-
modelGroups: [],
|
|
453
|
-
values: {}
|
|
454
|
-
}
|
|
455
|
-
};
|
|
137
|
+
it('should initialize successfully with valid config', async () => {
|
|
138
|
+
configJsonManagerStub.fetchConfig.resolves(true);
|
|
139
|
+
pluginManagerStub.initialize.resolves();
|
|
456
140
|
|
|
457
|
-
|
|
141
|
+
const result = pubmaticRtdProvider.pubmaticSubmodule.init(validConfig);
|
|
142
|
+
expect(result).to.be.true;
|
|
143
|
+
expect(configJsonManagerStub.fetchConfig.calledOnce).to.be.true;
|
|
144
|
+
expect(configJsonManagerStub.fetchConfig.firstCall.args[0]).to.equal('test-publisher-id');
|
|
145
|
+
expect(configJsonManagerStub.fetchConfig.firstCall.args[1]).to.equal('test-profile-id');
|
|
458
146
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
it('should correctly extract the first unique country code from response headers', async () => {
|
|
465
|
-
fetchStub.resolves(new Response(JSON.stringify({}), {
|
|
466
|
-
status: 200,
|
|
467
|
-
headers: { 'country_code': 'US,IN,US' }
|
|
468
|
-
}));
|
|
469
|
-
|
|
470
|
-
await fetchData('1234', '123', 'FLOORS');
|
|
471
|
-
expect(_country).to.equal('US');
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
it('should set _country to undefined if country_code header is missing', async () => {
|
|
475
|
-
fetchStub.resolves(new Response(JSON.stringify({}), {
|
|
476
|
-
status: 200
|
|
477
|
-
}));
|
|
478
|
-
|
|
479
|
-
await fetchData('1234', '123', 'FLOORS');
|
|
480
|
-
expect(_country).to.be.undefined;
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
it('should log error when JSON parsing fails', async () => {
|
|
484
|
-
fetchStub.resolves(new Response('Invalid JSON', { status: 200 }));
|
|
147
|
+
// Wait for promise to resolve
|
|
148
|
+
await pubmaticRtdProvider.getYmConfigPromise();
|
|
149
|
+
expect(pluginManagerStub.initialize.calledOnce).to.be.true;
|
|
150
|
+
expect(pluginManagerStub.initialize.firstCall.args[0]).to.equal(pubmaticRtdProvider.configJsonManager);
|
|
151
|
+
});
|
|
485
152
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
});
|
|
153
|
+
it('should handle config fetch error gracefully', async () => {
|
|
154
|
+
configJsonManagerStub.fetchConfig.resolves(false);
|
|
489
155
|
|
|
490
|
-
|
|
491
|
-
|
|
156
|
+
const result = pubmaticRtdProvider.pubmaticSubmodule.init(validConfig);
|
|
157
|
+
expect(result).to.be.true;
|
|
492
158
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
159
|
+
try {
|
|
160
|
+
await pubmaticRtdProvider.getYmConfigPromise();
|
|
161
|
+
} catch (e) {
|
|
162
|
+
expect(e.message).to.equal('Failed to fetch configuration');
|
|
163
|
+
}
|
|
496
164
|
|
|
497
|
-
|
|
498
|
-
fetchStub.rejects(new Error('Network Error'));
|
|
499
|
-
|
|
500
|
-
await fetchData('1234', '123', 'FLOORS');
|
|
501
|
-
expect(logErrorStub.called).to.be.true;
|
|
502
|
-
expect(logErrorStub.calledWith(sinon.match(/Error while fetching\s*FLOORS/))).to.be.true;
|
|
503
|
-
});
|
|
165
|
+
expect(pluginManagerStub.initialize.called).to.be.false;
|
|
504
166
|
});
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
521
|
-
};
|
|
522
|
-
|
|
523
|
-
const ortb2 = {
|
|
524
|
-
user: {
|
|
525
|
-
ext: {
|
|
526
|
-
ctr: 'US',
|
|
527
|
-
}
|
|
528
|
-
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('getBidRequestData', () => {
|
|
170
|
+
const reqBidsConfigObj = {
|
|
171
|
+
ortb2Fragments: {
|
|
172
|
+
bidder: {}
|
|
173
|
+
},
|
|
174
|
+
adUnits: [
|
|
175
|
+
{
|
|
176
|
+
code: 'div-1',
|
|
177
|
+
bids: [{ bidder: 'pubmatic', params: {} }]
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
code: 'div-2',
|
|
181
|
+
bids: [{ bidder: 'pubmatic', params: {} }]
|
|
529
182
|
}
|
|
183
|
+
]
|
|
184
|
+
};
|
|
185
|
+
let callback;
|
|
530
186
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
haveExited: false,
|
|
536
|
-
timer: null
|
|
537
|
-
};
|
|
538
|
-
|
|
539
|
-
beforeEach(() => {
|
|
540
|
-
callback = sinon.spy();
|
|
541
|
-
continueAuctionStub = sandbox.stub(priceFloors, 'continueAuction');
|
|
542
|
-
logErrorStub = sandbox.stub(utils, 'logError');
|
|
543
|
-
|
|
544
|
-
global.configMergedPromise = Promise.resolve();
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
afterEach(() => {
|
|
548
|
-
sandbox.restore(); // Restore all stubs/spies
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
it('should call continueAuction with correct hookConfig', async function () {
|
|
552
|
-
configMerged();
|
|
553
|
-
await pubmaticSubmodule.getBidRequestData(reqBidsConfigObj, callback);
|
|
554
|
-
|
|
555
|
-
expect(continueAuctionStub.called).to.be.true;
|
|
556
|
-
expect(continueAuctionStub.firstCall.args[0]).to.have.property('reqBidsConfigObj', reqBidsConfigObj);
|
|
557
|
-
expect(continueAuctionStub.firstCall.args[0]).to.have.property('haveExited', false);
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
// it('should merge country data into ortb2Fragments.bidder', async function () {
|
|
561
|
-
// configMerged();
|
|
562
|
-
// global._country = 'US';
|
|
563
|
-
// pubmaticSubmodule.getBidRequestData(reqBidsConfigObj, callback);
|
|
187
|
+
beforeEach(() => {
|
|
188
|
+
callback = sinon.stub();
|
|
189
|
+
pubmaticRtdProvider.setYmConfigPromise(Promise.resolve());
|
|
190
|
+
});
|
|
564
191
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
// });
|
|
192
|
+
it('should call pluginManager executeHook with correct parameters', (done) => {
|
|
193
|
+
pluginManagerStub.executeHook.resolves();
|
|
568
194
|
|
|
569
|
-
|
|
570
|
-
configMerged();
|
|
571
|
-
await pubmaticSubmodule.getBidRequestData(reqBidsConfigObj, callback);
|
|
195
|
+
pubmaticRtdProvider.pubmaticSubmodule.getBidRequestData(reqBidsConfigObj, callback);
|
|
572
196
|
|
|
573
|
-
|
|
574
|
-
|
|
197
|
+
setTimeout(() => {
|
|
198
|
+
expect(pluginManagerStub.executeHook.calledOnce).to.be.true;
|
|
199
|
+
expect(pluginManagerStub.executeHook.firstCall.args[0]).to.equal('processBidRequest');
|
|
200
|
+
expect(pluginManagerStub.executeHook.firstCall.args[1]).to.deep.equal(reqBidsConfigObj);
|
|
201
|
+
expect(callback.calledOnce).to.be.true;
|
|
202
|
+
done();
|
|
203
|
+
}, 0);
|
|
575
204
|
});
|
|
576
205
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
const promise = new Promise((resolve) => setTimeout(() => resolve('success'), 50));
|
|
580
|
-
const result = await withTimeout(promise, 100);
|
|
581
|
-
expect(result).to.equal('success');
|
|
582
|
-
});
|
|
206
|
+
it('should add country information to ORTB2', (done) => {
|
|
207
|
+
pluginManagerStub.executeHook.resolves();
|
|
583
208
|
|
|
584
|
-
|
|
585
|
-
const promise = new Promise((resolve) => setTimeout(() => resolve('success'), 200));
|
|
586
|
-
const result = await withTimeout(promise, 100);
|
|
587
|
-
expect(result).to.be.undefined;
|
|
588
|
-
});
|
|
209
|
+
pubmaticRtdProvider.pubmaticSubmodule.getBidRequestData(reqBidsConfigObj, callback);
|
|
589
210
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
expect(error.message).to.equal('Failure');
|
|
211
|
+
setTimeout(() => {
|
|
212
|
+
expect(reqBidsConfigObj.ortb2Fragments.bidder[pubmaticRtdProvider.CONSTANTS.SUBMODULE_NAME]).to.deep.equal({
|
|
213
|
+
user: {
|
|
214
|
+
ext: {
|
|
215
|
+
ctr: 'IN'
|
|
596
216
|
}
|
|
217
|
+
}
|
|
597
218
|
});
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
219
|
+
done();
|
|
220
|
+
}, 0);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe('getTargetingData', () => {
|
|
225
|
+
const adUnitCodes = ['div-1', 'div-2'];
|
|
226
|
+
const config = {
|
|
227
|
+
params: {
|
|
228
|
+
publisherId: 'test-publisher-id',
|
|
229
|
+
profileId: 'test-profile-id'
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
const userConsent = {};
|
|
233
|
+
const auction = {};
|
|
234
|
+
const unifiedPricingRule = {
|
|
235
|
+
'div-1': { key1: 'value1' },
|
|
236
|
+
'div-2': { key2: 'value2' }
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
it('should call pluginManager executeHook with correct parameters', () => {
|
|
240
|
+
pluginManagerStub.executeHook.returns(unifiedPricingRule);
|
|
241
|
+
|
|
242
|
+
const result = pubmaticRtdProvider.getTargetingData(adUnitCodes, config, userConsent, auction);
|
|
243
|
+
|
|
244
|
+
expect(pluginManagerStub.executeHook.calledOnce).to.be.true;
|
|
245
|
+
expect(pluginManagerStub.executeHook.firstCall.args[0]).to.equal('getTargeting');
|
|
246
|
+
expect(pluginManagerStub.executeHook.firstCall.args[1]).to.equal(adUnitCodes);
|
|
247
|
+
expect(pluginManagerStub.executeHook.firstCall.args[2]).to.equal(config);
|
|
248
|
+
expect(pluginManagerStub.executeHook.firstCall.args[3]).to.equal(userConsent);
|
|
249
|
+
expect(pluginManagerStub.executeHook.firstCall.args[4]).to.equal(auction);
|
|
250
|
+
expect(result).to.equal(unifiedPricingRule);
|
|
620
251
|
});
|
|
621
252
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
let logInfoStub;
|
|
625
|
-
|
|
626
|
-
beforeEach(() => {
|
|
627
|
-
sandbox = sinon.createSandbox();
|
|
628
|
-
logInfoStub = sandbox.stub(utils, 'logInfo');
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
afterEach(() => {
|
|
632
|
-
sandbox.restore();
|
|
633
|
-
});
|
|
634
|
-
|
|
635
|
-
it('should return empty object when profileConfigs is undefined', function () {
|
|
636
|
-
// Store the original value to restore it later
|
|
637
|
-
const originalProfileConfigs = getProfileConfigs();
|
|
638
|
-
// Set profileConfigs to undefined
|
|
639
|
-
setProfileConfigs(undefined);
|
|
640
|
-
|
|
641
|
-
const adUnitCodes = ['test-ad-unit'];
|
|
642
|
-
const config = {};
|
|
643
|
-
const userConsent = {};
|
|
644
|
-
const auction = {};
|
|
645
|
-
|
|
646
|
-
const result = getTargetingData(adUnitCodes, config, userConsent, auction);
|
|
647
|
-
|
|
648
|
-
// Restore the original value
|
|
649
|
-
setProfileConfigs(originalProfileConfigs);
|
|
650
|
-
|
|
651
|
-
expect(result).to.deep.equal({});
|
|
652
|
-
expect(logInfoStub.calledWith(sinon.match(/pmTargetingKeys is disabled or profileConfigs is undefined/))).to.be.true;
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
it('should return empty object when pmTargetingKeys.enabled is false', function () {
|
|
656
|
-
// Create profileConfigs with pmTargetingKeys.enabled set to false
|
|
657
|
-
const profileConfigsMock = {
|
|
658
|
-
plugins: {
|
|
659
|
-
dynamicFloors: {
|
|
660
|
-
pmTargetingKeys: {
|
|
661
|
-
enabled: false
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
};
|
|
666
|
-
|
|
667
|
-
// Store the original value to restore it later
|
|
668
|
-
const originalProfileConfigs = getProfileConfigs();
|
|
669
|
-
// Set profileConfigs to our mock
|
|
670
|
-
setProfileConfigs(profileConfigsMock);
|
|
671
|
-
|
|
672
|
-
const adUnitCodes = ['test-ad-unit'];
|
|
673
|
-
const config = {};
|
|
674
|
-
const userConsent = {};
|
|
675
|
-
const auction = {};
|
|
676
|
-
|
|
677
|
-
const result = getTargetingData(adUnitCodes, config, userConsent, auction);
|
|
678
|
-
|
|
679
|
-
// Restore the original value
|
|
680
|
-
setProfileConfigs(originalProfileConfigs);
|
|
681
|
-
|
|
682
|
-
expect(result).to.deep.equal({});
|
|
683
|
-
expect(logInfoStub.calledWith(sinon.match(/pmTargetingKeys is disabled or profileConfigs is undefined/))).to.be.true;
|
|
684
|
-
});
|
|
685
|
-
|
|
686
|
-
it('should set pm_ym_flrs to 0 when no RTD floor is applied to any bid', function () {
|
|
687
|
-
// Create profileConfigs with pmTargetingKeys.enabled set to true
|
|
688
|
-
const profileConfigsMock = {
|
|
689
|
-
plugins: {
|
|
690
|
-
dynamicFloors: {
|
|
691
|
-
pmTargetingKeys: {
|
|
692
|
-
enabled: true
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
};
|
|
697
|
-
|
|
698
|
-
// Store the original value to restore it later
|
|
699
|
-
const originalProfileConfigs = getProfileConfigs();
|
|
700
|
-
// Set profileConfigs to our mock
|
|
701
|
-
setProfileConfigs(profileConfigsMock);
|
|
702
|
-
|
|
703
|
-
// Create multiple ad unit codes to test
|
|
704
|
-
const adUnitCodes = ['ad-unit-1', 'ad-unit-2'];
|
|
705
|
-
const config = {};
|
|
706
|
-
const userConsent = {};
|
|
707
|
-
|
|
708
|
-
// Create a mock auction object with bids that don't have RTD floors applied
|
|
709
|
-
// This tests several scenarios where RTD floor is not applied:
|
|
710
|
-
// 1. No floorData
|
|
711
|
-
// 2. floorData but floorProvider is not 'PM'
|
|
712
|
-
// 3. floorData with floorProvider 'PM' but skipped is true
|
|
713
|
-
const auction = {
|
|
714
|
-
adUnits: [
|
|
715
|
-
{
|
|
716
|
-
code: 'ad-unit-1',
|
|
717
|
-
bids: [
|
|
718
|
-
{ bidder: 'bidderA' }, // No floorData
|
|
719
|
-
{ bidder: 'bidderB', floorData: { floorProvider: 'OTHER' } } // Not PM provider
|
|
720
|
-
]
|
|
721
|
-
},
|
|
722
|
-
{
|
|
723
|
-
code: 'ad-unit-2',
|
|
724
|
-
bids: [
|
|
725
|
-
{ bidder: 'bidderC', floorData: { floorProvider: 'PM', skipped: true } } // PM but skipped
|
|
726
|
-
]
|
|
727
|
-
}
|
|
728
|
-
],
|
|
729
|
-
bidsReceived: [
|
|
730
|
-
{ adUnitCode: 'ad-unit-1', bidder: 'bidderA' },
|
|
731
|
-
{ adUnitCode: 'ad-unit-1', bidder: 'bidderB', floorData: { floorProvider: 'OTHER' } },
|
|
732
|
-
{ adUnitCode: 'ad-unit-2', bidder: 'bidderC', floorData: { floorProvider: 'PM', skipped: true } }
|
|
733
|
-
]
|
|
734
|
-
};
|
|
735
|
-
|
|
736
|
-
const result = getTargetingData(adUnitCodes, config, userConsent, auction);
|
|
737
|
-
|
|
738
|
-
// Restore the original value
|
|
739
|
-
setProfileConfigs(originalProfileConfigs);
|
|
740
|
-
|
|
741
|
-
// Verify that for each ad unit code, only pm_ym_flrs is set to 0
|
|
742
|
-
expect(result['ad-unit-1']).to.have.property('pm_ym_flrs', 0);
|
|
743
|
-
expect(result['ad-unit-2']).to.have.property('pm_ym_flrs', 0);
|
|
744
|
-
});
|
|
745
|
-
|
|
746
|
-
it('should set pm_ym_flrs to 1 when RTD floor is applied to a bid', function () {
|
|
747
|
-
// Create profileConfigs with pmTargetingKeys.enabled set to true
|
|
748
|
-
const profileConfigsMock = {
|
|
749
|
-
plugins: {
|
|
750
|
-
dynamicFloors: {
|
|
751
|
-
pmTargetingKeys: {
|
|
752
|
-
enabled: true
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
};
|
|
757
|
-
|
|
758
|
-
// Store the original value to restore it later
|
|
759
|
-
const originalProfileConfigs = getProfileConfigs();
|
|
760
|
-
// Set profileConfigs to our mock
|
|
761
|
-
setProfileConfigs(profileConfigsMock);
|
|
762
|
-
|
|
763
|
-
// Create multiple ad unit codes to test
|
|
764
|
-
const adUnitCodes = ['ad-unit-1', 'ad-unit-2'];
|
|
765
|
-
const config = {};
|
|
766
|
-
const userConsent = {};
|
|
767
|
-
|
|
768
|
-
// Create a mock auction object with bids that have RTD floors applied
|
|
769
|
-
const auction = {
|
|
770
|
-
adUnits: [
|
|
771
|
-
{
|
|
772
|
-
code: 'ad-unit-1',
|
|
773
|
-
bids: [
|
|
774
|
-
{ bidder: 'bidderA', floorData: { floorProvider: 'PM', skipped: false } },
|
|
775
|
-
{ bidder: 'bidderB', floorData: { floorProvider: 'PM', skipped: false } }
|
|
776
|
-
]
|
|
777
|
-
},
|
|
778
|
-
{
|
|
779
|
-
code: 'ad-unit-2',
|
|
780
|
-
bids: [
|
|
781
|
-
{ bidder: 'bidderC', floorData: { floorProvider: 'PM', skipped: false } },
|
|
782
|
-
{ bidder: 'bidderD', floorData: { floorProvider: 'PM', skipped: false } }
|
|
783
|
-
]
|
|
784
|
-
}
|
|
785
|
-
],
|
|
786
|
-
bidsReceived: [
|
|
787
|
-
{ adUnitCode: 'ad-unit-1', bidder: 'bidderA', floorData: { floorProvider: 'PM', skipped: false } },
|
|
788
|
-
{ adUnitCode: 'ad-unit-1', bidder: 'bidderB', floorData: { floorProvider: 'PM', skipped: false } },
|
|
789
|
-
{ adUnitCode: 'ad-unit-2', bidder: 'bidderC', floorData: { floorProvider: 'PM', skipped: false } },
|
|
790
|
-
{ adUnitCode: 'ad-unit-2', bidder: 'bidderD', floorData: { floorProvider: 'PM', skipped: false } }
|
|
791
|
-
]
|
|
792
|
-
};
|
|
793
|
-
|
|
794
|
-
const result = getTargetingData(adUnitCodes, config, userConsent, auction);
|
|
795
|
-
|
|
796
|
-
// Restore the original value
|
|
797
|
-
setProfileConfigs(originalProfileConfigs);
|
|
798
|
-
|
|
799
|
-
// Verify that for each ad unit code, pm_ym_flrs is set to 1
|
|
800
|
-
expect(result['ad-unit-1']).to.have.property('pm_ym_flrs', 1);
|
|
801
|
-
expect(result['ad-unit-2']).to.have.property('pm_ym_flrs', 1);
|
|
802
|
-
});
|
|
803
|
-
|
|
804
|
-
it('should set different targeting keys for winning bids (status 1) and floored bids (status 2)', function () {
|
|
805
|
-
// Create profileConfigs with pmTargetingKeys.enabled set to true
|
|
806
|
-
const profileConfigsMock = {
|
|
807
|
-
plugins: {
|
|
808
|
-
dynamicFloors: {
|
|
809
|
-
pmTargetingKeys: {
|
|
810
|
-
enabled: true
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
};
|
|
815
|
-
|
|
816
|
-
const mockPbjs = {
|
|
817
|
-
getHighestCpmBids: (adUnitCode) => {
|
|
818
|
-
// For div2, return a winning bid
|
|
819
|
-
if (adUnitCode === 'div2') {
|
|
820
|
-
return [{
|
|
821
|
-
adUnitCode: 'div2',
|
|
822
|
-
cpm: 5.5,
|
|
823
|
-
floorData: {
|
|
824
|
-
floorValue: 5.0,
|
|
825
|
-
floorProvider: 'PM'
|
|
826
|
-
}
|
|
827
|
-
}];
|
|
828
|
-
}
|
|
829
|
-
// For all other ad units, return empty array (no winning bids)
|
|
830
|
-
return [];
|
|
831
|
-
}
|
|
832
|
-
};
|
|
833
|
-
|
|
834
|
-
// Stub getGlobal to return our mock object
|
|
835
|
-
const getGlobalStub = sandbox.stub(prebidGlobal, 'getGlobal').returns(mockPbjs);
|
|
836
|
-
|
|
837
|
-
// Store the original value to restore it later
|
|
838
|
-
const originalProfileConfigs = getProfileConfigs();
|
|
839
|
-
// Set profileConfigs to our mock
|
|
840
|
-
setProfileConfigs(profileConfigsMock);
|
|
841
|
-
|
|
842
|
-
// Create ad unit codes to test
|
|
843
|
-
const adUnitCodes = ['div2', 'div3'];
|
|
844
|
-
const config = {};
|
|
845
|
-
const userConsent = {};
|
|
846
|
-
|
|
847
|
-
// Create a mock auction object with bids that have RTD floors applied
|
|
848
|
-
const auction = {
|
|
849
|
-
adUnits: [
|
|
850
|
-
{ code: "div2", bids: [{ floorData: { floorProvider: "PM", skipped: false } }] },
|
|
851
|
-
{ code: "div3", bids: [{ floorData: { floorProvider: "PM", skipped: false } }] }
|
|
852
|
-
],
|
|
853
|
-
adUnitCodes: ["div2", "div3"],
|
|
854
|
-
bidsReceived: [[
|
|
855
|
-
{
|
|
856
|
-
"bidderCode": "appnexus",
|
|
857
|
-
"auctionId": "a262767c-5499-4e98-b694-af36dbcb50f6",
|
|
858
|
-
"mediaType": "banner",
|
|
859
|
-
"source": "client",
|
|
860
|
-
"cpm": 5.5,
|
|
861
|
-
"adUnitCode": "div2",
|
|
862
|
-
"adapterCode": "appnexus",
|
|
863
|
-
"originalCpm": 5.5,
|
|
864
|
-
"floorData": {
|
|
865
|
-
"floorValue": 5,
|
|
866
|
-
"floorRule": "banner|*|*|div2|*|*|*|*|*",
|
|
867
|
-
"floorRuleValue": 5,
|
|
868
|
-
"floorCurrency": "USD"
|
|
869
|
-
},
|
|
870
|
-
"bidder": "appnexus",
|
|
871
|
-
}
|
|
872
|
-
]],
|
|
873
|
-
bidsRejected: [
|
|
874
|
-
{adUnitCode: "div3", bidder: "pubmatic", cpm: 20, floorData: { floorValue: 40 }, rejectionReason: "Bid does not meet price floor" }]
|
|
875
|
-
};
|
|
876
|
-
|
|
877
|
-
const result = getTargetingData(adUnitCodes, config, userConsent, auction);
|
|
878
|
-
|
|
879
|
-
// Restore the original value
|
|
880
|
-
setProfileConfigs(originalProfileConfigs);
|
|
881
|
-
// Check the test results
|
|
882
|
-
expect(result['div2']).to.have.property('pm_ym_flrs', 1);
|
|
883
|
-
expect(result['div2']).to.have.property('pm_ym_flrv', '5.50');
|
|
884
|
-
expect(result['div2']).to.have.property('pm_ym_bid_s', 1);
|
|
885
|
-
|
|
886
|
-
expect(result['div3']).to.have.property('pm_ym_flrs', 1);
|
|
887
|
-
expect(result['div3']).to.have.property('pm_ym_flrv', '32.00');
|
|
888
|
-
expect(result['div3']).to.have.property('pm_ym_bid_s', 2);
|
|
889
|
-
|
|
890
|
-
getGlobalStub.restore();
|
|
891
|
-
});
|
|
892
|
-
|
|
893
|
-
describe('should handle the no bid scenario correctly', function () {
|
|
894
|
-
it('should handle no bid scenario correctly', function () {
|
|
895
|
-
// Create profileConfigs with pmTargetingKeys enabled
|
|
896
|
-
const profileConfigsMock = {
|
|
897
|
-
plugins: {
|
|
898
|
-
dynamicFloors: {
|
|
899
|
-
pmTargetingKeys: {
|
|
900
|
-
enabled: true,
|
|
901
|
-
multiplier: {
|
|
902
|
-
nobid: 1.2 // Explicit nobid multiplier
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
};
|
|
908
|
-
|
|
909
|
-
// Store the original value to restore it later
|
|
910
|
-
const originalProfileConfigs = getProfileConfigs();
|
|
911
|
-
// Set profileConfigs to our mock
|
|
912
|
-
setProfileConfigs(profileConfigsMock);
|
|
913
|
-
|
|
914
|
-
// Create ad unit codes to test
|
|
915
|
-
const adUnitCodes = ['Div2'];
|
|
916
|
-
const config = {};
|
|
917
|
-
const userConsent = {};
|
|
918
|
-
|
|
919
|
-
// Create a mock auction with no bids but with RTD floor applied
|
|
920
|
-
// For this test, we'll observe what the function actually does rather than
|
|
921
|
-
// try to match specific multiplier values
|
|
922
|
-
const auction = {
|
|
923
|
-
"auctionId": "faf0b7d0-3a12-4774-826a-3d56033d9a74",
|
|
924
|
-
"auctionStatus": "completed",
|
|
925
|
-
"adUnits": [
|
|
926
|
-
{
|
|
927
|
-
"code": "Div2",
|
|
928
|
-
"sizes": [[300, 250]],
|
|
929
|
-
"mediaTypes": {
|
|
930
|
-
"banner": { "sizes": [[300, 250]] }
|
|
931
|
-
},
|
|
932
|
-
"bids": [
|
|
933
|
-
{
|
|
934
|
-
"bidder": "pubmatic",
|
|
935
|
-
"params": {
|
|
936
|
-
"publisherId": "164392",
|
|
937
|
-
"adSlot": "/4374asd3431/DMDemo1@160x600"
|
|
938
|
-
},
|
|
939
|
-
"floorData": {
|
|
940
|
-
"floorProvider": "PM"
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
]
|
|
944
|
-
}
|
|
945
|
-
],
|
|
946
|
-
"adUnitCodes": ["Div2"],
|
|
947
|
-
"bidderRequests": [
|
|
948
|
-
{
|
|
949
|
-
"bidderCode": "pubmatic",
|
|
950
|
-
"auctionId": "faf0b7d0-3a12-4774-826a-3d56033d9a74",
|
|
951
|
-
"bids": [
|
|
952
|
-
{
|
|
953
|
-
"bidder": "pubmatic",
|
|
954
|
-
"adUnitCode": "Div2",
|
|
955
|
-
"floorData": {
|
|
956
|
-
"floorProvider": "PM"
|
|
957
|
-
},
|
|
958
|
-
"mediaTypes": {
|
|
959
|
-
"banner": { "sizes": [[300, 250]] }
|
|
960
|
-
},
|
|
961
|
-
"getFloor": () => { return { floor: 0.05, currency: 'USD' }; }
|
|
962
|
-
}
|
|
963
|
-
]
|
|
964
|
-
}
|
|
965
|
-
],
|
|
966
|
-
"noBids": [
|
|
967
|
-
{
|
|
968
|
-
"bidder": "pubmatic",
|
|
969
|
-
"adUnitCode": "Div2",
|
|
970
|
-
"floorData": {
|
|
971
|
-
"floorProvider": "PM",
|
|
972
|
-
"floorMin": 0.05
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
],
|
|
976
|
-
"bidsReceived": [],
|
|
977
|
-
"bidsRejected": [],
|
|
978
|
-
"winningBids": []
|
|
979
|
-
};
|
|
980
|
-
|
|
981
|
-
const result = getTargetingData(adUnitCodes, config, userConsent, auction);
|
|
982
|
-
|
|
983
|
-
// Restore the original value
|
|
984
|
-
setProfileConfigs(originalProfileConfigs);
|
|
985
|
-
|
|
986
|
-
// Verify correct values for no bid scenario
|
|
987
|
-
expect(result['Div2']['pm_ym_flrs']).to.equal(1); // RTD floor was applied
|
|
988
|
-
expect(result['Div2']['pm_ym_bid_s']).to.equal(0); // NOBID status
|
|
989
|
-
|
|
990
|
-
// Since finding floor values from bidder requests depends on implementation details
|
|
991
|
-
// we'll just verify the type rather than specific value
|
|
992
|
-
expect(result['Div2']['pm_ym_flrv']).to.be.a('string');
|
|
993
|
-
});
|
|
994
|
-
|
|
995
|
-
it('should handle no bid scenario correctly for single ad unit multiple size scenarios', function () {
|
|
996
|
-
// Create profileConfigs with pmTargetingKeys enabled
|
|
997
|
-
const profileConfigsMock = {
|
|
998
|
-
plugins: {
|
|
999
|
-
dynamicFloors: {
|
|
1000
|
-
pmTargetingKeys: {
|
|
1001
|
-
enabled: true,
|
|
1002
|
-
multiplier: {
|
|
1003
|
-
nobid: 1.2 // Explicit nobid multiplier
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
};
|
|
1009
|
-
|
|
1010
|
-
// Store the original value to restore it later
|
|
1011
|
-
const originalProfileConfigs = getProfileConfigs();
|
|
1012
|
-
// Set profileConfigs to our mock
|
|
1013
|
-
setProfileConfigs(profileConfigsMock);
|
|
1014
|
-
|
|
1015
|
-
// Create ad unit codes to test
|
|
1016
|
-
const adUnitCodes = ['Div2'];
|
|
1017
|
-
const config = {};
|
|
1018
|
-
const userConsent = {};
|
|
1019
|
-
|
|
1020
|
-
// Create a mock auction with no bids but with RTD floor applied
|
|
1021
|
-
// For this test, we'll observe what the function actually does rather than
|
|
1022
|
-
// try to match specific multiplier values
|
|
1023
|
-
const auction = {
|
|
1024
|
-
"auctionId": "faf0b7d0-3a12-4774-826a-3d56033d9a74",
|
|
1025
|
-
"auctionStatus": "completed",
|
|
1026
|
-
"adUnits": [
|
|
1027
|
-
{
|
|
1028
|
-
"code": "Div2",
|
|
1029
|
-
"sizes": [[300, 250]],
|
|
1030
|
-
"mediaTypes": {"banner": { "sizes": [[300, 250]] }},
|
|
1031
|
-
"bids": [
|
|
1032
|
-
{
|
|
1033
|
-
"bidder": "pubmatic",
|
|
1034
|
-
"params": {
|
|
1035
|
-
"publisherId": "164392",
|
|
1036
|
-
"adSlot": "/4374asd3431/DMDemo1@160x600"
|
|
1037
|
-
},
|
|
1038
|
-
"floorData": {
|
|
1039
|
-
"floorProvider": "PM"
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
]
|
|
1043
|
-
}
|
|
1044
|
-
],
|
|
1045
|
-
"adUnitCodes": [ "Div2"],
|
|
1046
|
-
"bidderRequests": [
|
|
1047
|
-
{
|
|
1048
|
-
"bidderCode": "pubmatic",
|
|
1049
|
-
"auctionId": "faf0b7d0-3a12-4774-826a-3d56033d9a74",
|
|
1050
|
-
"bids": [
|
|
1051
|
-
{
|
|
1052
|
-
"bidder": "pubmatic",
|
|
1053
|
-
"adUnitCode": "Div2",
|
|
1054
|
-
"floorData": {
|
|
1055
|
-
"floorProvider": "PM"
|
|
1056
|
-
},
|
|
1057
|
-
"mediaTypes": {
|
|
1058
|
-
"banner": { "sizes": [[300, 250]] }
|
|
1059
|
-
},
|
|
1060
|
-
"getFloor": () => { return { floor: 5, currency: 'USD' }; }
|
|
1061
|
-
}
|
|
1062
|
-
]
|
|
1063
|
-
}
|
|
1064
|
-
],
|
|
1065
|
-
"noBids": [
|
|
1066
|
-
{
|
|
1067
|
-
"bidder": "pubmatic",
|
|
1068
|
-
"adUnitCode": "Div2",
|
|
1069
|
-
"floorData": {
|
|
1070
|
-
"floorProvider": "PM",
|
|
1071
|
-
"floorMin": 0.05
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
],
|
|
1075
|
-
"bidsReceived": [],
|
|
1076
|
-
"bidsRejected": [],
|
|
1077
|
-
"winningBids": []
|
|
1078
|
-
};
|
|
1079
|
-
|
|
1080
|
-
const result = getTargetingData(adUnitCodes, config, userConsent, auction);
|
|
1081
|
-
|
|
1082
|
-
// Restore the original value
|
|
1083
|
-
setProfileConfigs(originalProfileConfigs);
|
|
1084
|
-
|
|
1085
|
-
// Verify correct values for no bid scenario
|
|
1086
|
-
expect(result['Div2']['pm_ym_flrs']).to.equal(1); // RTD floor was applied
|
|
1087
|
-
expect(result['Div2']['pm_ym_bid_s']).to.equal(0); // NOBID status
|
|
1088
|
-
|
|
1089
|
-
// Since finding floor values from bidder requests depends on implementation details
|
|
1090
|
-
// we'll just verify the type rather than specific value
|
|
1091
|
-
expect(result['Div2']['pm_ym_flrv']).to.be.a('string');
|
|
1092
|
-
expect(result['Div2']['pm_ym_flrv']).to.equal("6.00");
|
|
1093
|
-
});
|
|
1094
|
-
|
|
1095
|
-
it('should handle no bid scenario correctly for multi-format ad unit with different floors', function () {
|
|
1096
|
-
// Create profileConfigs with pmTargetingKeys enabled
|
|
1097
|
-
const profileConfigsMock = {
|
|
1098
|
-
plugins: {
|
|
1099
|
-
dynamicFloors: {
|
|
1100
|
-
pmTargetingKeys: {
|
|
1101
|
-
enabled: true,
|
|
1102
|
-
multiplier: {
|
|
1103
|
-
nobid: 1.2 // Explicit nobid multiplier
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
};
|
|
1109
|
-
|
|
1110
|
-
// Store the original value to restore it later
|
|
1111
|
-
const originalProfileConfigs = getProfileConfigs();
|
|
1112
|
-
// Set profileConfigs to our mock
|
|
1113
|
-
setProfileConfigs(profileConfigsMock);
|
|
253
|
+
it('should return empty object if no targeting data', () => {
|
|
254
|
+
pluginManagerStub.executeHook.returns({});
|
|
1114
255
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
256
|
+
const result = pubmaticRtdProvider.getTargetingData(adUnitCodes, config, userConsent, auction);
|
|
257
|
+
expect(result).to.deep.equal({});
|
|
258
|
+
});
|
|
259
|
+
});
|
|
1119
260
|
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
const floors = {
|
|
1123
|
-
'banner': 0.50, // Higher floor for banner
|
|
1124
|
-
'video': 0.25 // Lower floor for video
|
|
1125
|
-
};
|
|
261
|
+
describe('ConfigJsonManager', () => {
|
|
262
|
+
let configManager;
|
|
1126
263
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
};
|
|
1131
|
-
};
|
|
264
|
+
beforeEach(() => {
|
|
265
|
+
configManager = pubmaticRtdProvider.ConfigJsonManager();
|
|
266
|
+
});
|
|
1132
267
|
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
"banner": {
|
|
1142
|
-
"sizes": [[300, 250], [300, 600]]
|
|
1143
|
-
},
|
|
1144
|
-
"video": {
|
|
1145
|
-
"playerSize": [[640, 480]],
|
|
1146
|
-
"context": "instream"
|
|
1147
|
-
}
|
|
1148
|
-
},
|
|
1149
|
-
"bids": [
|
|
1150
|
-
{
|
|
1151
|
-
"bidder": "pubmatic",
|
|
1152
|
-
"params": {
|
|
1153
|
-
"publisherId": "test-publisher",
|
|
1154
|
-
"adSlot": "/test/slot"
|
|
1155
|
-
},
|
|
1156
|
-
"floorData": {
|
|
1157
|
-
"floorProvider": "PM"
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
]
|
|
1161
|
-
}
|
|
1162
|
-
],
|
|
1163
|
-
"adUnitCodes": ["multiFormatDiv"],
|
|
1164
|
-
"bidderRequests": [
|
|
1165
|
-
{
|
|
1166
|
-
"bidderCode": "pubmatic",
|
|
1167
|
-
"auctionId": "multi-format-test-auction",
|
|
1168
|
-
"bids": [
|
|
1169
|
-
{
|
|
1170
|
-
"bidder": "pubmatic",
|
|
1171
|
-
"adUnitCode": "multiFormatDiv",
|
|
1172
|
-
"mediaTypes": {
|
|
1173
|
-
"banner": {
|
|
1174
|
-
"sizes": [[300, 250], [300, 600]]
|
|
1175
|
-
},
|
|
1176
|
-
"video": {
|
|
1177
|
-
"playerSize": [[640, 480]],
|
|
1178
|
-
"context": "instream"
|
|
1179
|
-
}
|
|
1180
|
-
},
|
|
1181
|
-
"floorData": {
|
|
1182
|
-
"floorProvider": "PM"
|
|
1183
|
-
},
|
|
1184
|
-
"getFloor": mockGetFloor
|
|
1185
|
-
}
|
|
1186
|
-
]
|
|
1187
|
-
}
|
|
1188
|
-
],
|
|
1189
|
-
"noBids": [
|
|
1190
|
-
{
|
|
1191
|
-
"bidder": "pubmatic",
|
|
1192
|
-
"adUnitCode": "multiFormatDiv",
|
|
1193
|
-
"floorData": {
|
|
1194
|
-
"floorProvider": "PM"
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
],
|
|
1198
|
-
"bidsReceived": [],
|
|
1199
|
-
"bidsRejected": [],
|
|
1200
|
-
"winningBids": []
|
|
1201
|
-
};
|
|
268
|
+
it('should fetch config successfully', async () => {
|
|
269
|
+
const mockResponse = {
|
|
270
|
+
ok: true,
|
|
271
|
+
headers: {
|
|
272
|
+
get: sinon.stub().withArgs('country_code').returns('US')
|
|
273
|
+
},
|
|
274
|
+
json: sinon.stub().resolves({ plugins: { test: { enabled: true } } })
|
|
275
|
+
};
|
|
1202
276
|
|
|
1203
|
-
|
|
1204
|
-
const getFloorSpy = sinon.spy(auction.bidderRequests[0].bids[0], "getFloor");
|
|
277
|
+
fetchStub.resolves(mockResponse);
|
|
1205
278
|
|
|
1206
|
-
|
|
1207
|
-
const result = getTargetingData(adUnitCodes, config, userConsent, auction);
|
|
279
|
+
const result = await configManager.fetchConfig('pub-123', 'profile-456');
|
|
1208
280
|
|
|
1209
|
-
|
|
1210
|
-
|
|
281
|
+
expect(result).to.be.true;
|
|
282
|
+
expect(fetchStub.calledOnce).to.be.true;
|
|
283
|
+
expect(fetchStub.firstCall.args[0]).to.equal(`${pubmaticRtdProvider.CONSTANTS.ENDPOINTS.BASEURL}/pub-123/profile-456/${pubmaticRtdProvider.CONSTANTS.ENDPOINTS.CONFIGS}`);
|
|
284
|
+
expect(configManager.country).to.equal('US');
|
|
285
|
+
});
|
|
1211
286
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
expect(result['multiFormatDiv']['pm_ym_bid_s']).to.equal(0); // NOBID status
|
|
287
|
+
it('should handle fetch errors', async () => {
|
|
288
|
+
fetchStub.rejects(new Error('Network error'));
|
|
1215
289
|
|
|
1216
|
-
|
|
1217
|
-
expect(getFloorSpy.called).to.be.true;
|
|
1218
|
-
let bannerCallFound = false;
|
|
1219
|
-
let videoCallFound = false;
|
|
290
|
+
const result = await configManager.fetchConfig('pub-123', 'profile-456');
|
|
1220
291
|
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
});
|
|
292
|
+
expect(result).to.be.null;
|
|
293
|
+
expect(logErrorStub.calledOnce).to.be.true;
|
|
294
|
+
expect(logErrorStub.firstCall.args[0]).to.include('Error while fetching config');
|
|
295
|
+
});
|
|
1226
296
|
|
|
1227
|
-
|
|
1228
|
-
|
|
297
|
+
it('should get config by name', () => {
|
|
298
|
+
const mockConfig = {
|
|
299
|
+
plugins: {
|
|
300
|
+
testPlugin: { enabled: true }
|
|
301
|
+
}
|
|
302
|
+
};
|
|
1229
303
|
|
|
1230
|
-
|
|
1231
|
-
// we expect the RTD provider to use the minimum floor value (0.25)
|
|
1232
|
-
// We can't test the exact value due to multiplier application, but we can make sure
|
|
1233
|
-
// it's derived from the lower value
|
|
1234
|
-
expect(parseFloat(result['multiFormatDiv']['pm_ym_flrv'])).to.be.closeTo(0.25 * 1.2, 0.001); // 0.25 * nobid multiplier (1.2)
|
|
304
|
+
configManager.setYMConfig(mockConfig);
|
|
1235
305
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
});
|
|
1239
|
-
});
|
|
306
|
+
const result = configManager.getConfigByName('testPlugin');
|
|
307
|
+
expect(result).to.deep.equal({ enabled: true });
|
|
1240
308
|
});
|
|
309
|
+
});
|
|
1241
310
|
});
|