Skip to content

Commit

Permalink
Signal AD_RENDER_FAILED / AD_RENDER_SUCCEEDED events to Prebid (#152)
Browse files Browse the repository at this point in the history
Add a new type of cross-origin message ('Prebid Message') to signal when render-related events should be generated by Prebid.
  • Loading branch information
dgirardi authored Jan 21, 2022
1 parent 320a6d1 commit 23d1f09
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 61 deletions.
79 changes: 53 additions & 26 deletions src/renderingManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,35 +87,62 @@ export function newRenderingManager(win, environment) {
let origin = ev.origin || ev.originalEvent.origin;
if (adObject.message && adObject.message === 'Prebid Response' &&
publisherDomain === origin &&
adObject.adId === adId &&
(adObject.ad || adObject.adUrl)) {
let body = win.document.body;
let ad = adObject.ad;
let url = adObject.adUrl;
let width = adObject.width;
let height = adObject.height;

if (adObject.mediaType === 'video') {
console.log('Error trying to write ad.');
} else if (ad) {
const iframe = domHelper.getEmptyIframe(adObject.height, adObject.width);
body.appendChild(iframe);
iframe.contentDocument.open();
iframe.contentDocument.write(ad);
iframe.contentDocument.close();
} else if (url) {
const iframe = domHelper.getEmptyIframe(height, width);
iframe.style.display = 'inline';
iframe.style.overflow = 'hidden';
iframe.src = url;

domHelper.insertElement(iframe, document, 'body');
} else {
console.log(`Error trying to write ad. No ad for bid response id: ${id}`);
adObject.adId === adId) {
try {
let body = win.document.body;
let ad = adObject.ad;
let url = adObject.adUrl;
let width = adObject.width;
let height = adObject.height;

if (adObject.mediaType === 'video') {
signalRenderResult(false, {
reason: 'preventWritingOnMainDocument',
message: `Cannot render video ad ${adId}`
});
console.log('Error trying to write ad.');
} else if (ad) {
const iframe = domHelper.getEmptyIframe(adObject.height, adObject.width);
body.appendChild(iframe);
iframe.contentDocument.open();
iframe.contentDocument.write(ad);
iframe.contentDocument.close();
signalRenderResult(true);
} else if (url) {
const iframe = domHelper.getEmptyIframe(height, width);
iframe.style.display = 'inline';
iframe.style.overflow = 'hidden';
iframe.src = url;

domHelper.insertElement(iframe, document, 'body');
signalRenderResult(true);
} else {
signalRenderResult(false, {
reason: 'noAd',
message: `No ad for ${adId}`
});
console.log(`Error trying to write ad. No ad markup or adUrl for ${adId}`);
}
} catch (e) {
signalRenderResult(false, {reason: "exception", message: e.message});
console.log(`Error in rendering ad`, e);
}
}

function signalRenderResult(success, {reason, message} = {}) {
const payload = {
message: 'Prebid Event',
adId,
event: success ? 'adRenderSucceeded' : 'adRenderFailed',
}
if (!success) {
payload.info = {reason, message};
}
ev.source.postMessage(JSON.stringify(payload), publisherDomain);
}
}


function requestAdFromPrebid() {
let message = JSON.stringify({
message: 'Prebid Request',
Expand Down Expand Up @@ -144,7 +171,7 @@ export function newRenderingManager(win, environment) {

return `https://${host}${path}`;
}

/**
* update iframe by using size string to resize
* @param {string} size
Expand Down
164 changes: 129 additions & 35 deletions test/spec/renderingManager_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import * as domHelper from 'src/domHelper';
import { expect } from 'chai';
import { mocks } from 'test/helpers/mocks';
import { merge } from 'lodash';
import * as postscribe from "postscribe";

const renderingMocks = {
messages: [],
Expand Down Expand Up @@ -41,11 +40,14 @@ const renderingMocks = {
}
}

let mockIframe = {
contentDocument: {
open: sinon.spy(),
write: sinon.spy(),
close: sinon.spy()
function createMockIframe() {
return {
contentDocument: {
open: sinon.spy(),
write: sinon.spy(),
close: sinon.spy()
},
style: {},
}
}

Expand Down Expand Up @@ -304,59 +306,151 @@ describe('renderingManager', function() {
});

describe('cross domain creative', function() {
const ORIGIN = 'http://example.com';
let parseStub;
let iframeStub;
let triggerPixelSpy;
let mockWin;
let env;
let renderObject;
let ucTagData;
let mockIframe;
let eventSource;

beforeEach(function(){
mockIframe = createMockIframe();
parseStub = sinon.stub(utils, 'parseUrl');
iframeStub = sinon.stub(domHelper, 'getEmptyIframe');
iframeStub = sinon.stub(domHelper, 'getEmptyIframe').returns(mockIframe);
triggerPixelSpy = sinon.stub(utils, 'triggerPixel');
});

after(function() {
parseStub.restore();
iframeStub.restore();
triggerPixelSpy.restore();
});

it('should render cross domain creative', function() {
parseStub.returns({
protocol: 'http',
host: 'example.com'
});
iframeStub.returns(mockIframe);

const mockWin = merge(mocks.createFakeWindow('http://example.com'), renderingMocks.getWindowObject());
const env = {
mockWin = merge(mocks.createFakeWindow(ORIGIN), renderingMocks.getWindowObject());
env = {
isMobileApp: () => false,
isAmp: () => false,
canLocatePrebid: () => false
};
const renderObject = newRenderingManager(mockWin, env);
let ucTagData = {
renderObject = newRenderingManager(mockWin, env);
ucTagData = {
adId: '123',
adServerDomain: 'mypub.com',
pubUrl: 'http://example.com'
pubUrl: ORIGIN,
};
eventSource = null;

renderObject.renderAd(mockWin.document, ucTagData);

// dummy implementation of postmessage from prebid.js
let ev = {
origin: 'http://example.com',
message: JSON.stringify({
message: 'Prebid Response',
ad: 'ad',
adUrl: 'http://example.com',
adId: '123',
width: 300,
height: 250
})
});

afterEach(function() {
parseStub.restore();
iframeStub.restore();
triggerPixelSpy.restore();
});

function mockPrebidResponse(msg) {
eventSource = {
postMessage: sinon.spy()
};
mockWin.postMessage({
source: eventSource,
origin: ORIGIN,
message: JSON.stringify(Object.assign({message: 'Prebid Response'}, msg))
});
}

mockWin.postMessage(ev);
it('should render cross domain creative', function() {
mockPrebidResponse({
ad: 'ad',
adUrl: ORIGIN,
adId: '123',
width: 300,
height: 250
});
expect(mockIframe.contentDocument.write.args[0][0]).to.equal("ad");
});

describe('should signal event', () => {
const RENDER_FAILED = 'adRenderFailed',
RENDER_SUCCESS = 'adRenderSucceeded';

function expectEventMessage(expected) {
const actual = JSON.parse(eventSource.postMessage.args[0][0]);
sinon.assert.match(actual, Object.assign({message: 'Prebid Event'}, expected));
}

describe('AD_RENDER_FAILED', () => {
it('on video ads', () => {
mockPrebidResponse({
ad: 'ad',
adId: '123',
mediaType: 'video'
});
expectEventMessage({
adId: '123',
event: RENDER_FAILED,
info: {
reason: 'preventWritingOnMainDocument'
}
})
});

it('on ads that have no markup or adUrl', () => {
mockPrebidResponse({
adId: '123',
})
expectEventMessage({
adId: '123',
event: RENDER_FAILED,
info: {
reason: 'noAd'
}
});
});

it('on exceptions', () => {
iframeStub.callsFake(() => {
throw new Error()
});
mockPrebidResponse({
adId: '123',
ad: 'ad',
adUrl: ORIGIN,
});
expectEventMessage({
adId: '123',
event: RENDER_FAILED,
info: {
reason: 'exception'
}
});
})
});
describe('should post AD_RENDER_SUCCEEDED', () => {
it('on ad with markup', () => {
mockPrebidResponse({
adId: '123',
ad: 'markup'
});
expectEventMessage({
adId: '123',
event: RENDER_SUCCESS
});
});
it('on ad with adUrl', () => {
mockPrebidResponse({
adId: '123',
adUrl: 'url'
});
expectEventMessage({
adId: '123',
event: RENDER_SUCCESS
});
})
})
});
});

describe('legacy creative', function() {
Expand Down

0 comments on commit 23d1f09

Please sign in to comment.