prebid.js 9.52.0 → 9.53.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/.circleci/config.yml +27 -4
  2. package/creative/crossDomain.js +4 -2
  3. package/dist/33acrossAnalyticsAdapter.js +1 -1
  4. package/dist/33acrossBidAdapter.js +1 -1
  5. package/dist/33acrossIdSystem.js +1 -1
  6. package/dist/BTBidAdapter.js +1 -1
  7. package/dist/adagioAnalyticsAdapter.js +1 -1
  8. package/dist/adagioBidAdapter.js +1 -1
  9. package/dist/adagioUtils.js +1 -1
  10. package/dist/addefendBidAdapter.js +1 -1
  11. package/dist/adgenerationBidAdapter.js +1 -1
  12. package/dist/adlooxRtdProvider.js +1 -1
  13. package/dist/adqueryBidAdapter.js +1 -1
  14. package/dist/adrelevantisBidAdapter.js +1 -1
  15. package/dist/adstirBidAdapter.js +1 -1
  16. package/dist/adtrgtmeBidAdapter.js +1 -1
  17. package/dist/adxcgAnalyticsAdapter.js +1 -1
  18. package/dist/adxcgBidAdapter.js +1 -1
  19. package/dist/adyoulikeBidAdapter.js +1 -1
  20. package/dist/agmaAnalyticsAdapter.js +1 -1
  21. package/dist/ajaBidAdapter.js +1 -1
  22. package/dist/amxBidAdapter.js +1 -1
  23. package/dist/amxIdSystem.js +1 -1
  24. package/dist/aniviewBidAdapter.js +1 -1
  25. package/dist/appierAnalyticsAdapter.js +1 -1
  26. package/dist/appnexusBidAdapter.js +1 -1
  27. package/dist/asoBidAdapter.js +1 -1
  28. package/dist/axonixBidAdapter.js +1 -1
  29. package/dist/beopBidAdapter.js +1 -1
  30. package/dist/bidglassBidAdapter.js +1 -1
  31. package/dist/big-richmediaBidAdapter.js +1 -1
  32. package/dist/bitmediaBidAdapter.js +1 -1
  33. package/dist/bridBidAdapter.js +1 -1
  34. package/dist/bridgeuppBidAdapter.js +1 -1
  35. package/dist/bridgewellBidAdapter.js +1 -1
  36. package/dist/brightMountainMediaBidAdapter.js +1 -1
  37. package/dist/carodaBidAdapter.js +1 -1
  38. package/dist/chromeAiRtdProvider.js +1 -0
  39. package/dist/chtnwBidAdapter.js +1 -1
  40. package/dist/chunk-core.js +1 -1
  41. package/dist/concertBidAdapter.js +1 -1
  42. package/dist/connectadBidAdapter.js +1 -1
  43. package/dist/consumableBidAdapter.js +1 -1
  44. package/dist/contxtfulBidAdapter.js +1 -1
  45. package/dist/conversantAnalyticsAdapter.js +1 -1
  46. package/dist/conversantBidAdapter.js +1 -1
  47. package/dist/craftBidAdapter.js +1 -1
  48. package/dist/criteoBidAdapter.js +1 -1
  49. package/dist/cwireBidAdapter.js +1 -1
  50. package/dist/dailymotionBidAdapter.js +1 -1
  51. package/dist/debugging-standalone.js +1 -1
  52. package/dist/dependencies.json +10 -2
  53. package/dist/dspxBidAdapter.js +1 -1
  54. package/dist/dxkultureBidAdapter.js +1 -1
  55. package/dist/eplanningBidAdapter.js +1 -1
  56. package/dist/equativBidAdapter.js +1 -1
  57. package/dist/eskimiBidAdapter.js +1 -1
  58. package/dist/euidIdSystem.js +1 -1
  59. package/dist/exadsBidAdapter.js +1 -1
  60. package/dist/excoBidAdapter.js +1 -1
  61. package/dist/fanAdapter.js +1 -1
  62. package/dist/feedadBidAdapter.js +1 -1
  63. package/dist/finativeBidAdapter.js +1 -1
  64. package/dist/freewheel-sspBidAdapter.js +1 -1
  65. package/dist/gmosspBidAdapter.js +1 -1
  66. package/dist/greenbidsAnalyticsAdapter.js +1 -1
  67. package/dist/greenbidsBidAdapter.js +1 -1
  68. package/dist/greenbidsRtdProvider.js +1 -1
  69. package/dist/gridBidAdapter.js +1 -1
  70. package/dist/gumgumBidAdapter.js +1 -1
  71. package/dist/h12mediaBidAdapter.js +1 -1
  72. package/dist/hypelabBidAdapter.js +1 -1
  73. package/dist/id5AnalyticsAdapter.js +1 -1
  74. package/dist/id5IdSystem.js +1 -1
  75. package/dist/imdsBidAdapter.js +1 -1
  76. package/dist/improvedigitalBidAdapter.js +1 -1
  77. package/dist/inmobiBidAdapter.js +1 -1
  78. package/dist/insticatorBidAdapter.js +1 -1
  79. package/dist/intentIqAnalyticsAdapter.js +1 -1
  80. package/dist/ixBidAdapter.js +1 -1
  81. package/dist/jixieBidAdapter.js +1 -1
  82. package/dist/jixieIdSystem.js +1 -0
  83. package/dist/justpremiumBidAdapter.js +1 -1
  84. package/dist/kargoBidAdapter.js +1 -1
  85. package/dist/kimberliteBidAdapter.js +1 -1
  86. package/dist/konduitAnalyticsAdapter.js +1 -1
  87. package/dist/kueezBidAdapter.js +1 -1
  88. package/dist/lassoBidAdapter.js +1 -1
  89. package/dist/lifestreetBidAdapter.js +1 -1
  90. package/dist/liveIntentId.js +1 -1
  91. package/dist/logicadBidAdapter.js +1 -1
  92. package/dist/loglyliftBidAdapter.js +1 -1
  93. package/dist/luceadBidAdapter.js +1 -1
  94. package/dist/mabidderBidAdapter.js +1 -1
  95. package/dist/madsenseBidAdapter.js +1 -1
  96. package/dist/magniteAnalyticsAdapter.js +1 -1
  97. package/dist/malltvAnalyticsAdapter.js +1 -1
  98. package/dist/marsmediaBidAdapter.js +1 -1
  99. package/dist/mediafuseBidAdapter.js +1 -1
  100. package/dist/medianetBidAdapter.js +1 -1
  101. package/dist/medianetUtils.js +1 -1
  102. package/dist/mediasquareBidAdapter.js +1 -1
  103. package/dist/mgidBidAdapter.js +1 -1
  104. package/dist/missenaBidAdapter.js +1 -1
  105. package/dist/mobilefuseBidAdapter.js +1 -1
  106. package/dist/nextMillenniumBidAdapter.js +1 -1
  107. package/dist/nexx360Utils.js +1 -1
  108. package/dist/nobidAnalyticsAdapter.js +1 -1
  109. package/dist/nobidBidAdapter.js +1 -1
  110. package/dist/nodalsAiRtdProvider.js +1 -1
  111. package/dist/not-for-prod/prebid.js +175 -173
  112. package/dist/oguryBidAdapter.js +1 -1
  113. package/dist/onetagBidAdapter.js +1 -1
  114. package/dist/ooloAnalyticsAdapter.js +1 -1
  115. package/dist/openxBidAdapter.js +1 -1
  116. package/dist/optidigitalBidAdapter.js +1 -1
  117. package/dist/orbidderBidAdapter.js +1 -1
  118. package/dist/ortb2.5Translator.js +1 -1
  119. package/dist/outbrainBidAdapter.js +1 -1
  120. package/dist/ozoneBidAdapter.js +1 -1
  121. package/dist/pixfutureBidAdapter.js +1 -1
  122. package/dist/prebidServerBidAdapter.js +1 -1
  123. package/dist/publinkIdSystem.js +1 -1
  124. package/dist/pubmaticAnalyticsAdapter.js +1 -1
  125. package/dist/pubmaticBidAdapter.js +1 -1
  126. package/dist/pubmaticRtdProvider.js +1 -1
  127. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  128. package/dist/pubxaiAnalyticsAdapter.js +1 -1
  129. package/dist/pxyzBidAdapter.js +1 -1
  130. package/dist/quantcastBidAdapter.js +1 -1
  131. package/dist/readpeakBidAdapter.js +1 -1
  132. package/dist/relaidoBidAdapter.js +1 -1
  133. package/dist/relevatehealthBidAdapter.js +1 -1
  134. package/dist/retailspotBidAdapter.js +1 -1
  135. package/dist/rhythmoneBidAdapter.js +1 -1
  136. package/dist/riseUtils.js +1 -1
  137. package/dist/rubiconBidAdapter.js +1 -1
  138. package/dist/seedingAllianceBidAdapter.js +1 -1
  139. package/dist/seedtagBidAdapter.js +1 -1
  140. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  141. package/dist/sharethroughBidAdapter.js +1 -1
  142. package/dist/showheroes-bsBidAdapter.js +1 -1
  143. package/dist/smaatoBidAdapter.js +1 -1
  144. package/dist/smartadserverBidAdapter.js +1 -1
  145. package/dist/smartxBidAdapter.js +1 -1
  146. package/dist/smilewantedBidAdapter.js +1 -1
  147. package/dist/snigelBidAdapter.js +1 -1
  148. package/dist/sonobiBidAdapter.js +1 -1
  149. package/dist/sovrnBidAdapter.js +1 -1
  150. package/dist/sparteoBidAdapter.js +1 -1
  151. package/dist/sspBCBidAdapter.js +1 -1
  152. package/dist/stvBidAdapter.js +1 -1
  153. package/dist/sublimeBidAdapter.js +1 -1
  154. package/dist/taboolaBidAdapter.js +1 -1
  155. package/dist/tappxBidAdapter.js +1 -1
  156. package/dist/targetVideoBidAdapter.js +1 -1
  157. package/dist/teadsBidAdapter.js +1 -1
  158. package/dist/terceptAnalyticsAdapter.js +1 -1
  159. package/dist/themoneytizerBidAdapter.js +1 -1
  160. package/dist/trionBidAdapter.js +1 -1
  161. package/dist/tripleliftBidAdapter.js +1 -1
  162. package/dist/ttdBidAdapter.js +1 -1
  163. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  164. package/dist/uid2IdSystem.js +1 -1
  165. package/dist/underdogmediaBidAdapter.js +1 -1
  166. package/dist/undertoneBidAdapter.js +1 -1
  167. package/dist/unrulyBidAdapter.js +1 -1
  168. package/dist/userId.js +1 -1
  169. package/dist/vidazooUtils.js +1 -1
  170. package/dist/videobyteBidAdapter.js +1 -1
  171. package/dist/visxBidAdapter.js +1 -1
  172. package/dist/vuukleBidAdapter.js +1 -1
  173. package/dist/widespaceBidAdapter.js +1 -1
  174. package/dist/winrBidAdapter.js +1 -1
  175. package/dist/yahooAdsBidAdapter.js +1 -1
  176. package/dist/yandexBidAdapter.js +1 -1
  177. package/dist/yieldmoBidAdapter.js +1 -1
  178. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  179. package/gulpfile.js +12 -6
  180. package/integrationExamples/chromeai/japanese.html +224 -0
  181. package/integrationExamples/gpt/x-domain/creative.html +1 -1
  182. package/karma.conf.maker.js +7 -7
  183. package/karmaRunner.js +3 -3
  184. package/libraries/liveIntentId/shared.js +16 -0
  185. package/modules/.submodules.json +1 -0
  186. package/modules/aniviewBidAdapter.js +32 -23
  187. package/modules/chromeAiRtdProvider.js +421 -0
  188. package/modules/chromeAiRtdProvider.md +230 -0
  189. package/modules/fanAdapter.js +318 -124
  190. package/modules/ixBidAdapter.js +5 -0
  191. package/modules/jixieIdSystem.js +186 -0
  192. package/modules/ozoneBidAdapter.js +214 -336
  193. package/modules/prebidServerBidAdapter/index.js +59 -35
  194. package/modules/pubmaticRtdProvider.js +418 -4
  195. package/modules/pubmaticRtdProvider.md +12 -2
  196. package/modules/relevatehealthBidAdapter.js +20 -130
  197. package/modules/relevatehealthBidAdapter.md +1 -2
  198. package/modules/sovrnBidAdapter.js +4 -4
  199. package/modules/teadsBidAdapter.js +5 -0
  200. package/modules/ttdBidAdapter.js +5 -4
  201. package/modules/userId/index.js +30 -31
  202. package/package.json +5 -4
  203. package/src/adapterManager.js +3 -0
  204. package/test/spec/libraries/cmUtils_spec.js +17 -12
  205. package/test/spec/modules/aniviewBidAdapter_spec.js +15 -2
  206. package/test/spec/modules/chromeAiRtdProvider_spec.js +393 -0
  207. package/test/spec/modules/dgkeywordRtdProvider_spec.js +5 -2
  208. package/test/spec/modules/euidIdSystem_spec.js +9 -3
  209. package/test/spec/modules/fanAdapter_spec.js +264 -268
  210. package/test/spec/modules/id5IdSystem_spec.js +57 -101
  211. package/test/spec/modules/identityLinkIdSystem_spec.js +1 -0
  212. package/test/spec/modules/idxIdSystem_spec.js +2 -75
  213. package/test/spec/modules/instreamTracking_spec.js +15 -18
  214. package/test/spec/modules/ixBidAdapter_spec.js +26 -0
  215. package/test/spec/modules/jixieIdSystem_spec.js +303 -0
  216. package/test/spec/modules/liveIntentExternalIdSystem_spec.js +5 -0
  217. package/test/spec/modules/liveIntentIdMinimalSystem_spec.js +5 -0
  218. package/test/spec/modules/liveIntentIdSystem_spec.js +38 -0
  219. package/test/spec/modules/lmpIdSystem_spec.js +2 -81
  220. package/test/spec/modules/ozoneBidAdapter_spec.js +511 -658
  221. package/test/spec/modules/prebidServerBidAdapter_spec.js +72 -2
  222. package/test/spec/modules/pubmaticBidAdapter_spec.js +411 -126
  223. package/test/spec/modules/pubmaticRtdProvider_spec.js +946 -2
  224. package/test/spec/modules/raynRtdProvider_spec.js +18 -22
  225. package/test/spec/modules/relevatehealthBidAdapter_spec.js +9 -39
  226. package/test/spec/modules/sharedIdSystem_spec.js +3 -3
  227. package/test/spec/modules/sovrnBidAdapter_spec.js +71 -3
  228. package/test/spec/modules/teadsBidAdapter_spec.js +9 -4
  229. package/test/spec/modules/ttdBidAdapter_spec.js +24 -2
  230. package/test/spec/modules/uid2IdSystem_helpers.js +5 -2
  231. package/test/spec/modules/userId_spec.js +142 -387
  232. package/test/spec/modules/yieldoneAnalyticsAdapter_spec.js +13 -12
  233. package/test/spec/modules/zeotapIdPlusIdSystem_spec.js +3 -69
  234. package/wdio.shared.conf.js +2 -2
  235. package/CLAUDE.md +0 -1
