prebid-universal-creative 1.17.2 → 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/{2088600fa2b5f5e3c29c.max.js → main.max.js} +403 -371
- 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 -16
- package/src/cookieSync.js +2 -0
- package/src/cookieSyncWithConsent.js +2 -0
- package/src/mobileAndAmpRender.js +2 -2
- package/src/nativeAssetManager.js +1 -1
- package/src/nativeORTBTrackerManager.js +0 -2
- package/src/nativeTrackerManager.js +1 -1
- package/src/postscribeRender.js +63 -2
- package/src/renderingManager.js +1 -1
- package/test/spec/mobileAndAmpRender_spec.js +86 -93
- 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/karma.conf.maker.js
CHANGED
|
@@ -4,6 +4,16 @@ const karmaConstants = require('karma').constants;
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
6
|
function setBrowsers(karmaConf, browserstack) {
|
|
7
|
+
if (typeof process.getuid === 'function' && process.getuid() === 0) {
|
|
8
|
+
karmaConf.customLaunchers = Object.assign({}, karmaConf.customLaunchers, {
|
|
9
|
+
ChromeHeadlessNoSandbox: {
|
|
10
|
+
base: 'ChromeHeadless',
|
|
11
|
+
flags: ['--no-sandbox']
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
karmaConf.browsers = ['ChromeHeadlessNoSandbox'];
|
|
15
|
+
}
|
|
16
|
+
|
|
7
17
|
if (browserstack) {
|
|
8
18
|
karmaConf.browserStack = {
|
|
9
19
|
username: process.env.BROWSERSTACK_USERNAME,
|
|
@@ -46,17 +56,19 @@ function setReporters(karmaConf, codeCoverage, browserstack) {
|
|
|
46
56
|
function newWebpackConfig(codeCoverage) {
|
|
47
57
|
const webpackConfig = _.cloneDeep(webpackConf);
|
|
48
58
|
webpackConfig.devtool = 'inline-source-map';
|
|
59
|
+
delete webpackConfig.optimization;
|
|
49
60
|
|
|
50
61
|
if (codeCoverage) {
|
|
51
|
-
webpackConfig.module.rules.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
use: {
|
|
55
|
-
loader: 'istanbul-instrumenter-loader',
|
|
56
|
-
options: { esModules: true }
|
|
57
|
-
},
|
|
58
|
-
exclude: /(node_modules)|(test)|(resources)|(template)|(testpages)/
|
|
62
|
+
const babelRule = webpackConfig.module.rules.find(rule => {
|
|
63
|
+
const loaders = Array.isArray(rule.use) ? rule.use : [rule.use];
|
|
64
|
+
return loaders.some(loader => loader && loader.loader === 'babel-loader');
|
|
59
65
|
});
|
|
66
|
+
const babelLoader = babelRule.use.find(loader => loader.loader === 'babel-loader');
|
|
67
|
+
babelLoader.options.plugins = (babelLoader.options.plugins || []).concat([
|
|
68
|
+
['istanbul', {
|
|
69
|
+
exclude: ['test/**/*.js', 'resources/**/*.js', 'template/**/*.js', 'testpages/**/*.js']
|
|
70
|
+
}]
|
|
71
|
+
]);
|
|
60
72
|
}
|
|
61
73
|
return webpackConfig;
|
|
62
74
|
}
|
|
@@ -88,12 +100,12 @@ module.exports = function(codeCoverage, browserstack, watchMode) {
|
|
|
88
100
|
],
|
|
89
101
|
webpack: webpackConfig,
|
|
90
102
|
webpackMiddleware: {
|
|
91
|
-
|
|
103
|
+
stats: 'errors-only'
|
|
92
104
|
},
|
|
93
105
|
|
|
94
106
|
// frameworks to use
|
|
95
107
|
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
|
96
|
-
frameworks: ['mocha', 'chai', 'sinon'],
|
|
108
|
+
frameworks: ['mocha', 'chai', 'sinon', 'webpack'],
|
|
97
109
|
|
|
98
110
|
// list of files / patterns to load in the browser
|
|
99
111
|
files: files,
|
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,50 +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
|
-
"
|
|
75
|
-
"
|
|
73
|
+
"gulp-cli": "^3.0.0",
|
|
74
|
+
"postscribe": "^2.0.8"
|
|
76
75
|
}
|
|
77
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
|
|
|
@@ -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) {
|
|
@@ -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
|
|
package/src/postscribeRender.js
CHANGED
|
@@ -3,8 +3,69 @@ import postscribe from 'postscribe';
|
|
|
3
3
|
export function writeAdHtml(markup, ps = postscribe) {
|
|
4
4
|
// remove <?xml> and <!doctype> tags
|
|
5
5
|
// https://github.com/prebid/prebid-universal-creative/issues/134
|
|
6
|
-
markup = markup
|
|
7
|
-
|
|
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, {
|
|
8
20
|
error: console.error
|
|
9
21
|
});
|
|
10
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';
|
|
@@ -2,91 +2,28 @@ import { renderAmpOrMobileAd } from 'src/mobileAndAmpRender';
|
|
|
2
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/postscribeRender';
|
|
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
|
);
|
|
@@ -301,9 +241,62 @@ describe('writeAdHtml', () => {
|
|
|
301
241
|
sinon.assert.calledWith(ps, sinon.match.any, '<div>mock-ad</div>')
|
|
302
242
|
});
|
|
303
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>')
|
|
260
|
+
});
|
|
261
|
+
|
|
304
262
|
it('should execute script tag inserted into the body', () => {
|
|
305
263
|
const markup = '<script>window.testScriptExecuted=true;</script>'
|
|
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
|
}
|