prebid-universal-creative 1.13.0 → 1.14.1
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 +3 -3
- package/.github/workflows/issue_tracker.yml +89 -0
- package/README.md +1 -1
- package/browsers.json +17 -33
- package/dist/creative.js +3 -3
- package/dist/creative.max.js +516 -420
- package/dist/native-render.js +3 -3
- package/dist/native-trk.js +3 -3
- package/dist/uid.js +2 -2
- package/package.json +12 -7
- package/src/messaging.js +43 -0
- package/src/nativeAssetManager.js +185 -77
- package/src/nativeORTBTrackerManager.js +32 -0
- package/src/nativeRenderManager.js +10 -12
- package/src/nativeTrackerManager.js +18 -7
- package/src/renderingManager.js +58 -38
- package/src/utils.js +3 -0
- package/test/helpers/mocks.js +7 -3
- package/test/spec/messaging_spec.js +64 -0
- package/test/spec/nativeAssetManager_spec.js +493 -347
- package/test/spec/nativeORTBTrackerManager_spec.js +76 -0
- package/test/spec/nativeRenderManager_spec.js +18 -1
- package/test/spec/renderingManager_spec.js +133 -43
@@ -0,0 +1,76 @@
|
|
1
|
+
import { addNativeClickTrackers, fireNativeImpressionTrackers } from 'src/nativeORTBTrackerManager';
|
2
|
+
import * as utils from 'src/utils';
|
3
|
+
|
4
|
+
describe('test firing native trackers', function () {
|
5
|
+
let triggerPixel;
|
6
|
+
let loadScript;
|
7
|
+
let getElementsByClassName;
|
8
|
+
let sendMessage;
|
9
|
+
|
10
|
+
beforeEach(function () {
|
11
|
+
triggerPixel = sinon.stub(utils, 'triggerPixel');
|
12
|
+
loadScript = sinon.stub(utils, 'loadScript');
|
13
|
+
sendMessage = sinon.spy();
|
14
|
+
|
15
|
+
getElementsByClassName = sinon.stub(document, 'getElementsByClassName').callsFake(() => {
|
16
|
+
return [{
|
17
|
+
addEventListener: (event, callback, capture) => {
|
18
|
+
// immediately call the callback to test the click
|
19
|
+
callback({
|
20
|
+
target: {
|
21
|
+
getAttribute: (name) => {
|
22
|
+
return 1;
|
23
|
+
}
|
24
|
+
}
|
25
|
+
})
|
26
|
+
}
|
27
|
+
}]
|
28
|
+
});
|
29
|
+
});
|
30
|
+
|
31
|
+
afterEach(function () {
|
32
|
+
triggerPixel.restore();
|
33
|
+
loadScript.restore();
|
34
|
+
getElementsByClassName.restore();
|
35
|
+
sendMessage.resetHistory();
|
36
|
+
});
|
37
|
+
|
38
|
+
|
39
|
+
it('should fire impression trackers', function () {
|
40
|
+
let imgUrl = 'foo.bar/event?type=img';
|
41
|
+
let jsUrl = 'foo.bar/event?type=js';
|
42
|
+
|
43
|
+
|
44
|
+
fireNativeImpressionTrackers("abc123", sendMessage);
|
45
|
+
|
46
|
+
expect(sendMessage.getCall(0).args[0]).to.deep.equal({
|
47
|
+
message: 'Prebid Native',
|
48
|
+
action: 'fireNativeImpressionTrackers',
|
49
|
+
adId: 'abc123'
|
50
|
+
})
|
51
|
+
});
|
52
|
+
|
53
|
+
it('should fire asset clicktrackers', function () {
|
54
|
+
let assetTrackers = ['foo.bar/click?id=1', 'foo.bar/click?id=2'];
|
55
|
+
let mainTrackers = ['foo.bar/click?id=3'];
|
56
|
+
let adId = "abc123";
|
57
|
+
let nativeOrtb = {
|
58
|
+
assets: [{
|
59
|
+
id: 1,
|
60
|
+
link: { clicktrackers: assetTrackers }
|
61
|
+
}],
|
62
|
+
link: {
|
63
|
+
clicktrackers: mainTrackers
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
addNativeClickTrackers(adId, nativeOrtb, sendMessage);
|
68
|
+
expect(sendMessage.getCall(0).args[0]).to.deep.equal({
|
69
|
+
message: "Prebid Native",
|
70
|
+
action: 'click',
|
71
|
+
adId: 'abc123',
|
72
|
+
assetId: 1
|
73
|
+
});
|
74
|
+
|
75
|
+
});
|
76
|
+
});
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import { newNativeRenderManager } from 'src/nativeRenderManager';
|
2
|
+
import * as nam from 'src/nativeAssetManager';
|
2
3
|
import { expect } from 'chai';
|
3
4
|
import { mocks } from 'test/helpers/mocks';
|
4
5
|
import { merge } from 'lodash';
|
@@ -22,6 +23,17 @@ describe('nativeRenderManager', function () {
|
|
22
23
|
describe('load renderNativeAd', function () {
|
23
24
|
let mockWin;
|
24
25
|
let consoleWarn;
|
26
|
+
let assetManagerStub;
|
27
|
+
|
28
|
+
before(function() {
|
29
|
+
assetManagerStub = sinon.stub(nam, 'newNativeAssetManager').callsFake(() => {
|
30
|
+
return {
|
31
|
+
loadAssets: (adId, callback) => {
|
32
|
+
callback();
|
33
|
+
}
|
34
|
+
}
|
35
|
+
});
|
36
|
+
});
|
25
37
|
|
26
38
|
let tagData = {
|
27
39
|
pubUrl: 'http://example.com',
|
@@ -36,6 +48,11 @@ describe('nativeRenderManager', function () {
|
|
36
48
|
|
37
49
|
afterEach(function () {
|
38
50
|
consoleWarn.restore();
|
51
|
+
assetManagerStub.resetHistory();
|
52
|
+
});
|
53
|
+
|
54
|
+
after(function() {
|
55
|
+
assetManagerStub.restore();
|
39
56
|
});
|
40
57
|
|
41
58
|
it('should verify the postMessage for impression trackers was executed', function() {
|
@@ -81,7 +98,7 @@ describe('nativeRenderManager', function () {
|
|
81
98
|
expect(mockWin.parent.postMessage.callCount).to.equal(2);
|
82
99
|
|
83
100
|
let postMessageTargetDomain = mockWin.parent.postMessage.args[0][1];
|
84
|
-
let postMessageContents = mockWin.parent.postMessage.args[
|
101
|
+
let postMessageContents = mockWin.parent.postMessage.args[1][0];
|
85
102
|
let rawPostMessage = JSON.parse(postMessageContents);
|
86
103
|
|
87
104
|
expect(rawPostMessage.message).to.exist.and.to.equal("Prebid Native");
|
@@ -4,11 +4,11 @@ import * as domHelper from 'src/domHelper';
|
|
4
4
|
import { expect } from 'chai';
|
5
5
|
import { mocks } from 'test/helpers/mocks';
|
6
6
|
import { merge } from 'lodash';
|
7
|
-
import * as postscribe from "postscribe";
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
function renderingMocks() {
|
9
|
+
return {
|
10
|
+
messages: [],
|
11
|
+
getWindowObject: function() {
|
12
12
|
return {
|
13
13
|
document: {
|
14
14
|
body: {
|
@@ -39,13 +39,17 @@ const renderingMocks = {
|
|
39
39
|
innerHeight: 250
|
40
40
|
}
|
41
41
|
}
|
42
|
+
}
|
42
43
|
}
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
function createMockIframe() {
|
46
|
+
return {
|
47
|
+
contentDocument: {
|
48
|
+
open: sinon.spy(),
|
49
|
+
write: sinon.spy(),
|
50
|
+
close: sinon.spy()
|
51
|
+
},
|
52
|
+
style: {},
|
49
53
|
}
|
50
54
|
}
|
51
55
|
|
@@ -76,7 +80,7 @@ describe('renderingManager', function() {
|
|
76
80
|
writeHtmlSpy = sinon.spy(utils, 'writeAdHtml');
|
77
81
|
sendRequestSpy = sinon.spy(utils, 'sendRequest');
|
78
82
|
triggerPixelSpy = sinon.spy(utils, 'triggerPixel');
|
79
|
-
mockWin = merge(mocks.createFakeWindow('http://example.com'), renderingMocks.getWindowObject());
|
83
|
+
mockWin = merge(mocks.createFakeWindow('http://example.com'), renderingMocks().getWindowObject());
|
80
84
|
});
|
81
85
|
|
82
86
|
afterEach(function() {
|
@@ -203,7 +207,7 @@ describe('renderingManager', function() {
|
|
203
207
|
writeHtmlSpy = sinon.spy(utils, 'writeAdHtml');
|
204
208
|
sendRequestSpy = sinon.spy(utils, 'sendRequest');
|
205
209
|
triggerPixelSpy = sinon.spy(utils, 'triggerPixel');
|
206
|
-
mockWin = merge(mocks.createFakeWindow('http://example.com'), renderingMocks.getWindowObject());
|
210
|
+
mockWin = merge(mocks.createFakeWindow('http://example.com'), renderingMocks().getWindowObject());
|
207
211
|
});
|
208
212
|
|
209
213
|
afterEach(function() {
|
@@ -304,64 +308,150 @@ describe('renderingManager', function() {
|
|
304
308
|
});
|
305
309
|
|
306
310
|
describe('cross domain creative', function() {
|
311
|
+
const ORIGIN = 'http://example.com';
|
307
312
|
let parseStub;
|
308
313
|
let iframeStub;
|
309
314
|
let triggerPixelSpy;
|
315
|
+
let mockWin;
|
316
|
+
let env;
|
317
|
+
let renderObject;
|
318
|
+
let ucTagData;
|
319
|
+
let mockIframe;
|
320
|
+
let eventSource;
|
321
|
+
|
310
322
|
beforeEach(function(){
|
323
|
+
mockIframe = createMockIframe();
|
311
324
|
parseStub = sinon.stub(utils, 'parseUrl');
|
312
|
-
iframeStub = sinon.stub(domHelper, 'getEmptyIframe');
|
325
|
+
iframeStub = sinon.stub(domHelper, 'getEmptyIframe').returns(mockIframe);
|
313
326
|
triggerPixelSpy = sinon.stub(utils, 'triggerPixel');
|
314
|
-
});
|
315
|
-
|
316
|
-
after(function() {
|
317
|
-
parseStub.restore();
|
318
|
-
iframeStub.restore();
|
319
|
-
triggerPixelSpy.restore();
|
320
|
-
});
|
321
|
-
|
322
|
-
it('should render cross domain creative', function() {
|
323
327
|
parseStub.returns({
|
324
328
|
protocol: 'http',
|
325
329
|
host: 'example.com'
|
326
330
|
});
|
327
|
-
|
328
|
-
|
329
|
-
const mockWin = merge(mocks.createFakeWindow('http://example.com'), renderingMocks.getWindowObject());
|
330
|
-
const env = {
|
331
|
+
mockWin = merge(mocks.createFakeWindow(ORIGIN), renderingMocks().getWindowObject());
|
332
|
+
env = {
|
331
333
|
isMobileApp: () => false,
|
332
334
|
isAmp: () => false,
|
333
335
|
canLocatePrebid: () => false
|
334
336
|
};
|
335
|
-
|
336
|
-
|
337
|
+
renderObject = newRenderingManager(mockWin, env);
|
338
|
+
ucTagData = {
|
337
339
|
adId: '123',
|
338
340
|
adServerDomain: 'mypub.com',
|
339
|
-
pubUrl:
|
341
|
+
pubUrl: ORIGIN,
|
340
342
|
};
|
341
|
-
|
342
343
|
renderObject.renderAd(mockWin.document, ucTagData);
|
343
344
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
345
|
+
});
|
346
|
+
|
347
|
+
afterEach(function() {
|
348
|
+
parseStub.restore();
|
349
|
+
iframeStub.restore();
|
350
|
+
triggerPixelSpy.restore();
|
351
|
+
});
|
352
|
+
|
353
|
+
function mockPrebidResponse(msg) {
|
354
|
+
mockWin.postMessage({
|
355
|
+
origin: ORIGIN,
|
356
|
+
message: JSON.stringify(Object.assign({message: 'Prebid Response'}, msg))
|
357
|
+
});
|
358
|
+
}
|
356
359
|
|
357
|
-
|
360
|
+
it('should render cross domain creative', function() {
|
361
|
+
mockPrebidResponse({
|
362
|
+
ad: 'ad',
|
363
|
+
adUrl: ORIGIN,
|
364
|
+
adId: '123',
|
365
|
+
width: 300,
|
366
|
+
height: 250
|
367
|
+
});
|
358
368
|
expect(mockIframe.contentDocument.write.args[0][0]).to.equal("ad");
|
359
369
|
});
|
370
|
+
|
371
|
+
describe('should signal event', () => {
|
372
|
+
const RENDER_FAILED = 'adRenderFailed',
|
373
|
+
RENDER_SUCCESS = 'adRenderSucceeded';
|
374
|
+
|
375
|
+
function expectEventMessage(expected) {
|
376
|
+
const actual = JSON.parse(mockWin.parent.postMessage.args[1][0]);
|
377
|
+
sinon.assert.match(actual, Object.assign({message: 'Prebid Event'}, expected));
|
378
|
+
}
|
379
|
+
|
380
|
+
describe('AD_RENDER_FAILED', () => {
|
381
|
+
it('on video ads', () => {
|
382
|
+
mockPrebidResponse({
|
383
|
+
ad: 'ad',
|
384
|
+
adId: '123',
|
385
|
+
mediaType: 'video'
|
386
|
+
});
|
387
|
+
expectEventMessage({
|
388
|
+
adId: '123',
|
389
|
+
event: RENDER_FAILED,
|
390
|
+
info: {
|
391
|
+
reason: 'preventWritingOnMainDocument'
|
392
|
+
}
|
393
|
+
})
|
394
|
+
});
|
395
|
+
|
396
|
+
it('on ads that have no markup or adUrl', () => {
|
397
|
+
mockPrebidResponse({
|
398
|
+
adId: '123',
|
399
|
+
})
|
400
|
+
expectEventMessage({
|
401
|
+
adId: '123',
|
402
|
+
event: RENDER_FAILED,
|
403
|
+
info: {
|
404
|
+
reason: 'noAd'
|
405
|
+
}
|
406
|
+
});
|
407
|
+
});
|
408
|
+
|
409
|
+
it('on exceptions', () => {
|
410
|
+
iframeStub.callsFake(() => {
|
411
|
+
throw new Error()
|
412
|
+
});
|
413
|
+
mockPrebidResponse({
|
414
|
+
adId: '123',
|
415
|
+
ad: 'ad',
|
416
|
+
adUrl: ORIGIN,
|
417
|
+
});
|
418
|
+
expectEventMessage({
|
419
|
+
adId: '123',
|
420
|
+
event: RENDER_FAILED,
|
421
|
+
info: {
|
422
|
+
reason: 'exception'
|
423
|
+
}
|
424
|
+
});
|
425
|
+
})
|
426
|
+
});
|
427
|
+
describe('should post AD_RENDER_SUCCEEDED', () => {
|
428
|
+
it('on ad with markup', () => {
|
429
|
+
mockPrebidResponse({
|
430
|
+
adId: '123',
|
431
|
+
ad: 'markup'
|
432
|
+
});
|
433
|
+
expectEventMessage({
|
434
|
+
adId: '123',
|
435
|
+
event: RENDER_SUCCESS
|
436
|
+
});
|
437
|
+
});
|
438
|
+
it('on ad with adUrl', () => {
|
439
|
+
mockPrebidResponse({
|
440
|
+
adId: '123',
|
441
|
+
adUrl: 'url'
|
442
|
+
});
|
443
|
+
expectEventMessage({
|
444
|
+
adId: '123',
|
445
|
+
event: RENDER_SUCCESS
|
446
|
+
});
|
447
|
+
})
|
448
|
+
})
|
449
|
+
});
|
360
450
|
});
|
361
451
|
|
362
452
|
describe('legacy creative', function() {
|
363
453
|
it('should render legacy creative', function() {
|
364
|
-
const mockWin = merge(mocks.createFakeWindow('http://example.com'), renderingMocks.getWindowObject());
|
454
|
+
const mockWin = merge(mocks.createFakeWindow('http://example.com'), renderingMocks().getWindowObject());
|
365
455
|
const env = {
|
366
456
|
isMobileApp: () => false,
|
367
457
|
isAmp: () => false,
|