prebid-universal-creative 1.17.1 → 1.18.0
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 +1 -1
- package/README.md +74 -7
- package/browsers.json +12 -12
- package/dist/amp.js +3 -3
- package/dist/banner.js +3 -3
- package/dist/creative.js +3 -3
- package/dist/load-cookie-with-consent.html +1 -1
- package/dist/load-cookie.html +1 -1
- package/dist/main.max.js +3172 -0
- package/dist/mobile.js +3 -3
- package/dist/native-render.js +3 -3
- package/dist/native-trk.js +3 -3
- package/dist/native.js +3 -3
- package/dist/uid.js +3 -3
- package/dist/video.js +3 -3
- package/gulpfile.js +35 -11
- package/karma.conf.maker.js +22 -10
- package/package.json +15 -15
- package/src/cookieSync.js +2 -0
- package/src/cookieSyncWithConsent.js +2 -0
- package/src/mobileAndAmpRender.js +4 -3
- package/src/nativeAssetManager.js +1 -1
- package/src/nativeORTBTrackerManager.js +0 -2
- package/src/nativeTrackerManager.js +1 -1
- package/src/postscribeRender.js +71 -0
- package/src/renderingManager.js +1 -1
- package/test/spec/mobileAndAmpRender_spec.js +89 -96
- package/test/spec/nativeAssetManager_spec.js +20 -8
- package/test/spec/nativeORTBTrackerManager_spec.js +2 -2
- package/test/spec/nativeTrackerManager_spec.js +3 -8
- package/test/spec/renderingManager_spec.js +4 -24
- package/test/spec/uid_spec.js +10 -29
- package/test/test_index.js +1 -1
- package/webpack.conf.js +12 -15
- package/dist/0df2612ebfffd5ad651a.max.js +0 -1050
- package/src/adHtmlRender.js +0 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prebid-universal-creative",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "Universal creative for Prebid apps",
|
|
5
5
|
"main": "dist/creative.js",
|
|
6
6
|
"scripts": {
|
|
@@ -28,49 +28,49 @@
|
|
|
28
28
|
"@babel/register": "^7.6.2",
|
|
29
29
|
"@playwright/test": "^1.50.1",
|
|
30
30
|
"babel-loader": "^8.0.5",
|
|
31
|
+
"babel-plugin-istanbul": "^8.0.0",
|
|
31
32
|
"babel-plugin-transform-es3-member-expression-literals": "^6.22.0",
|
|
32
33
|
"babel-plugin-transform-es3-property-literals": "^6.22.0",
|
|
33
34
|
"chai": "^4.1.2",
|
|
34
|
-
"core-js": "^3.
|
|
35
|
-
"core-js-pure": "^3.
|
|
35
|
+
"core-js": "^3.49.0",
|
|
36
|
+
"core-js-pure": "^3.49.0",
|
|
36
37
|
"del": "^5.0.0",
|
|
37
38
|
"execa": "^1.0.0",
|
|
38
39
|
"gulp": "^5.0.0",
|
|
39
40
|
"gulp-clean": "^0.4.0",
|
|
40
41
|
"gulp-connect": "^5.7.0",
|
|
41
|
-
"gulp-eslint": "^
|
|
42
|
+
"gulp-eslint": "^6.0.0",
|
|
42
43
|
"gulp-header": "^2.0.1",
|
|
43
44
|
"gulp-inject": "^5.0.2",
|
|
44
45
|
"gulp-rename": "^1.2.2",
|
|
45
46
|
"gulp-replace": "^0.6.1",
|
|
46
47
|
"gulp-uglify": "^3.0.0",
|
|
47
48
|
"istanbul": "^0.4.5",
|
|
48
|
-
"istanbul-instrumenter-loader": "^3.0.0",
|
|
49
49
|
"karma": "^6.4.0",
|
|
50
50
|
"karma-browserstack-launcher": "^1.3.0",
|
|
51
51
|
"karma-chai": "^0.1.0",
|
|
52
52
|
"karma-chrome-launcher": "^2.2.0",
|
|
53
|
-
"karma-coverage": "^
|
|
54
|
-
"karma-coverage-istanbul-reporter": "^
|
|
53
|
+
"karma-coverage": "^2.2.1",
|
|
54
|
+
"karma-coverage-istanbul-reporter": "^3.0.3",
|
|
55
55
|
"karma-mocha": "^2.0.1",
|
|
56
56
|
"karma-mocha-reporter": "^2.2.5",
|
|
57
57
|
"karma-sinon": "^1.0.5",
|
|
58
58
|
"karma-sourcemap-loader": "^0.3.7",
|
|
59
59
|
"karma-spec-reporter": "^0.0.31",
|
|
60
|
-
"karma-webpack": "^
|
|
60
|
+
"karma-webpack": "^5.0.1",
|
|
61
61
|
"lodash": "^4.17.14",
|
|
62
|
-
"mocha": "^
|
|
62
|
+
"mocha": "^11.7.6",
|
|
63
63
|
"opn": "^6.0.0",
|
|
64
|
-
"sinon": "^
|
|
65
|
-
"string-replace-
|
|
64
|
+
"sinon": "^22.0.0",
|
|
65
|
+
"string-replace-loader": "^3.3.0",
|
|
66
66
|
"webdriverio": "^9.10.0",
|
|
67
|
-
"webpack": "^
|
|
68
|
-
"webpack-
|
|
69
|
-
"webpack-stream": "^4.0.0",
|
|
67
|
+
"webpack": "^5.107.2",
|
|
68
|
+
"webpack-stream": "^7.0.0",
|
|
70
69
|
"yargs": "^11.0.0"
|
|
71
70
|
},
|
|
72
71
|
"dependencies": {
|
|
73
72
|
"babel-plugin-transform-object-assign": "^6.22.0",
|
|
74
|
-
"gulp-cli": "^3.0.0"
|
|
73
|
+
"gulp-cli": "^3.0.0",
|
|
74
|
+
"postscribe": "^2.0.8"
|
|
75
75
|
}
|
|
76
76
|
}
|
package/src/cookieSync.js
CHANGED
|
@@ -24,6 +24,7 @@ const coopSyncParam = parseQueryParam('coop_sync', window.location.search);
|
|
|
24
24
|
const COOP_SYNC = !coopSyncParam || coopSyncParam === 'true' || !!parseInt(coopSyncParam);
|
|
25
25
|
const GDPR = sanitizeGdpr(parseInt(parseQueryParam('gdpr', window.location.search), 10));
|
|
26
26
|
const GDPR_CONSENT = sanitizeGdprConsent(parseQueryParam('gdpr_consent', window.location.search));
|
|
27
|
+
const ACCOUNT = parseQueryParam('account', window.location.search);
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* checks to make sure URL is valid. Regex from https://validatejs.org/#validators-url, https://gist.github.com/dperini/729294
|
|
@@ -267,6 +268,7 @@ function getStringifiedData(endPointArgs) {
|
|
|
267
268
|
}
|
|
268
269
|
};
|
|
269
270
|
if(BIDDER_ARGS) data.bidders = BIDDER_ARGS;
|
|
271
|
+
if(ACCOUNT) data.account = ACCOUNT;
|
|
270
272
|
|
|
271
273
|
return JSON.stringify(data);
|
|
272
274
|
}
|
|
@@ -24,6 +24,7 @@ const coopSyncParam = parseQueryParam('coop_sync', window.location.search);
|
|
|
24
24
|
const COOP_SYNC = !coopSyncParam || coopSyncParam === 'true' || !!parseInt(coopSyncParam);
|
|
25
25
|
const TIMEOUT = sanitizeTimeout(parseInt(parseQueryParam('timeout', window.location.search), 10));
|
|
26
26
|
const DEFAULT_GDPR_SCOPE = sanitizeScope(parseInt(parseQueryParam('defaultGdprScope', window.location.search), 10));
|
|
27
|
+
const ACCOUNT = parseQueryParam('account', window.location.search);
|
|
27
28
|
|
|
28
29
|
let consent = {};
|
|
29
30
|
let syncRan = false;
|
|
@@ -284,6 +285,7 @@ function getStringifiedData(endPointArgs) {
|
|
|
284
285
|
}
|
|
285
286
|
};
|
|
286
287
|
if(BIDDER_ARGS) data.bidders = BIDDER_ARGS;
|
|
288
|
+
if(ACCOUNT) data.account = ACCOUNT;
|
|
287
289
|
|
|
288
290
|
data = attachConsent(data);
|
|
289
291
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getCreativeCommentMarkup, triggerPixel, createTrackPixelHtml, loadScript, getCreativeComment, writeAdUrl, transformAuctionTargetingData, sendRequest, getUUID } from './utils';
|
|
2
2
|
import { isSafeFrame, isMobileApp } from './environment';
|
|
3
3
|
import { insertElement } from './domHelper';
|
|
4
|
-
import { writeAdHtml } from './
|
|
4
|
+
import { writeAdHtml } from './postscribeRender';
|
|
5
5
|
|
|
6
6
|
const DEFAULT_CACHE_HOST = 'prebid.adnxs.com';
|
|
7
7
|
const DEFAULT_CACHE_PATH = '/pbc/v1/cache';
|
|
@@ -66,7 +66,7 @@ function resizeIframe(width, height) {
|
|
|
66
66
|
const iframeHeight = window.innerHeight;
|
|
67
67
|
if (iframeWidth !== width || iframeHeight !== height) {
|
|
68
68
|
if (isSafeFrame(window)) {
|
|
69
|
-
function resize(
|
|
69
|
+
function resize() {
|
|
70
70
|
let newWidth = width - iframeWidth;
|
|
71
71
|
let newHeight = height - iframeHeight;
|
|
72
72
|
window.$sf.ext.expand({r: newWidth, b: newHeight, push: true});
|
|
@@ -131,7 +131,7 @@ function responseCallback(isMobileApp, hbPb) {
|
|
|
131
131
|
if (bidObject.burl) {
|
|
132
132
|
let triggerBurl = function () { triggerPixel(bidObject.burl); };
|
|
133
133
|
if (isMobileApp) {
|
|
134
|
-
|
|
134
|
+
loadScript(window, 'mraid.js',
|
|
135
135
|
function () { // Success loading MRAID
|
|
136
136
|
let result = registerMRAIDViewableEvent(triggerBurl);
|
|
137
137
|
if (!result) {
|
|
@@ -149,6 +149,7 @@ function responseCallback(isMobileApp, hbPb) {
|
|
|
149
149
|
if (isMobileApp) {
|
|
150
150
|
let adhtml = loadScript(window, bidObject.nurl);
|
|
151
151
|
ad += constructMarkup(adhtml.outerHTML, width, height);
|
|
152
|
+
|
|
152
153
|
writeAdHtml(ad);
|
|
153
154
|
} else {
|
|
154
155
|
let nurl = bidObject.nurl;
|
|
@@ -430,7 +430,7 @@ export function newNativeAssetManager(win, nativeTag, mkMessenger = prebidMessen
|
|
|
430
430
|
stopListening();
|
|
431
431
|
const resize = () => requestHeightResize(
|
|
432
432
|
bid.adId,
|
|
433
|
-
(document.body.clientHeight || document.body.offsetHeight),
|
|
433
|
+
(document.body.clientHeight || document.body.offsetHeight || document.documentElement.scrollHeight),
|
|
434
434
|
document.body.clientWidth > 1 ? document.body.clientWidth : undefined
|
|
435
435
|
);
|
|
436
436
|
document.readyState === 'complete' ? resize() : window.onload = resize;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Script to handle firing impression and click trackers from native teamplates
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { triggerPixel, transformAuctionTargetingData } from './utils';
|
|
5
5
|
import { newNativeAssetManager } from './nativeAssetManager';
|
|
6
6
|
import {prebidMessenger} from './messaging.js';
|
|
7
7
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import postscribe from 'postscribe';
|
|
2
|
+
|
|
3
|
+
export function writeAdHtml(markup, ps = postscribe) {
|
|
4
|
+
// remove <?xml> and <!doctype> tags
|
|
5
|
+
// https://github.com/prebid/prebid-universal-creative/issues/134
|
|
6
|
+
markup = markup
|
|
7
|
+
.replace(/<\?xml\b[^>]*(?:\?>|>)/gi, '')
|
|
8
|
+
.replace(/<!doctype\b[^>\[]*(?:\[[^\]]*\][^>]*)?>/gi, '');
|
|
9
|
+
|
|
10
|
+
let finalMarkup;
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
finalMarkup = normalizeMarkup(markup);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error("Error normalizing markup:", error.message);
|
|
16
|
+
finalMarkup = markup;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
ps(document.body, finalMarkup, {
|
|
20
|
+
error: console.error
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Normalizes an HTML string by parsing and re-serializing it,
|
|
26
|
+
* returning the content between custom PUC_START and PUC_END markers.
|
|
27
|
+
*
|
|
28
|
+
* This function is specifically aimed at addressing an issue with `postscribe` where double quotes inside single-quoted
|
|
29
|
+
* HTML attributes are not correctly escaped.
|
|
30
|
+
*/
|
|
31
|
+
function normalizeMarkup(markup) {
|
|
32
|
+
const timestamp = Date.now();
|
|
33
|
+
const startMarkerId = `PUC_START_${timestamp}`;
|
|
34
|
+
const endMarkerId = `PUC_END_${timestamp}`;
|
|
35
|
+
const startMarker = `<div id="${startMarkerId}"></div>`;
|
|
36
|
+
const endMarker = `<div id="${endMarkerId}"></div>`;
|
|
37
|
+
const doc = new DOMParser().parseFromString(`${startMarker}${markup}${endMarker}`, "text/html");
|
|
38
|
+
|
|
39
|
+
const textMap = new Map();
|
|
40
|
+
let textId = 0;
|
|
41
|
+
|
|
42
|
+
const replaceTextNodes = (node) => {
|
|
43
|
+
if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) {
|
|
44
|
+
const id = `PUC_NODE_TEXT_${textId++}_${timestamp}`;
|
|
45
|
+
textMap.set(id, node.textContent);
|
|
46
|
+
const span = doc.createElement("span");
|
|
47
|
+
span.dataset.textId = id;
|
|
48
|
+
node.parentNode.replaceChild(span, node);
|
|
49
|
+
} else {
|
|
50
|
+
[...node.childNodes].forEach(replaceTextNodes);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let current = doc.querySelector(`#${startMarkerId}`).nextSibling;
|
|
55
|
+
const end = doc.querySelector(`#${endMarkerId}`);
|
|
56
|
+
while (current && current !== end) {
|
|
57
|
+
replaceTextNodes(current);
|
|
58
|
+
current = current.nextSibling;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const serialized = new XMLSerializer().serializeToString(doc);
|
|
62
|
+
const snippet = serialized
|
|
63
|
+
.split(startMarker)[1]
|
|
64
|
+
.split(endMarker)[0]
|
|
65
|
+
.replace(
|
|
66
|
+
/<span data-text-id="(PUC_NODE_TEXT_\d+_\d+)"[^>]*><\/span>/g,
|
|
67
|
+
(_, id) => textMap.get(id) || ""
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return snippet.trim();
|
|
71
|
+
}
|
package/src/renderingManager.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { transformAuctionTargetingData } from './utils';
|
|
2
2
|
import { canLocatePrebid } from './environment';
|
|
3
3
|
import { insertElement, getEmptyIframe } from './domHelper';
|
|
4
4
|
import {prebidMessenger, renderEventMessage} from './messaging.js';
|
|
@@ -1,92 +1,29 @@
|
|
|
1
1
|
import { renderAmpOrMobileAd } from 'src/mobileAndAmpRender';
|
|
2
|
-
import * as postscribeRender from 'src/
|
|
2
|
+
import * as postscribeRender from 'src/postscribeRender'
|
|
3
3
|
import * as utils from 'src/utils';
|
|
4
4
|
import { expect } from 'chai';
|
|
5
|
-
import {
|
|
6
|
-
import { merge } from 'lodash';
|
|
7
|
-
import {writeAdHtml} from 'src/adHtmlRender';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
function renderingMocks() {
|
|
11
|
-
return {
|
|
12
|
-
messages: [],
|
|
13
|
-
getWindowObject: function () {
|
|
14
|
-
return {
|
|
15
|
-
document: {
|
|
16
|
-
body: {
|
|
17
|
-
appendChild: sinon.spy(),
|
|
18
|
-
},
|
|
19
|
-
createComment: () => true,
|
|
20
|
-
},
|
|
21
|
-
parent: {
|
|
22
|
-
postMessage: sinon.spy(),
|
|
23
|
-
$$PREBID_GLOBAL$$: {
|
|
24
|
-
renderAd: sinon.spy(),
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
postMessage: (message, domain) => {
|
|
28
|
-
this.messages[0](message);
|
|
29
|
-
},
|
|
30
|
-
top: null,
|
|
31
|
-
$sf: {
|
|
32
|
-
ext: {
|
|
33
|
-
register: sinon.spy(),
|
|
34
|
-
expand: sinon.spy(),
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
addEventListener: (type, listener, capture) => {
|
|
38
|
-
this.messages.push(listener);
|
|
39
|
-
},
|
|
40
|
-
innerWidth: 300,
|
|
41
|
-
innerHeight: 250,
|
|
42
|
-
};
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
describe("renderingManager", function () {
|
|
48
|
-
let xhr;
|
|
49
|
-
let requests;
|
|
50
|
-
|
|
51
|
-
before(function () {
|
|
52
|
-
xhr = sinon.useFakeXMLHttpRequest();
|
|
53
|
-
xhr.onCreate = (request) => requests.push(request);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
beforeEach(function () {
|
|
57
|
-
requests = [];
|
|
58
|
-
});
|
|
5
|
+
import { writeAdHtml } from 'src/postscribeRender';
|
|
59
6
|
|
|
60
|
-
after(function () {
|
|
61
|
-
xhr.restore();
|
|
62
|
-
});
|
|
63
7
|
|
|
8
|
+
describe("renderingManager", function () {
|
|
64
9
|
describe("mobile creative", function () {
|
|
10
|
+
let sandbox;
|
|
65
11
|
let writeHtmlSpy;
|
|
66
|
-
let
|
|
67
|
-
let
|
|
68
|
-
let mockWin;
|
|
69
|
-
|
|
70
|
-
before(function () {
|
|
71
|
-
writeHtmlSpy = sinon.spy(postscribeRender, "writeAdHtml");
|
|
72
|
-
sendRequestSpy = sinon.spy(utils, "sendRequest");
|
|
73
|
-
triggerPixelSpy = sinon.spy(utils, "triggerPixel");
|
|
74
|
-
mockWin = merge(
|
|
75
|
-
mocks.createFakeWindow("http://example.com"),
|
|
76
|
-
renderingMocks().getWindowObject()
|
|
77
|
-
);
|
|
78
|
-
});
|
|
12
|
+
let sendRequestStub;
|
|
13
|
+
let requestCallbacks;
|
|
79
14
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
15
|
+
beforeEach(function () {
|
|
16
|
+
sandbox = sinon.createSandbox();
|
|
17
|
+
requestCallbacks = [];
|
|
18
|
+
writeHtmlSpy = sandbox.spy(postscribeRender, "writeAdHtml");
|
|
19
|
+
sendRequestStub = sandbox.stub(utils, "sendRequest").callsFake((url, callback) => {
|
|
20
|
+
requestCallbacks.push(callback);
|
|
21
|
+
});
|
|
22
|
+
sandbox.spy(utils, "triggerPixel");
|
|
84
23
|
});
|
|
85
24
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
sendRequestSpy.restore();
|
|
89
|
-
triggerPixelSpy.restore();
|
|
25
|
+
afterEach(function () {
|
|
26
|
+
sandbox.restore();
|
|
90
27
|
});
|
|
91
28
|
|
|
92
29
|
it("should render mobile app creative", function () {
|
|
@@ -106,9 +43,9 @@ describe("renderingManager", function () {
|
|
|
106
43
|
adm: "ad-markup",
|
|
107
44
|
wurl: "https://test.prebidcache.wurl",
|
|
108
45
|
};
|
|
109
|
-
|
|
46
|
+
requestCallbacks[0](JSON.stringify(response));
|
|
110
47
|
expect(writeHtmlSpy.callCount).to.equal(1);
|
|
111
|
-
expect(
|
|
48
|
+
expect(sendRequestStub.args[0][0]).to.equal(
|
|
112
49
|
"https://example.com/path?uuid=123"
|
|
113
50
|
);
|
|
114
51
|
});
|
|
@@ -129,9 +66,9 @@ describe("renderingManager", function () {
|
|
|
129
66
|
crid: 123,
|
|
130
67
|
adm: "ad-markup",
|
|
131
68
|
};
|
|
132
|
-
|
|
69
|
+
requestCallbacks[0](JSON.stringify(response));
|
|
133
70
|
expect(writeHtmlSpy.callCount).to.equal(1);
|
|
134
|
-
expect(
|
|
71
|
+
expect(sendRequestStub.args[0][0]).to.equal(
|
|
135
72
|
"https://example.com/path?uuid=123"
|
|
136
73
|
);
|
|
137
74
|
});
|
|
@@ -149,9 +86,9 @@ describe("renderingManager", function () {
|
|
|
149
86
|
crid: 123,
|
|
150
87
|
adm: "ad-markup",
|
|
151
88
|
};
|
|
152
|
-
|
|
89
|
+
requestCallbacks[0](JSON.stringify(response));
|
|
153
90
|
expect(writeHtmlSpy.callCount).to.equal(1);
|
|
154
|
-
expect(
|
|
91
|
+
expect(sendRequestStub.args[0][0]).to.equal(
|
|
155
92
|
"https://prebid.adnxs.com/pbc/v1/cache?uuid=123"
|
|
156
93
|
);
|
|
157
94
|
});
|
|
@@ -178,7 +115,7 @@ describe("renderingManager", function () {
|
|
|
178
115
|
// crid: 123,
|
|
179
116
|
// adm: '<script src="notExistingScript.js"></script>'
|
|
180
117
|
// };
|
|
181
|
-
//
|
|
118
|
+
// requestCallbacks[0](JSON.stringify(response));
|
|
182
119
|
|
|
183
120
|
// setTimeout(() => {
|
|
184
121
|
// expect(consoleErrorSpy.callCount).to.equal(1);
|
|
@@ -192,14 +129,17 @@ describe("renderingManager", function () {
|
|
|
192
129
|
let writeHtmlSpy;
|
|
193
130
|
let sendRequestSpy;
|
|
194
131
|
let triggerPixelSpy;
|
|
195
|
-
let mockWin;
|
|
196
132
|
let ucTagData;
|
|
197
133
|
let response;
|
|
134
|
+
let requestCallbacks;
|
|
198
135
|
|
|
199
136
|
beforeEach(function () {
|
|
200
|
-
sandbox = sinon.
|
|
137
|
+
sandbox = sinon.createSandbox();
|
|
138
|
+
requestCallbacks = [];
|
|
201
139
|
writeHtmlSpy = sandbox.spy(postscribeRender, "writeAdHtml");
|
|
202
|
-
sendRequestSpy = sandbox.
|
|
140
|
+
sendRequestSpy = sandbox.stub(utils, "sendRequest").callsFake((url, callback) => {
|
|
141
|
+
requestCallbacks.push(callback);
|
|
142
|
+
});
|
|
203
143
|
triggerPixelSpy = sandbox.spy(utils, "triggerPixel");
|
|
204
144
|
ucTagData = {
|
|
205
145
|
cacheHost: "example.com",
|
|
@@ -226,21 +166,21 @@ describe("renderingManager", function () {
|
|
|
226
166
|
sandbox.spy(window.parent, 'postMessage');
|
|
227
167
|
ucTagData.size = '400x500'
|
|
228
168
|
renderAmpOrMobileAd(ucTagData);
|
|
229
|
-
|
|
169
|
+
requestCallbacks[0](JSON.stringify(response));
|
|
230
170
|
sinon.assert.calledWith(window.parent.postMessage, {
|
|
231
171
|
sentinel: "amp",
|
|
232
172
|
type: "embed-size",
|
|
233
173
|
width: 400,
|
|
234
174
|
height: 500,
|
|
235
175
|
});
|
|
236
|
-
})
|
|
176
|
+
});
|
|
237
177
|
|
|
238
178
|
it("should render amp creative", function () {
|
|
239
179
|
ucTagData.hbPb = "10.00";
|
|
240
180
|
renderAmpOrMobileAd(ucTagData);
|
|
241
181
|
|
|
242
182
|
|
|
243
|
-
|
|
183
|
+
requestCallbacks[0](JSON.stringify(response));
|
|
244
184
|
expect(writeHtmlSpy.args[0][0]).to.equal(
|
|
245
185
|
"<!--Creative 123 served by Prebid.js Header Bidding-->ad-markup10.00"
|
|
246
186
|
);
|
|
@@ -255,7 +195,7 @@ describe("renderingManager", function () {
|
|
|
255
195
|
it("should replace AUCTION_PRICE with response.price over hbPb", function () {
|
|
256
196
|
renderAmpOrMobileAd(ucTagData);
|
|
257
197
|
response.price = 12.5;
|
|
258
|
-
|
|
198
|
+
requestCallbacks[0](JSON.stringify(response));
|
|
259
199
|
expect(writeHtmlSpy.args[0][0]).to.equal(
|
|
260
200
|
"<!--Creative 123 served by Prebid.js Header Bidding-->ad-markup12.5"
|
|
261
201
|
);
|
|
@@ -269,7 +209,7 @@ describe("renderingManager", function () {
|
|
|
269
209
|
|
|
270
210
|
it("should replace AUCTION_PRICE with with empty value when neither price nor hbPb exist", function () {
|
|
271
211
|
renderAmpOrMobileAd(ucTagData);
|
|
272
|
-
|
|
212
|
+
requestCallbacks[0](JSON.stringify(response));
|
|
273
213
|
expect(writeHtmlSpy.args[0][0]).to.equal(
|
|
274
214
|
"<!--Creative 123 served by Prebid.js Header Bidding-->ad-markup"
|
|
275
215
|
);
|
|
@@ -292,13 +232,31 @@ describe('writeAdHtml', () => {
|
|
|
292
232
|
it('removes DOCTYPE from markup', () => {
|
|
293
233
|
const ps = sinon.stub();
|
|
294
234
|
writeAdHtml('<!DOCTYPE html><div>mock-ad</div>', ps);
|
|
295
|
-
sinon.assert.calledWith(ps, '<div>mock-ad</div>')
|
|
235
|
+
sinon.assert.calledWith(ps, sinon.match.any, '<div>mock-ad</div>')
|
|
296
236
|
});
|
|
297
237
|
|
|
298
238
|
it('removes lowercase doctype from markup', () => {
|
|
299
239
|
const ps = sinon.stub();
|
|
300
240
|
writeAdHtml('<!doctype html><div>mock-ad</div>', ps);
|
|
301
|
-
sinon.assert.calledWith(ps, '<div>mock-ad</div>')
|
|
241
|
+
sinon.assert.calledWith(ps, sinon.match.any, '<div>mock-ad</div>')
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('removes XML declarations from markup', () => {
|
|
245
|
+
const ps = sinon.stub();
|
|
246
|
+
writeAdHtml('<?xml version="1.0" encoding="UTF-8"?><div>mock-ad</div>', ps);
|
|
247
|
+
sinon.assert.calledWith(ps, sinon.match.any, '<div>mock-ad</div>')
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('removes malformed XML declarations from markup', () => {
|
|
251
|
+
const ps = sinon.stub();
|
|
252
|
+
writeAdHtml('<?xml version="1.0"><div>mock-ad</div>', ps);
|
|
253
|
+
sinon.assert.calledWith(ps, sinon.match.any, '<div>mock-ad</div>')
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('removes doctype declarations with internal subsets from markup for postscribe compatibility (see postscribe issue 358)', () => {
|
|
257
|
+
const ps = sinon.stub();
|
|
258
|
+
writeAdHtml('<!DOCTYPE html [<!ENTITY nbsp " ">]><div>mock-ad</div>', ps);
|
|
259
|
+
sinon.assert.calledWith(ps, sinon.match.any, '<div>mock-ad</div>')
|
|
302
260
|
});
|
|
303
261
|
|
|
304
262
|
it('should execute script tag inserted into the body', () => {
|
|
@@ -306,4 +264,39 @@ describe('writeAdHtml', () => {
|
|
|
306
264
|
writeAdHtml(markup);
|
|
307
265
|
expect(window.testScriptExecuted).to.equal(true);
|
|
308
266
|
});
|
|
267
|
+
|
|
268
|
+
it('should handle single quotes with inner double quotes', () => {
|
|
269
|
+
const input = `<img title='uh "oh" > this should all be inside the title attribute'>`;
|
|
270
|
+
console.log('Input: ', input);
|
|
271
|
+
|
|
272
|
+
writeAdHtml(input);
|
|
273
|
+
|
|
274
|
+
const img = document.querySelector('img:last-of-type');
|
|
275
|
+
if (img) {
|
|
276
|
+
console.log('Output:', img.outerHTML);
|
|
277
|
+
console.log('Title: ', img.getAttribute('title'));
|
|
278
|
+
|
|
279
|
+
const expected = 'uh "oh" > this should all be inside the title attribute';
|
|
280
|
+
expect(img.getAttribute('title')).to.equal(expected);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('should handle JSON in data attributes with single quotes', () => {
|
|
285
|
+
const input = `<div data-json='{"key": "value"}'>Test</div>`;
|
|
286
|
+
console.log('Input: ', input);
|
|
287
|
+
|
|
288
|
+
writeAdHtml(input);
|
|
289
|
+
|
|
290
|
+
const div = document.querySelector('div[data-json]:last-of-type');
|
|
291
|
+
if (div) {
|
|
292
|
+
console.log('Output:', div.outerHTML);
|
|
293
|
+
console.log('Data: ', div.getAttribute('data-json'));
|
|
294
|
+
|
|
295
|
+
const dataJson = div.getAttribute('data-json');
|
|
296
|
+
expect(dataJson).to.equal('{"key": "value"}');
|
|
297
|
+
|
|
298
|
+
const parsed = JSON.parse(dataJson);
|
|
299
|
+
expect(parsed.key).to.equal('value');
|
|
300
|
+
}
|
|
301
|
+
});
|
|
309
302
|
})
|
|
@@ -43,7 +43,7 @@ const mockDocument = {
|
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
// creates mock postmessage response from prebid's native.js:getAssetMessage
|
|
46
|
-
function createResponder(assets,url,template
|
|
46
|
+
function createResponder(assets,url,template) {
|
|
47
47
|
return function(type, listener) {
|
|
48
48
|
if (type !== 'message') { return; }
|
|
49
49
|
|
|
@@ -443,7 +443,7 @@ describe('nativeAssetManager', () => {
|
|
|
443
443
|
expect(win.document.body.innerHTML).to.not.include(`##hb_native_asset_id_4##`);
|
|
444
444
|
|
|
445
445
|
// test that we are replacing ALL asset occurrences
|
|
446
|
-
expect((
|
|
446
|
+
expect((win.document.body.innerHTML.match(/new value/g) || []).length, "expected 2 occurrences of \"new value\"").to.equal(2);
|
|
447
447
|
});
|
|
448
448
|
|
|
449
449
|
it('no placeholders found but requests all assets flag set - adTemplate', () => {
|
|
@@ -522,7 +522,7 @@ describe('nativeAssetManager', () => {
|
|
|
522
522
|
let cb = sinon.spy();
|
|
523
523
|
let targetingData = {
|
|
524
524
|
uuid: '123'
|
|
525
|
-
}
|
|
525
|
+
};
|
|
526
526
|
|
|
527
527
|
sinon.stub(utils, 'sendRequest').callsFake(function(arg1, cb) {
|
|
528
528
|
let response = JSON.stringify({
|
|
@@ -560,7 +560,7 @@ describe('nativeAssetManager', () => {
|
|
|
560
560
|
// cta was not in the response so it should default to an empty string
|
|
561
561
|
expect(win.document.body.innerHTML).to.include('<h1></h1>');
|
|
562
562
|
expect(cb.getCall(0).args[0]).to.haveOwnProperty('eventtrackers');
|
|
563
|
-
})
|
|
563
|
+
});
|
|
564
564
|
})
|
|
565
565
|
|
|
566
566
|
describe('safe frame disabled', () => {
|
|
@@ -620,14 +620,26 @@ describe('nativeAssetManager', () => {
|
|
|
620
620
|
expect(win.document.body.innerHTML).to.include(`<a href="http://www.example.com" target="_blank" class="pb-click">new value</a>`);
|
|
621
621
|
});
|
|
622
622
|
|
|
623
|
+
function getResizeRequest() {
|
|
624
|
+
return win.parent.postMessage.args
|
|
625
|
+
.map(([msg]) => JSON.parse(msg))
|
|
626
|
+
.find((msg) => msg.action === 'resizeNativeHeight')
|
|
627
|
+
}
|
|
628
|
+
|
|
623
629
|
it('should not request width resize if width is 1', () => {
|
|
624
630
|
sandbox.stub(document.body, 'clientWidth').get(() => 1);
|
|
625
631
|
const nativeAssetManager = makeManager();
|
|
626
632
|
nativeAssetManager.loadAssets(AD_ID);
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
633
|
+
expect(getResizeRequest().width).to.not.exist;
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
it('should use scrollHeight if offsetHeight & clientHeight are 0', () => {
|
|
637
|
+
sandbox.stub(document.body, 'clientHeight').get(() => 0);
|
|
638
|
+
sandbox.stub(document.body, 'offsetHeight').get(() => 0);
|
|
639
|
+
sandbox.stub(document.documentElement, 'scrollHeight').get(() => 123);
|
|
640
|
+
const nativeAssetManager = makeManager();
|
|
641
|
+
nativeAssetManager.loadAssets(AD_ID);
|
|
642
|
+
expect(getResizeRequest().height).to.eql(123);
|
|
631
643
|
})
|
|
632
644
|
|
|
633
645
|
it('should set the iframe to the width of the container', () => {
|
|
@@ -14,11 +14,11 @@ describe('test firing native trackers', function () {
|
|
|
14
14
|
|
|
15
15
|
getElementsByClassName = sinon.stub(document, 'getElementsByClassName').callsFake(() => {
|
|
16
16
|
return [{
|
|
17
|
-
addEventListener: (event, callback
|
|
17
|
+
addEventListener: (event, callback) => {
|
|
18
18
|
// immediately call the callback to test the click
|
|
19
19
|
callback({
|
|
20
20
|
target: {
|
|
21
|
-
getAttribute: (
|
|
21
|
+
getAttribute: () => {
|
|
22
22
|
return 1;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -27,11 +27,6 @@ describe('nativeTrackerManager', function () {
|
|
|
27
27
|
pubUrl: 'http://example.com'
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
let tagDataAlt = {
|
|
31
|
-
pubUrl: 'http://example.com',
|
|
32
|
-
adId: 'ad123',
|
|
33
|
-
assetsToReplace: ['image','hb_native_linkurl','body','title'],
|
|
34
|
-
};
|
|
35
30
|
|
|
36
31
|
beforeEach(function () {
|
|
37
32
|
mockWin = merge(mocks.createFakeWindow(tagData.pubUrl), renderingMocks.getWindowObject());
|
|
@@ -49,7 +44,7 @@ describe('nativeTrackerManager', function () {
|
|
|
49
44
|
value: 'ad123'
|
|
50
45
|
}
|
|
51
46
|
},
|
|
52
|
-
addEventListener: (
|
|
47
|
+
addEventListener: () => {
|
|
53
48
|
},
|
|
54
49
|
}];
|
|
55
50
|
|
|
@@ -74,7 +69,7 @@ describe('nativeTrackerManager', function () {
|
|
|
74
69
|
value: 'ad123'
|
|
75
70
|
}
|
|
76
71
|
},
|
|
77
|
-
addEventListener: ((type, listener
|
|
72
|
+
addEventListener: ((type, listener) => {
|
|
78
73
|
listener({
|
|
79
74
|
})
|
|
80
75
|
})
|
|
@@ -97,7 +92,7 @@ describe('nativeTrackerManager', function () {
|
|
|
97
92
|
|
|
98
93
|
it('should verify 2 warning messages (one for impression, one for click) was executed', function() {
|
|
99
94
|
mockWin.document.getElementsByClassName = () => [{
|
|
100
|
-
addEventListener: ((type, listener
|
|
95
|
+
addEventListener: ((type, listener) => {
|
|
101
96
|
listener({
|
|
102
97
|
})
|
|
103
98
|
})
|