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.
- package/.circleci/config.yml +27 -4
- package/creative/crossDomain.js +4 -2
- 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/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/chromeAiRtdProvider.js +1 -0
- 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/debugging-standalone.js +1 -1
- package/dist/dependencies.json +10 -2
- 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/fanAdapter.js +1 -1
- package/dist/feedadBidAdapter.js +1 -1
- package/dist/finativeBidAdapter.js +1 -1
- package/dist/freewheel-sspBidAdapter.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/jixieIdSystem.js +1 -0
- 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 -173
- 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/optidigitalBidAdapter.js +1 -1
- package/dist/orbidderBidAdapter.js +1 -1
- package/dist/ortb2.5Translator.js +1 -1
- package/dist/outbrainBidAdapter.js +1 -1
- package/dist/ozoneBidAdapter.js +1 -1
- package/dist/pixfutureBidAdapter.js +1 -1
- package/dist/prebidServerBidAdapter.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/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/relevatehealthBidAdapter.js +1 -1
- package/dist/retailspotBidAdapter.js +1 -1
- package/dist/rhythmoneBidAdapter.js +1 -1
- package/dist/riseUtils.js +1 -1
- package/dist/rubiconBidAdapter.js +1 -1
- package/dist/seedingAllianceBidAdapter.js +1 -1
- package/dist/seedtagBidAdapter.js +1 -1
- 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/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/userId.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/gulpfile.js +12 -6
- package/integrationExamples/chromeai/japanese.html +224 -0
- package/integrationExamples/gpt/x-domain/creative.html +1 -1
- package/karma.conf.maker.js +7 -7
- package/karmaRunner.js +3 -3
- package/libraries/liveIntentId/shared.js +16 -0
- package/modules/.submodules.json +1 -0
- package/modules/aniviewBidAdapter.js +32 -23
- package/modules/chromeAiRtdProvider.js +421 -0
- package/modules/chromeAiRtdProvider.md +230 -0
- package/modules/fanAdapter.js +318 -124
- package/modules/ixBidAdapter.js +5 -0
- package/modules/jixieIdSystem.js +186 -0
- package/modules/ozoneBidAdapter.js +214 -336
- package/modules/prebidServerBidAdapter/index.js +59 -35
- package/modules/pubmaticRtdProvider.js +418 -4
- package/modules/pubmaticRtdProvider.md +12 -2
- package/modules/relevatehealthBidAdapter.js +20 -130
- package/modules/relevatehealthBidAdapter.md +1 -2
- package/modules/sovrnBidAdapter.js +4 -4
- package/modules/teadsBidAdapter.js +5 -0
- package/modules/ttdBidAdapter.js +5 -4
- package/modules/userId/index.js +30 -31
- package/package.json +5 -4
- package/src/adapterManager.js +3 -0
- package/test/spec/libraries/cmUtils_spec.js +17 -12
- package/test/spec/modules/aniviewBidAdapter_spec.js +15 -2
- package/test/spec/modules/chromeAiRtdProvider_spec.js +393 -0
- package/test/spec/modules/dgkeywordRtdProvider_spec.js +5 -2
- package/test/spec/modules/euidIdSystem_spec.js +9 -3
- package/test/spec/modules/fanAdapter_spec.js +264 -268
- package/test/spec/modules/id5IdSystem_spec.js +57 -101
- package/test/spec/modules/identityLinkIdSystem_spec.js +1 -0
- package/test/spec/modules/idxIdSystem_spec.js +2 -75
- package/test/spec/modules/instreamTracking_spec.js +15 -18
- package/test/spec/modules/ixBidAdapter_spec.js +26 -0
- package/test/spec/modules/jixieIdSystem_spec.js +303 -0
- package/test/spec/modules/liveIntentExternalIdSystem_spec.js +5 -0
- package/test/spec/modules/liveIntentIdMinimalSystem_spec.js +5 -0
- package/test/spec/modules/liveIntentIdSystem_spec.js +38 -0
- package/test/spec/modules/lmpIdSystem_spec.js +2 -81
- package/test/spec/modules/ozoneBidAdapter_spec.js +511 -658
- package/test/spec/modules/prebidServerBidAdapter_spec.js +72 -2
- package/test/spec/modules/pubmaticBidAdapter_spec.js +411 -126
- package/test/spec/modules/pubmaticRtdProvider_spec.js +946 -2
- package/test/spec/modules/raynRtdProvider_spec.js +18 -22
- package/test/spec/modules/relevatehealthBidAdapter_spec.js +9 -39
- package/test/spec/modules/sharedIdSystem_spec.js +3 -3
- package/test/spec/modules/sovrnBidAdapter_spec.js +71 -3
- package/test/spec/modules/teadsBidAdapter_spec.js +9 -4
- package/test/spec/modules/ttdBidAdapter_spec.js +24 -2
- package/test/spec/modules/uid2IdSystem_helpers.js +5 -2
- package/test/spec/modules/userId_spec.js +142 -387
- package/test/spec/modules/yieldoneAnalyticsAdapter_spec.js +13 -12
- package/test/spec/modules/zeotapIdPlusIdSystem_spec.js +3 -69
- package/wdio.shared.conf.js +2 -2
- 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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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;
|