@@ -0,0 +1,393 @@
1
+ import * as chromeAiRtdProvider from 'modules/chromeAiRtdProvider.js';
2
+ import * as utils from 'src/utils.js';
3
+ import { config } from 'src/config.js';
4
+ import * as storageManager from 'src/storageManager.js';
5
+
6
+ describe('Chrome AI RTD Provider', function() {
7
+ // Set up sandbox for all stubs
8
+ const sandbox = sinon.createSandbox();
9
+ // Mock storage manager
10
+ const mockStorage = {
11
+ hasLocalStorage: sinon.stub(),
12
+ localStorageIsEnabled: sinon.stub(),
13
+ getDataFromLocalStorage: sinon.stub(),
14
+ setDataInLocalStorage: sinon.stub()
15
+ };
16
+
17
+ // Mock page URL for testing
18
+ const mockPageUrl = 'https://example.com/test-page';
19
+
20
+ // Mock Chrome AI API instances
21
+ let mockLanguageDetectorInstance;
22
+ let mockSummarizerInstance;
23
+
24
+ // Mock API availability status
25
+ let mockLanguageDetectorAvailability;
26
+ let mockSummarizerAvailability;
27
+
28
+ // Original globals
29
+ let originalLanguageDetector;
30
+ let originalSummarizer;
31
+
32
+ // Stubs
33
+ let logMessageStub, logErrorStub; // Removed deepAccessStub, deepSetValueStub
34
+ let mockTopDocument;
35
+ let querySelectorStub;
36
+
37
+ beforeEach(function() {
38
+ // Reset sandbox for each test
39
+ sandbox.reset();
40
+
41
+ // Save original globals
42
+ originalLanguageDetector = self.LanguageDetector;
43
+ originalSummarizer = self.Summarizer;
44
+
45
+ // Create stubs
46
+ logMessageStub = sandbox.stub(utils, 'logMessage');
47
+ logErrorStub = sandbox.stub(utils, 'logError');
48
+
49
+ // Stub storage manager
50
+ sandbox.stub(storageManager, 'getCoreStorageManager').returns(mockStorage);
51
+ mockStorage.hasLocalStorage.returns(true);
52
+ mockStorage.localStorageIsEnabled.returns(true);
53
+ mockStorage.getDataFromLocalStorage.returns(null); // Default to no data
54
+ mockStorage.setDataInLocalStorage.returns(true);
55
+
56
+ // Stub document properties
57
+ querySelectorStub = sandbox.stub();
58
+ mockTopDocument = {
59
+ body: { textContent: 'Default page body text for testing.' },
60
+ title: 'Test Page Title',
61
+ querySelector: querySelectorStub
62
+ };
63
+ querySelectorStub.withArgs('article').returns(null); // Default: no article found
64
+
65
+ sandbox.stub(utils, 'getWindowTop').returns({
66
+ location: { href: mockPageUrl },
67
+ document: mockTopDocument
68
+ });
69
+
70
+ // Create mock instances
71
+ mockLanguageDetectorInstance = {
72
+ detect: sandbox.stub().resolves([{ detectedLanguage: 'en', confidence: 0.9 }]),
73
+ ready: Promise.resolve(),
74
+ addEventListener: sandbox.stub()
75
+ };
76
+
77
+ mockSummarizerInstance = {
78
+ summarize: sandbox.stub().resolves('Test summary'),
79
+ ready: Promise.resolve(),
80
+ addEventListener: sandbox.stub()
81
+ };
82
+
83
+ // Reset mock availability to default values
84
+ mockLanguageDetectorAvailability = 'available';
85
+ mockSummarizerAvailability = 'available';
86
+
87
+ // Mock global Chrome AI API constructors and their methods
88
+ // LanguageDetector
89
+ const MockLanguageDetectorFn = function() { /* This constructor body isn't called by the module */ };
90
+ Object.defineProperty(MockLanguageDetectorFn, 'name', { value: 'LanguageDetector', configurable: true });
91
+ MockLanguageDetectorFn.availability = sandbox.stub().resolves('available'); // Default to 'available'
92
+ MockLanguageDetectorFn.create = sandbox.stub().resolves(mockLanguageDetectorInstance);
93
+ self.LanguageDetector = MockLanguageDetectorFn;
94
+
95
+ // Summarizer
96
+ const MockSummarizerFn = function() { /* This constructor body isn't called by the module */ };
97
+ Object.defineProperty(MockSummarizerFn, 'name', { value: 'Summarizer', configurable: true });
98
+ MockSummarizerFn.availability = sandbox.stub().resolves('available'); // Default to 'available'
99
+ MockSummarizerFn.create = sandbox.stub().resolves(mockSummarizerInstance);
100
+ self.Summarizer = MockSummarizerFn;
101
+ });
102
+
103
+ afterEach(function() {
104
+ // Restore original globals
105
+ if (originalLanguageDetector) {
106
+ self.LanguageDetector = originalLanguageDetector;
107
+ } else {
108
+ delete self.LanguageDetector;
109
+ }
110
+
111
+ if (originalSummarizer) {
112
+ self.Summarizer = originalSummarizer;
113
+ } else {
114
+ delete self.Summarizer;
115
+ }
116
+
117
+ // Restore sandbox
118
+ sandbox.restore();
119
+ });
120
+
121
+ // Test basic module structure
122
+ describe('Module Structure', function() {
123
+ it('should have required methods', function() {
124
+ expect(chromeAiRtdProvider.chromeAiSubmodule.name).to.equal('chromeAi');
125
+ expect(typeof chromeAiRtdProvider.chromeAiSubmodule.init).to.equal('function');
126
+ expect(typeof chromeAiRtdProvider.chromeAiSubmodule.getBidRequestData).to.equal('function');
127
+ });
128
+
129
+ it('should have the correct module name', function() {
130
+ expect(chromeAiRtdProvider.chromeAiSubmodule.name).to.equal('chromeAi');
131
+ });
132
+
133
+ it('should have the correct constants', function() {
134
+ expect(chromeAiRtdProvider.CONSTANTS).to.be.an('object');
135
+ expect(chromeAiRtdProvider.CONSTANTS.SUBMODULE_NAME).to.equal('chromeAi');
136
+ expect(chromeAiRtdProvider.CONSTANTS.STORAGE_KEY).to.equal('chromeAi_detected_data');
137
+ expect(chromeAiRtdProvider.CONSTANTS.MIN_TEXT_LENGTH).to.be.a('number');
138
+ });
139
+ });
140
+
141
+ // Test initialization
142
+ describe('Initialization (init function)', function() {
143
+ beforeEach(function() {
144
+ // Simulate empty localStorage for init tests
145
+ mockStorage.getDataFromLocalStorage.withArgs(chromeAiRtdProvider.CONSTANTS.STORAGE_KEY).returns(null);
146
+ // Reset call history for setDataInLocalStorage if needed, or ensure it's clean
147
+ mockStorage.setDataInLocalStorage.resetHistory();
148
+ });
149
+
150
+ afterEach(function() {
151
+ // Clean up localStorage stubs if necessary, or reset to default behavior
152
+ mockStorage.getDataFromLocalStorage.withArgs(chromeAiRtdProvider.CONSTANTS.STORAGE_KEY).returns(null); // Reset to default for other describe blocks
153
+ mockStorage.setDataInLocalStorage.resetHistory();
154
+ });
155
+
156
+ it('should handle LanguageDetector API unavailability (when availability() returns unavailable)', function() {
157
+ // Ensure LanguageDetector constructor itself is available (which it is by beforeEach setup)
158
+ // Configure its availability() method to return 'unavailable' for this test
159
+ sandbox.stub(chromeAiRtdProvider, 'getPrioritizedLanguageData').returns(null);
160
+ self.LanguageDetector.availability.resolves('unavailable');
161
+ return chromeAiRtdProvider.chromeAiSubmodule.init({ params: { languageDetector: { enabled: true } } }).then(function(result) {
162
+ // The init might still resolve to true if other features (like summarizer if enabled & available) initialize successfully.
163
+ // We are checking that the specific error for LanguageDetector being unavailable is logged.
164
+ expect(logErrorStub.calledWith(sinon.match('ChromeAI-Rtd-Provider: LanguageDetector is unavailable.'))).to.be.true;
165
+ });
166
+ });
167
+
168
+ it('should attempt language detection if no prior language data (default config)', async function() {
169
+ // Ensure getPrioritizedLanguageData returns null to force detection path
170
+ sandbox.stub(chromeAiRtdProvider, 'getPrioritizedLanguageData').returns(null);
171
+
172
+ // Ensure getPageText returns valid text for detection
173
+ mockTopDocument.querySelector.withArgs('article').returns(null);
174
+ mockTopDocument.body.textContent = 'Sufficiently long text for detection.';
175
+
176
+ await chromeAiRtdProvider.chromeAiSubmodule.init({}); // Initialize with default config
177
+
178
+ expect(logMessageStub.calledWith(sinon.match('Initializing with config'))).to.be.true;
179
+ // Check that the actual language detection was attempted
180
+ expect(mockLanguageDetectorInstance.detect.called).to.be.true;
181
+ });
182
+
183
+ it('should handle Summarizer API unavailability (when availability() returns unavailable)', function() {
184
+ self.Summarizer.availability.resolves('unavailable');
185
+
186
+ return chromeAiRtdProvider.chromeAiSubmodule.init({ params: { summarizer: { enabled: true } } }).then(function(result) {
187
+ expect(logErrorStub.calledWith(sinon.match('ChromeAI-Rtd-Provider: Summarizer is unavailable.'))).to.be.true;
188
+ // Init might still resolve to true if other features initialize successfully.
189
+ });
190
+ });
191
+
192
+ it('should attempt model download if Summarizer availability is "after-download"', function() {
193
+ self.Summarizer.availability.resolves('after-download');
194
+
195
+ return chromeAiRtdProvider.chromeAiSubmodule.init({ params: { summarizer: { enabled: true } } }).then(() => {
196
+ expect(self.Summarizer.create.called).to.be.true;
197
+ expect(mockSummarizerInstance.addEventListener.calledWith('downloadprogress', sinon.match.func)).to.be.true;
198
+ });
199
+ });
200
+
201
+ it('should return a promise', function() {
202
+ const result = chromeAiRtdProvider.chromeAiSubmodule.init({});
203
+ expect(result).to.be.an.instanceof(Promise);
204
+ return result; // Ensure Mocha waits for the promise
205
+ });
206
+
207
+ it('should initialize with custom config', function() {
208
+ const customConfig = {
209
+ params: {
210
+ languageDetector: {
211
+ enabled: true,
212
+ ortb2Path: 'custom.language.path',
213
+ confidence: 0.7
214
+ },
215
+ summarizer: {
216
+ enabled: true,
217
+ ortb2Path: 'custom.keywords.path',
218
+ cacheInLocalStorage: true
219
+ }
220
+ }
221
+ };
222
+
223
+ return chromeAiRtdProvider.chromeAiSubmodule.init(customConfig).then(function(result) {
224
+ expect(typeof result).to.equal('boolean');
225
+ expect(logMessageStub.calledWith(sinon.match('Initializing with config'))).to.be.true;
226
+ });
227
+ });
228
+
229
+ it('should handle disabled features in config', function() {
230
+ const disabledConfig = {
231
+ params: {
232
+ languageDetector: { enabled: false },
233
+ summarizer: { enabled: false }
234
+ }
235
+ };
236
+
237
+ return chromeAiRtdProvider.chromeAiSubmodule.init(disabledConfig).then(function(result) {
238
+ expect(result).to.be.true;
239
+ expect(logMessageStub.calledWith(sinon.match('Language detection disabled by config'))).to.be.true;
240
+ expect(logMessageStub.calledWith(sinon.match('Summarizer disabled by config.'))).to.be.true;
241
+ });
242
+ });
243
+ });
244
+
245
+ // Test storage functions
246
+ describe('Storage Functions', function() {
247
+ beforeEach(function() {
248
+ mockStorage.getDataFromLocalStorage.resetHistory();
249
+ mockStorage.setDataInLocalStorage.resetHistory();
250
+ mockStorage.setDataInLocalStorage.returns(true); // Default success
251
+ });
252
+
253
+ describe('chromeAiRtdProvider._getChromeAiDataFromLocalStorage', function() {
254
+ it('should return null if localStorage is not available', function() {
255
+ mockStorage.hasLocalStorage.returns(false);
256
+ expect(chromeAiRtdProvider._getChromeAiDataFromLocalStorage(mockPageUrl)).to.be.null;
257
+ });
258
+
259
+ it('should return null if localStorage is not enabled', function() {
260
+ mockStorage.localStorageIsEnabled.returns(false);
261
+ expect(chromeAiRtdProvider._getChromeAiDataFromLocalStorage(mockPageUrl)).to.be.null;
262
+ });
263
+
264
+ it('should return null if no data in localStorage for the URL', function() {
265
+ mockStorage.getDataFromLocalStorage.withArgs(chromeAiRtdProvider.CONSTANTS.STORAGE_KEY).returns(JSON.stringify({ 'other/url': {} }));
266
+ expect(chromeAiRtdProvider._getChromeAiDataFromLocalStorage(mockPageUrl)).to.be.null;
267
+ });
268
+ });
269
+ describe('chromeAiRtdProvider.storeDetectedKeywords', function() {
270
+ it('should return false if keywords are not provided or empty', function() {
271
+ expect(chromeAiRtdProvider.storeDetectedKeywords(null, mockPageUrl)).to.be.false;
272
+ expect(chromeAiRtdProvider.storeDetectedKeywords([], mockPageUrl)).to.be.false;
273
+ expect(logMessageStub.calledWith(sinon.match('No valid keywords array to store'))).to.be.true;
274
+ });
275
+ });
276
+ });
277
+
278
+ // Test language detection main function
279
+ describe('chromeAiRtdProvider.detectLanguage (main function)', function() {
280
+ it('should detect language using Chrome AI API', async function() {
281
+ const result = await chromeAiRtdProvider.detectLanguage('This is a test text');
282
+ expect(result).to.deep.equal({ language: 'en', confidence: 0.9 });
283
+ expect(mockLanguageDetectorInstance.detect.calledOnceWith('This is a test text')).to.be.true;
284
+ });
285
+
286
+ it('should return null if API is not available', async function() {
287
+ self.LanguageDetector.create.resolves(null); // Simulate API creation failure
288
+ const result = await chromeAiRtdProvider.detectLanguage('This is a test text');
289
+ expect(result).to.be.null;
290
+ });
291
+
292
+ it('should return null if confidence is below threshold', async function() {
293
+ mockLanguageDetectorInstance.detect.resolves([{ detectedLanguage: 'en', confidence: 0.5 }]);
294
+ // Need to re-init to pick up the new default config confidence if it changed, or set it explicitly
295
+ await chromeAiRtdProvider.chromeAiSubmodule.init({ params: { languageDetector: { confidence: 0.8 } } });
296
+ const result = await chromeAiRtdProvider.detectLanguage('This is a test text');
297
+ expect(result).to.be.null;
298
+ });
299
+ });
300
+ // Test getBidRequestData
301
+ describe('getBidRequestData', function() {
302
+ let reqBidsConfigObj;
303
+ let onDoneSpy;
304
+
305
+ beforeEach(async function() {
306
+ // Initialize the module with a config that enables both features for these tests
307
+ await chromeAiRtdProvider.chromeAiSubmodule.init({
308
+ params: {
309
+ languageDetector: { enabled: true, ortb2Path: 'site.content.language' },
310
+ summarizer: { enabled: true, ortb2Path: 'site.content.ext.keywords', cacheInLocalStorage: false }
311
+ }
312
+ });
313
+
314
+ reqBidsConfigObj = {
315
+ adUnits: [{ code: 'adunit1' }],
316
+ ortb2Fragments: {
317
+ global: {}
318
+ }
319
+ };
320
+ onDoneSpy = sinon.spy();
321
+ // Reset stubs that might be called by getBidRequestData indirectly via init or helper functions
322
+ logMessageStub.resetHistory();
323
+ });
324
+
325
+ it('should call the callback function', function() {
326
+ chromeAiRtdProvider.chromeAiSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy);
327
+ expect(onDoneSpy.calledOnce).to.be.true;
328
+ });
329
+
330
+ it('should ensure ortb2Fragments.global exists', function() {
331
+ delete reqBidsConfigObj.ortb2Fragments.global;
332
+ chromeAiRtdProvider.chromeAiSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy);
333
+ expect(reqBidsConfigObj.ortb2Fragments.global).to.be.an('object');
334
+ });
335
+
336
+ it('should not enrich language if already present in auction ORTB2', function() {
337
+ // Set language directly in ortb2Fragments for this test case
338
+ utils.deepSetValue(reqBidsConfigObj.ortb2Fragments.global, 'site.content.language', 'es');
339
+
340
+ chromeAiRtdProvider.chromeAiSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy);
341
+
342
+ // Verify that the language was not changed
343
+ expect(utils.deepAccess(reqBidsConfigObj.ortb2Fragments.global, 'site.content.language')).to.equal('es');
344
+ expect(logMessageStub.calledWith(sinon.match('Lang already in auction ORTB2 at path'))).to.be.true;
345
+ });
346
+
347
+ it('should enrich with detected keywords if not in auction ORTB2', async function() {
348
+ mockSummarizerInstance.summarize.resolves('newly detected summary');
349
+ await chromeAiRtdProvider.chromeAiSubmodule.init({ // Re-init to trigger summarizer with mocks
350
+ params: {
351
+ summarizer: { enabled: true, ortb2Path: 'site.content.ext.keywords', cacheInLocalStorage: false },
352
+ languageDetector: { enabled: false } // Disable lang to isolate test
353
+ }
354
+ });
355
+
356
+ chromeAiRtdProvider.chromeAiSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy);
357
+ expect(utils.deepAccess(reqBidsConfigObj.ortb2Fragments.global, 'site.content.ext.keywords')).to.deep.equal(['newly detected summary']);
358
+ });
359
+
360
+ it('should not enrich keywords if already present in auction ORTB2', function() {
361
+ // Set keywords directly in ortb2Fragments for this test case
362
+ utils.deepSetValue(reqBidsConfigObj.ortb2Fragments.global, 'site.content.ext.keywords', ['existing', 'keywords']);
363
+
364
+ chromeAiRtdProvider.chromeAiSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy);
365
+
366
+ // Verify that keywords were not changed
367
+ expect(utils.deepAccess(reqBidsConfigObj.ortb2Fragments.global, 'site.content.ext.keywords')).to.deep.equal(['existing', 'keywords']);
368
+ expect(logMessageStub.calledWith(sinon.match('Keywords already present in auction_ortb2 at path'))).to.be.true;
369
+ });
370
+
371
+ it('should handle language detection disabled', function() {
372
+ chromeAiRtdProvider.chromeAiSubmodule.init({ params: { languageDetector: { enabled: false } } }); // Re-init with lang disabled
373
+ chromeAiRtdProvider.chromeAiSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy);
374
+ expect(logMessageStub.calledWith(sinon.match('Language detection disabled, no lang enrichment.'))).to.be.true;
375
+ const langPath = chromeAiRtdProvider.CONSTANTS.DEFAULT_CONFIG.languageDetector.ortb2Path;
376
+ // Check that language was not set by trying to access it; it should be undefined or its original value if any
377
+ // This is a bit indirect. If we could spy on deepSetValue, it would be cleaner.
378
+ // For now, we assume if it's not set to the detected value, the non-enrichment path was taken.
379
+ // A more robust check would be to ensure no new properties were added if it was initially empty.
380
+ expect(utils.deepAccess(reqBidsConfigObj.ortb2Fragments.global, langPath)).to.be.undefined;
381
+ });
382
+
383
+ it('should handle summarizer disabled', function() {
384
+ chromeAiRtdProvider.chromeAiSubmodule.init({ params: { summarizer: { enabled: false } } }); // Re-init with summarizer disabled
385
+ chromeAiRtdProvider.chromeAiSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy);
386
+ expect(logMessageStub.calledWith(sinon.match('Summarizer disabled, no keyword enrichment.'))).to.be.true;
387
+ // Check that no keyword enrichment was attempted
388
+ const keywordPath = chromeAiRtdProvider.CONSTANTS.DEFAULT_CONFIG.summarizer.ortb2Path; // or the configured one
389
+ // Verify that keywords were not set by checking the path
390
+ expect(utils.deepAccess(reqBidsConfigObj.ortb2Fragments.global, keywordPath)).to.be.undefined;
391
+ });
392
+ });
393
+ });
@@ -280,6 +280,7 @@ describe('Digital Garage Keyword Module', function () {
280
280
  request.respond(404);
281
281
  });
