Skip to content

Commit

Permalink
#7803 fixes
Browse files Browse the repository at this point in the history
* getAd changed to inbuilt ajax method
  • Loading branch information
ym-abaranov committed Jan 28, 2022
1 parent 2ccb9b7 commit 8bec1fb
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 123 deletions.
73 changes: 34 additions & 39 deletions modules/yieldmoSyntheticInventoryModule.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { config } from '../src/config.js';
import { isGptPubadsDefined, isFn } from '../src/utils.js';
import * as ajax from '../src/ajax.js'
import strIncludes from 'core-js-pure/features/string/includes.js';

export const MODULE_NAME = 'Yieldmo Synthetic Inventory Module';
export const AD_SERVER_ENDPOINT = 'https://ads.yieldmo.com/v002/t_ads/ads';
export const AD_REQUEST_TYPE = 'GET';
const USPAPI_VERSION = 1;

let cmpVersion = 0;
Expand All @@ -23,7 +23,22 @@ export function init(config) {
window.top.googletag = window.top.googletag || {};
window.top.googletag.cmd = window.top.googletag.cmd || [];
}
getAd(`${AD_SERVER_ENDPOINT}?${serialize(collectData(config.placementId, consentDataObj))}`, config);
ajax.ajaxBuilder()(`${AD_SERVER_ENDPOINT}?${serialize(collectData(config.placementId, consentDataObj))}`, {
success: (responceText, responseObj) => {
window.top.__ymAds = processResponse(responseObj);
const googletag = window.top.googletag;
googletag.cmd.push(() => {
if (window.top.document.body) {
googletagCmd(config, googletag);
} else {
window.top.document.addEventListener('DOMContentLoaded', () => googletagCmd(config, googletag));
}
});
},
error: (message, err) => {
throw err;
}
});
}
}
};
Expand Down Expand Up @@ -58,11 +73,6 @@ function collectData(placementId, consentDataObj) {
const connection = window.navigator.connection || {};
const description = Array.prototype.slice.call(document.getElementsByTagName('meta'))
.filter((meta) => meta.getAttribute('name') === 'description')[0];
const pageDimensions = {
density: window.top.devicePixelRatio || 0,
height: window.top.screen.height || window.top.screen.availHeight || window.top.outerHeight || window.top.innerHeight || 481,
width: window.top.screen.width || window.top.screen.availWidth || window.top.outerWidth || window.top.innerWidth || 321,
};

return {
bust: timeStamp,
Expand All @@ -74,15 +84,15 @@ function collectData(placementId, consentDataObj) {
p: placementId,
description: description ? description.content.substring(0, 1000) : '',
title: document.title,
scrd: pageDimensions.density,
h: pageDimensions.height,
w: pageDimensions.width,
scrd: window.top.devicePixelRatio || 0,
h: window.top.screen.height || window.top.screen.availHeight || window.top.outerHeight || window.top.innerHeight || 481,
w: window.top.screen.width || window.top.screen.availWidth || window.top.outerWidth || window.top.innerWidth || 321,
pft: timeStamp,
ct: timeStamp,
connect: connection.effectiveType,
bwe: connection.downlink ? connection.downlink + 'Mb/sec' : '',
rtt: connection.rtt,
sd: connection.saveData,
connect: typeof connection.effectiveType !== 'undefined' ? connection.effectiveType : undefined,
bwe: typeof connection.downlink !== 'undefined' ? connection.downlink + 'Mb/sec' : undefined,
rtt: typeof connection.rtt !== 'undefined' ? String(connection.rtt) : undefined,
sd: typeof connection.saveData !== 'undefined' ? String(connection.saveData) : undefined,
us_privacy: (consentDataObj.usp && consentDataObj.usp.usPrivacy) || '',
cmp: (consentDataObj.cmp && consentDataObj.cmp.tcString) || ''
};
Expand All @@ -98,35 +108,20 @@ function serialize(dataObj) {
return str.join('&');
}

function processResponse(response) {
let responseBody;
function processResponse(res) {
let parsedResponseBody;
try {
responseBody = JSON.parse(response.responseText);
parsedResponseBody = JSON.parse(res.responseText);
} catch (err) {
throw new Error(`${MODULE_NAME}: response body is not valid JSON`);
throw new Error(`${MODULE_NAME}: response is not valid JSON`);
}
if (response.status !== 200 || !responseBody.data || !responseBody.data.length || !responseBody.data[0].ads || !responseBody.data[0].ads.length) {
throw new Error(`${MODULE_NAME}: NOAD`);
if (res && res.status === 204) {
throw new Error(`${MODULE_NAME}: no content success status`);
}
return responseBody;
}

function getAd(url, config) {
const req = new XMLHttpRequest();
req.open(AD_REQUEST_TYPE, url, true);
req.onload = (e) => {
const response = processResponse(e.target);
window.top.__ymAds = response;
const googletag = window.top.googletag;
googletag.cmd.push(() => {
if (window.top.document.body) {
googletagCmd(config, googletag);
} else {
window.top.document.addEventListener('DOMContentLoaded', () => googletagCmd(config, googletag));
}
});
};
req.send(null);
if (parsedResponseBody.data && parsedResponseBody.data.length && parsedResponseBody.data[0].error_code) {
throw new Error(`${MODULE_NAME}: no ad, error_code: ${parsedResponseBody.data[0].error_code}`);
}
return parsedResponseBody;
}

function checkSandbox(w) {
Expand Down
146 changes: 62 additions & 84 deletions test/spec/modules/yieldmoSyntheticInventoryModule_spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expect } from 'chai';
import * as ajax from 'src/ajax.js';
import {
init,
MODULE_NAME,
Expand Down Expand Up @@ -68,16 +69,15 @@ describe('Yieldmo Synthetic Inventory Module', function() {
}).throw(`${MODULE_NAME}: adUnitPath required`);
});

describe('getAd', () => {
let requestMock = {
open: sinon.stub(),
send: sinon.stub(),
};
const originalXMLHttpRequest = window.XMLHttpRequest;
const originalConnection = window.navigator.connection;
let clock;
let adServerRequest;
let response;
describe('Ajax ad request', () => {
let sandbox;

const setAjaxStub = (cb) => {
const ajaxStub = sandbox.stub().callsFake(cb);
sandbox.stub(ajax, 'ajaxBuilder').callsFake(() => ajaxStub);
return ajaxStub;
}

const responseData = {
data: [{
ads: [{
Expand All @@ -87,128 +87,104 @@ describe('Yieldmo Synthetic Inventory Module', function() {
};

beforeEach(() => {
window.XMLHttpRequest = function FakeXMLHttpRequest() {
this.open = requestMock.open;
this.send = requestMock.send;

adServerRequest = this;
};

response = {
target: {
responseText: JSON.stringify(responseData),
status: 200,
}
};

clock = sinon.useFakeTimers();
Object.defineProperty(window.navigator, 'connection', { value: {}, writable: true });
sandbox = sinon.sandbox.create();
});

afterEach(() => {
window.XMLHttpRequest = originalXMLHttpRequest;

requestMock.open.resetBehavior();
requestMock.open.resetHistory();
requestMock.send.resetBehavior();
requestMock.send.resetHistory();

adServerRequest = undefined;

clock.restore();
});

after(() => {
window.navigator.connection = originalConnection;
sandbox.restore();
});

it('should open ad request to ad server', () => {
const ajaxStub = setAjaxStub((url, callbackObj) => {});

init(mockedYmConfig);

const adServerHost = (new URL(requestMock.open.getCall(0).args[1])).host;
expect(adServerHost).to.be.equal('ads.yieldmo.com');
expect((new URL(ajaxStub.getCall(0).args[0])).host).to.be.equal('ads.yieldmo.com');
});

it('should properly combine ad request query', () => {
const pageDimensions = {
density: window.top.devicePixelRatio || 0,
height: window.top.screen.height || window.screen.top.availHeight || window.top.outerHeight || window.top.innerHeight || 481,
width: window.top.screen.width || window.screen.top.availWidth || window.top.outerWidth || window.top.innerWidth || 321,
};
const title = 'Test title value';
const ajaxStub = setAjaxStub((url, callbackObj) => {});
const documentStubTitle = sandbox.stub(document, 'title').value(title);
const connection = window.navigator.connection || {};

init(mockedYmConfig);

const queryParams = getQuearyParamsFromUrl(requestMock.open.getCall(0).args[1]);

const queryParams = getQuearyParamsFromUrl(ajaxStub.getCall(0).args[0]);
const timeStamp = queryParams.bust;

expect(queryParams).to.deep.equal({
const paramsToCompare = {
title,
_s: '1',
dnt: 'false',
e: '4',
h: `${pageDimensions.height}`,
p: mockedYmConfig.placementId,
page_url: window.top.location.href,
pr: window.top.location.href,
scrd: `${pageDimensions.density}`,
w: `${pageDimensions.width}`,
title: document.title,
});
});

it('should send ad request to ad server', () => {
init(mockedYmConfig);
bust: timeStamp,
pft: timeStamp,
ct: timeStamp,
connect: typeof connection.effectiveType !== 'undefined' ? connection.effectiveType : undefined,
bwe: typeof connection.downlink !== 'undefined' ? connection.downlink + 'Mb/sec' : undefined,
rtt: typeof connection.rtt !== 'undefined' ? String(connection.rtt) : undefined,
sd: typeof connection.saveData !== 'undefined' ? String(connection.saveData) : undefined,
scrd: String(window.top.devicePixelRatio || 0),
h: String(window.top.screen.height || window.screen.top.availHeight || window.top.outerHeight || window.top.innerHeight || 481),
w: String(window.top.screen.width || window.screen.top.availWidth || window.top.outerWidth || window.top.innerWidth || 321),
};

expect(requestMock.send.calledOnceWith(null)).to.be.true;
expect(queryParams).to.eql(JSON.parse(JSON.stringify(paramsToCompare)));
});

it('should throw an error if can not parse response', () => {
response.target.responseText = undefined;
it('should send ad request to ad server', () => {
const ajaxStub = setAjaxStub((url, callbackObj) => {});

init(mockedYmConfig);

expect(() => adServerRequest.onload(response)).to.throw();
expect(ajaxStub.calledOnce).to.be.true;
});

it('should throw an error if status is not 200', () => {
response.target.status = 500;

init(mockedYmConfig);
it('should throw an error if can not parse response', () => {
const ajaxStub = setAjaxStub((url, callbackObj) => {
callbackObj.success('', {responseText: '__invalid_JSON__', status: 200});
});

expect(() => adServerRequest.onload(response)).to.throw();
expect(() => init(mockedYmConfig)).to.throw('Yieldmo Synthetic Inventory Module: response is not valid JSON');
});

it('should throw an error if there is no data in response', () => {
response.target.responseText = '{}';

init(mockedYmConfig);
it('should throw an error if status is 204', () => {
const ajaxStub = setAjaxStub((url, callbackObj) => {
callbackObj.success('', {status: 204, responseText: '{}'});
});

expect(() => adServerRequest.onload(response)).to.throw();
expect(() => init(mockedYmConfig)).to.throw('Yieldmo Synthetic Inventory Module: no content success status');
});

it('should throw an error if there is no ads in response data', () => {
response.target.responseText = '{ data: [{}] }';

init(mockedYmConfig);
it('should throw an error if error_code present in the ad response', () => {
const ajaxStub = setAjaxStub((url, callbackObj) => {
callbackObj.success('', {status: 200, responseText: '{"data": [{"error_code": "NOAD"}]}'});
});

expect(() => adServerRequest.onload(response)).to.throw();
expect(() => init(mockedYmConfig)).to.throw('Yieldmo Synthetic Inventory Module: no ad, error_code: NOAD');
});

it('should store ad response in window object', () => {
init(mockedYmConfig);

adServerRequest.onload(response);
const ajaxStub = setAjaxStub((url, callbackObj) => {
callbackObj.success(JSON.stringify(responseData), {status: 200, responseText: JSON.stringify(responseData)});
});

init(mockedYmConfig);
expect(window.top.__ymAds).to.deep.equal(responseData);
});

it('should add correct googletag.cmd', function() {
const containerName = 'ym_sim_container_' + mockedYmConfig.placementId;
const gtag = setGoogletag();

init(mockedYmConfig);
const ajaxStub = setAjaxStub((url, callbackObj) => {
callbackObj.success(JSON.stringify(responseData), {status: 200, responseText: '{"data": [{"ads": []}]}'});
});

adServerRequest.onload(response);
init(mockedYmConfig);

expect(gtag.cmd.length).to.equal(1);

Expand Down Expand Up @@ -252,6 +228,7 @@ describe('Yieldmo Synthetic Inventory Module', function() {
window.XMLHttpRequest = function FakeXMLHttpRequest() {
this.open = requestMock.open;
this.send = requestMock.send;
this.setRequestHeader = () => {};
};

clock = sinon.useFakeTimers();
Expand Down Expand Up @@ -426,6 +403,7 @@ describe('Yieldmo Synthetic Inventory Module', function() {
window.XMLHttpRequest = function FakeXMLHttpRequest() {
this.open = requestMock.open;
this.send = requestMock.send;
this.setRequestHeader = () => {};
};

clock = sinon.useFakeTimers();
Expand Down

0 comments on commit 8bec1fb

Please sign in to comment.