Skip to content

Commit def50a1

Browse files
author
Michele Nasti
committed
Signal AD_RENDER_FAILED / AD_RENDER_SUCCEEDED events to Prebid (prebid#152)
1 parent 30cb542 commit def50a1

File tree

2 files changed

+175
-56
lines changed

2 files changed

+175
-56
lines changed

src/renderingManager.js

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -57,32 +57,58 @@ export function renderCrossDomain(win, adId, pubAdServerDomain = '', pubUrl) {
5757
let origin = ev.origin || ev.originalEvent.origin;
5858
if (adObject.message && adObject.message === 'Prebid Response' &&
5959
publisherDomain === origin &&
60-
adObject.adId === adId &&
61-
(adObject.ad || adObject.adUrl)) {
62-
let body = win.document.body;
63-
let ad = adObject.ad;
64-
let url = adObject.adUrl;
65-
let width = adObject.width;
66-
let height = adObject.height;
60+
adObject.adId === adId) {
61+
try {
62+
let body = win.document.body;
63+
let ad = adObject.ad;
64+
let url = adObject.adUrl;
65+
let width = adObject.width;
66+
let height = adObject.height;
67+
68+
if (adObject.mediaType === 'video') {
69+
signalRenderResult(false, {
70+
reason: 'preventWritingOnMainDocument',
71+
message: `Cannot render video ad ${adId}`
72+
});
73+
console.log('Error trying to write ad.');
74+
} else if (ad) {
75+
const iframe = domHelper.getEmptyIframe(adObject.height, adObject.width);
76+
body.appendChild(iframe);
77+
iframe.contentDocument.open();
78+
iframe.contentDocument.write(ad);
79+
iframe.contentDocument.close();
80+
signalRenderResult(true);
81+
} else if (url) {
82+
const iframe = domHelper.getEmptyIframe(height, width);
83+
iframe.style.display = 'inline';
84+
iframe.style.overflow = 'hidden';
85+
iframe.src = url;
6786

68-
if (adObject.mediaType === 'video') {
69-
console.log('Error trying to write ad.');
70-
} else if (ad) {
71-
const iframe = domHelper.getEmptyIframe(adObject.height, adObject.width);
72-
body.appendChild(iframe);
73-
iframe.contentDocument.open();
74-
iframe.contentDocument.write(ad);
75-
iframe.contentDocument.close();
76-
} else if (url) {
77-
const iframe = domHelper.getEmptyIframe(height, width);
78-
iframe.style.display = 'inline';
79-
iframe.style.overflow = 'hidden';
80-
iframe.src = url;
87+
domHelper.insertElement(iframe, document, 'body');
88+
signalRenderResult(true);
89+
} else {
90+
signalRenderResult(false, {
91+
reason: 'noAd',
92+
message: `No ad for ${adId}`
93+
});
94+
console.log(`Error trying to write ad. No ad markup or adUrl for ${adId}`);
95+
}
96+
} catch (e) {
97+
signalRenderResult(false, { reason: "exception", message: e.message });
98+
console.log(`Error in rendering ad`, e);
99+
}
100+
}
81101

82-
domHelper.insertElement(iframe, document, 'body');
83-
} else {
84-
console.log(`Error trying to write ad. No ad for bid response id: ${id}`);
102+
function signalRenderResult(success, { reason, message } = {}) {
103+
const payload = {
104+
message: 'Prebid Event',
105+
adId,
106+
event: success ? 'adRenderSucceeded' : 'adRenderFailed',
107+
};
108+
if (!success) {
109+
payload.info = { reason, message };
85110
}
111+
ev.source.postMessage(JSON.stringify(payload), publisherDomain);
86112
}
87113
}
88114

test/spec/renderingManager_spec.js

Lines changed: 126 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import * as domHelper from 'src/domHelper';
44
import { expect } from 'chai';
55
import { mocks } from 'test/helpers/mocks';
66
import { merge } from 'lodash';
7-
import * as postscribe from "postscribe";
87

98
const renderingMocks = {
109
messages: [],
@@ -41,11 +40,14 @@ const renderingMocks = {
4140
}
4241
}
4342

44-
let mockIframe = {
45-
contentDocument: {
46-
open: sinon.spy(),
47-
write: sinon.spy(),
48-
close: sinon.spy()
43+
function createMockIframe() {
44+
return {
45+
contentDocument: {
46+
open: sinon.spy(),
47+
write: sinon.spy(),
48+
close: sinon.spy()
49+
},
50+
style: {},
4951
}
5052
}
5153

@@ -67,52 +69,143 @@ describe('renderingManager', function() {
6769
});
6870

6971
describe('cross domain creative', function() {
72+
const ORIGIN = 'http://example.com';
7073
let parseStub;
7174
let iframeStub;
7275
let triggerPixelSpy;
76+
let mockWin;
77+
let ucTagData;
78+
let mockIframe;
79+
let eventSource;
80+
7381
beforeEach(function(){
82+
mockIframe = createMockIframe();
7483
parseStub = sinon.stub(utils, 'parseUrl');
75-
iframeStub = sinon.stub(domHelper, 'getEmptyIframe');
84+
iframeStub = sinon.stub(domHelper, 'getEmptyIframe').returns(mockIframe);
7685
triggerPixelSpy = sinon.stub(utils, 'triggerPixel');
77-
});
78-
79-
after(function() {
80-
parseStub.restore();
81-
iframeStub.restore();
82-
triggerPixelSpy.restore();
83-
});
84-
85-
it('should render cross domain creative', function() {
8686
parseStub.returns({
8787
protocol: 'http',
8888
host: 'example.com'
8989
});
90-
iframeStub.returns(mockIframe);
91-
92-
const mockWin = merge(mocks.createFakeWindow('http://example.com'), renderingMocks.getWindowObject());
93-
let ucTagData = {
90+
mockWin = merge(mocks.createFakeWindow(ORIGIN), renderingMocks.getWindowObject());
91+
ucTagData = {
9492
adId: '123',
9593
adServerDomain: 'mypub.com',
96-
pubUrl: 'http://example.com'
94+
pubUrl: ORIGIN,
9795
};
96+
eventSource = null;
97+
9898
renderCrossDomain(mockWin, ucTagData.adId, ucTagData.adServerDomain, ucTagData.pubUrl);
9999

100-
// dummy implementation of postmessage from prebid.js
101-
let ev = {
102-
origin: 'http://example.com',
103-
message: JSON.stringify({
104-
message: 'Prebid Response',
105-
ad: 'ad',
106-
adUrl: 'http://example.com',
107-
adId: '123',
108-
width: 300,
109-
height: 250
110-
})
100+
});
101+
102+
afterEach(function () {
103+
parseStub.restore();
104+
iframeStub.restore();
105+
triggerPixelSpy.restore();
106+
});
107+
108+
function mockPrebidResponse(msg) {
109+
eventSource = {
110+
postMessage: sinon.spy()
111111
};
112+
mockWin.postMessage({
113+
source: eventSource,
114+
origin: ORIGIN,
115+
message: JSON.stringify(Object.assign({ message: 'Prebid Response' }, msg))
116+
});
117+
}
112118

113-
mockWin.postMessage(ev);
119+
it('should render cross domain creative', function() {
120+
mockPrebidResponse({
121+
ad: 'ad',
122+
adUrl: ORIGIN,
123+
adId: '123',
124+
width: 300,
125+
height: 250
126+
});
114127
expect(mockIframe.contentDocument.write.args[0][0]).to.equal("ad");
115128
});
129+
130+
describe('should signal event', () => {
131+
const RENDER_FAILED = 'adRenderFailed',
132+
RENDER_SUCCESS = 'adRenderSucceeded';
133+
134+
function expectEventMessage(expected) {
135+
const actual = JSON.parse(eventSource.postMessage.args[0][0]);
136+
sinon.assert.match(actual, Object.assign({ message: 'Prebid Event' }, expected));
137+
}
138+
139+
describe('AD_RENDER_FAILED', () => {
140+
it('on video ads', () => {
141+
mockPrebidResponse({
142+
ad: 'ad',
143+
adId: '123',
144+
mediaType: 'video'
145+
});
146+
expectEventMessage({
147+
adId: '123',
148+
event: RENDER_FAILED,
149+
info: {
150+
reason: 'preventWritingOnMainDocument'
151+
}
152+
});
153+
});
154+
155+
it('on ads that have no markup or adUrl', () => {
156+
mockPrebidResponse({
157+
adId: '123',
158+
});
159+
expectEventMessage({
160+
adId: '123',
161+
event: RENDER_FAILED,
162+
info: {
163+
reason: 'noAd'
164+
}
165+
});
166+
});
167+
168+
it('on exceptions', () => {
169+
iframeStub.callsFake(() => {
170+
throw new Error();
171+
});
172+
mockPrebidResponse({
173+
adId: '123',
174+
ad: 'ad',
175+
adUrl: ORIGIN,
176+
});
177+
expectEventMessage({
178+
adId: '123',
179+
event: RENDER_FAILED,
180+
info: {
181+
reason: 'exception'
182+
}
183+
});
184+
});
185+
});
186+
describe('should post AD_RENDER_SUCCEEDED', () => {
187+
it('on ad with markup', () => {
188+
mockPrebidResponse({
189+
adId: '123',
190+
ad: 'markup'
191+
});
192+
expectEventMessage({
193+
adId: '123',
194+
event: RENDER_SUCCESS
195+
});
196+
});
197+
it('on ad with adUrl', () => {
198+
mockPrebidResponse({
199+
adId: '123',
200+
adUrl: 'url'
201+
});
202+
expectEventMessage({
203+
adId: '123',
204+
event: RENDER_SUCCESS
205+
});
206+
})
207+
})
208+
});
116209
});
117210

118211
describe('legacy creative', function() {

0 commit comments

Comments
 (0)