282
282
  it('should get profiles timeout.', function (done) {
283
+ const clock = sinon.useFakeTimers();
283
284
  let pbjs = cloneDeep(config);
284
285
  pbjs.adUnits = cloneDeep(AD_UNITS);
285
286
  let moduleConfig = cloneDeep(DEF_CONFIG);
@@ -310,7 +311,8 @@ describe('Digital Garage Keyword Module', function () {
310
311
  null
311
312
  );
312
313
  const request = server.requests[0];
313
- setTimeout(() => {
314
+ if (request) {
315
+ clock.tick(50);
314
316
  if (request) {
315
317
  request.respond(
316
318
  200,
@@ -318,7 +320,8 @@ describe('Digital Garage Keyword Module', function () {
318
320
  JSON.stringify(DUMMY_RESPONSE)
319
321
  );
320
322
  }
321
- }, 50);
323
+ }
324
+ clock.restore();
322
325
  });
323
326
  it('should get profiles ok(200).', function (done) {
324
327
  let pbjs = cloneDeep(config);
@@ -38,9 +38,15 @@ const cstgApiUrl = 'https://prod.euid.eu/v2/token/client-generate';
38
38
  const headers = { 'Content-Type': 'application/json' };
39
39
  const makeSuccessResponseBody = (token) => btoa(JSON.stringify({ status: 'success', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: token } }));
40
40
  const makeOptoutResponseBody = (token) => btoa(JSON.stringify({ status: 'optout', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: token } }));
41
- const expectToken = (bid, token) => expect(bid?.userId ?? {}).to.deep.include(makeEuidIdentityContainer(token));
42
- const expectOptout = (bid, token) => expect(bid?.userId ?? {}).to.deep.include(makeEuidOptoutContainer(token));
43
- const expectNoIdentity = (bid) => expect(bid).to.not.haveOwnProperty('userId');
41
+ function findEuid(bid) {
42
+ return (bid?.userIdAsEids ?? []).find(e => e.source === 'euid.eu');
43
+ }
44
+ const expectToken = (bid, token) => {
45
+ const eid = findEuid(bid);
46
+ expect(eid && eid.uids[0].id).to.equal(token);
47
+ };
48
+ const expectOptout = (bid) => expect(findEuid(bid)).to.be.undefined;
49
+ const expectNoIdentity = (bid) => expect(findEuid(bid)).to.be.undefined;
44
50
 
45
51
  describe('EUID module', function() {
46
52
  let suiteSandbox, restoreSubtleToUndefined = false;