Skip to content

Commit c738042

Browse files
committed
AG-30194 Add freewheel-admanager redirect resource and scriptlet. #401
Squashed commit of the following: commit 84beb81 Author: Adam Wróblewski <adam@adguard.com> Date: Wed Mar 4 15:09:17 2026 +0100 Add missed import freewheel-admanager.test commit 0938401 Author: Adam Wróblewski <adam@adguard.com> Date: Wed Mar 4 14:58:53 2026 +0100 Add freewheel-admanager redirect resource and scriptlet
1 parent 81ebdaf commit c738042

10 files changed

Lines changed: 232 additions & 0 deletions

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ The format is based on [Keep a Changelog], and this project adheres to [Semantic
1414

1515
### Added
1616

17+
- `freewheel-admanager` redirect resource and scriptlet [#401].
1718
- `getCreativeId` method to `Ad` class in `google-ima3` redirect [#515].
1819

1920
### Changed
@@ -33,6 +34,7 @@ The format is based on [Keep a Changelog], and this project adheres to [Semantic
3334
`debug-current-inline-script` and `log-on-stack-trace` scriptlets [#513].
3435

3536
[Unreleased]: https://github.com/AdguardTeam/Scriptlets/compare/v2.2.16...HEAD
37+
[#401]: https://github.com/AdguardTeam/Scriptlets/issues/401
3638
[#513]: https://github.com/AdguardTeam/Scriptlets/issues/513
3739
[#515]: https://github.com/AdguardTeam/Scriptlets/issues/515
3840
[#545]: https://github.com/AdguardTeam/Scriptlets/issues/545

scripts/compatibility-table.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@
7070
"adg": "fingerprintjs3",
7171
"ubo": "fingerprintjs3.js (fingerprintjs3)"
7272
},
73+
{
74+
"adg": "freewheel-admanager"
75+
},
7376
{
7477
"adg": "gemius"
7578
},
@@ -467,6 +470,9 @@
467470
"adg": "fingerprintjs3",
468471
"ubo": "fingerprint3.js"
469472
},
473+
{
474+
"adg": "freewheel-admanager"
475+
},
470476
{
471477
"adg": "google-analytics",
472478
"ubo": "google-analytics_analytics.js"
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { hit, noopFunc, noopThis } from '../helpers';
2+
3+
/**
4+
* @redirect freewheel-admanager
5+
*
6+
* @description
7+
* Mocks the FreeWheel Ad Manager.
8+
*
9+
* ### Examples
10+
*
11+
* ```adblock
12+
* ||mssl.fwmrm.net/libs/adm/6.55.0/AdManager.js$script,redirect=freewheel-admanager
13+
* ```
14+
*
15+
* @added unknown.
16+
*/
17+
18+
export function FreewheelAdManager(source) {
19+
const eventsMap = new Map();
20+
const adManagerFunc = noopFunc;
21+
adManagerFunc.prototype.addEventListener = function (type, callback) {
22+
if (type) {
23+
eventsMap.set(type, callback);
24+
}
25+
};
26+
adManagerFunc.prototype.addKeyValue = noopFunc;
27+
adManagerFunc.prototype.addTemporalSlot = noopFunc;
28+
adManagerFunc.prototype.dispose = noopFunc;
29+
adManagerFunc.prototype.newContext = noopThis;
30+
adManagerFunc.prototype.registerCustomPlayer = noopFunc;
31+
adManagerFunc.prototype.registerVideoDisplayBase = noopFunc;
32+
adManagerFunc.prototype.removeEventListener = noopFunc;
33+
adManagerFunc.prototype.resize = noopFunc;
34+
adManagerFunc.prototype.setCapability = noopFunc;
35+
adManagerFunc.prototype.setContentVideoElement = noopFunc;
36+
adManagerFunc.prototype.setLogLevel = noopFunc;
37+
adManagerFunc.prototype.setNetwork = noopFunc;
38+
adManagerFunc.prototype.setParameter = noopFunc;
39+
adManagerFunc.prototype.setProfile = noopFunc;
40+
adManagerFunc.prototype.setServer = noopFunc;
41+
adManagerFunc.prototype.setSiteSection = noopFunc;
42+
adManagerFunc.prototype.setVideoAsset = noopFunc;
43+
adManagerFunc.prototype.setVideoDisplaySize = noopFunc;
44+
adManagerFunc.prototype.submitRequest = () => {
45+
const event = {
46+
type: window.tv.freewheel.SDK.EVENT_SLOT_ENDED,
47+
};
48+
const callbackFunc = eventsMap.get('EVENT_SLOT_ENDED');
49+
if (callbackFunc && typeof callbackFunc === 'function') {
50+
setTimeout(() => {
51+
try {
52+
callbackFunc(event);
53+
} catch (ex) {
54+
// Silently catch errors
55+
}
56+
}, 1);
57+
}
58+
};
59+
60+
window.tv = {
61+
freewheel: {
62+
SDK: {
63+
_instanceQueue: {},
64+
Ad: noopFunc,
65+
AdListener: noopFunc,
66+
AdManager: adManagerFunc,
67+
EVENT_SLOT_ENDED: 'EVENT_SLOT_ENDED',
68+
setLogLevel: noopFunc,
69+
},
70+
},
71+
};
72+
73+
hit(source);
74+
}
75+
76+
export const FreewheelAdManagerNames = [
77+
'freewheel-admanager',
78+
];
79+
80+
// eslint-disable-next-line prefer-destructuring
81+
FreewheelAdManager.primaryName = FreewheelAdManagerNames[0];
82+
83+
FreewheelAdManager.injections = [hit, noopFunc, noopThis];

src/redirects/redirects-list.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ export { DidomiLoader } from './didomi-loader';
2323
export { Prebid } from './prebid';
2424
export { prebidAds } from './prebid-ads';
2525
export { NaverWcslog } from './naver-wcslog';
26+
export { FreewheelAdManager } from './freewheel-admanager';

src/redirects/redirects-names-list.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ export { DidomiLoaderNames } from './didomi-loader';
2323
export { PrebidNames } from './prebid';
2424
export { prebidAdsNames } from './prebid-ads';
2525
export { NaverWcslogNames } from './naver-wcslog';
26+
export { FreewheelAdManagerNames } from './freewheel-admanager';
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { FreewheelAdManager, FreewheelAdManagerNames } from '../redirects/freewheel-admanager';
2+
3+
/**
4+
* @scriptlet freewheel-admanager
5+
*
6+
* @description
7+
* Mocks the FreeWheel Ad Manager.
8+
*
9+
* ### Examples
10+
*
11+
* ```adblock
12+
* example.org#%#//scriptlet('freewheel-admanager')
13+
* ```
14+
*
15+
* @added unknown.
16+
*/
17+
export { FreewheelAdManager, FreewheelAdManagerNames };

src/scriptlets/scriptlets-list.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export { AmazonApstag } from './amazon-apstag';
8585
export { DidomiLoader } from './didomi-loader';
8686
export { Fingerprintjs2 } from './fingerprintjs2';
8787
export { Fingerprintjs3 } from './fingerprintjs3';
88+
export { FreewheelAdManager } from './freewheel-admanager';
8889
export { Gemius } from './gemius';
8990
export { GoogleAnalytics } from './google-analytics';
9091
export { GoogleAnalyticsGa } from './google-analytics-ga';

src/scriptlets/scriptlets-names-list.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export { AmazonApstagNames } from './amazon-apstag';
8585
export { DidomiLoaderNames } from './didomi-loader';
8686
export { Fingerprintjs2Names } from './fingerprintjs2';
8787
export { Fingerprintjs3Names } from './fingerprintjs3';
88+
export { FreewheelAdManagerNames } from './freewheel-admanager';
8889
export { GemiusNames } from './gemius';
8990
export { GoogleAnalyticsNames } from './google-analytics';
9091
export { GoogleAnalyticsGaNames } from './google-analytics-ga';
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/* eslint-disable no-underscore-dangle */
2+
import { clearGlobalProps, getRedirectsInstance, evalWrapper } from '../helpers';
3+
4+
const { test, module } = QUnit;
5+
const name = 'freewheel-admanager';
6+
7+
const freewheelTvProp = 'tv';
8+
9+
const afterEach = () => {
10+
clearGlobalProps(freewheelTvProp);
11+
};
12+
13+
let redirects;
14+
const before = async () => {
15+
redirects = await getRedirectsInstance();
16+
};
17+
18+
module(name, { before, afterEach });
19+
20+
test('Mocked - window.tv.freewheel.SDK structure', (assert) => {
21+
evalWrapper(redirects.getRedirect(name).content);
22+
23+
assert.ok(window.tv, 'window.tv created');
24+
assert.ok(window.tv.freewheel, 'window.tv.freewheel created');
25+
assert.ok(window.tv.freewheel.SDK, 'window.tv.freewheel.SDK created');
26+
27+
const { SDK } = window.tv.freewheel;
28+
assert.ok(SDK.Ad, 'SDK.Ad mocked');
29+
assert.ok(SDK.AdManager, 'SDK.AdManager mocked');
30+
assert.ok(SDK.AdListener, 'SDK.AdListener mocked');
31+
assert.ok(SDK.setLogLevel, 'SDK.setLogLevel mocked');
32+
assert.strictEqual(SDK.EVENT_SLOT_ENDED, 'EVENT_SLOT_ENDED', 'SDK.EVENT_SLOT_ENDED is set correctly');
33+
assert.deepEqual(SDK._instanceQueue, {}, 'SDK._instanceQueue is empty object');
34+
});
35+
36+
test('Mocked - AdManager instance methods', (assert) => {
37+
evalWrapper(redirects.getRedirect(name).content);
38+
39+
const AdManager = new window.tv.freewheel.SDK.AdManager();
40+
41+
const methods = new Set([
42+
'addEventListener',
43+
'addKeyValue',
44+
'addTemporalSlot',
45+
'dispose',
46+
'newContext',
47+
'registerCustomPlayer',
48+
'registerVideoDisplayBase',
49+
'removeEventListener',
50+
'resize',
51+
'setCapability',
52+
'setContentVideoElement',
53+
'setLogLevel',
54+
'setNetwork',
55+
'setParameter',
56+
'setProfile',
57+
'setServer',
58+
'setSiteSection',
59+
'setVideoAsset',
60+
'setVideoDisplaySize',
61+
'submitRequest',
62+
]);
63+
64+
methods.forEach((method) => {
65+
assert.strictEqual(typeof AdManager[method], 'function', `AdManager.${method} is a function`);
66+
});
67+
});
68+
69+
test('Mocked - submitRequest fires EVENT_SLOT_ENDED callback', (assert) => {
70+
const done = assert.async();
71+
72+
evalWrapper(redirects.getRedirect(name).content);
73+
74+
const { SDK } = window.tv.freewheel;
75+
const adManager = new SDK.AdManager();
76+
77+
let callbackFired = false;
78+
let receivedEventType;
79+
80+
adManager.addEventListener('EVENT_SLOT_ENDED', (event) => {
81+
callbackFired = true;
82+
receivedEventType = event.type;
83+
});
84+
85+
adManager.submitRequest();
86+
87+
setTimeout(() => {
88+
assert.strictEqual(callbackFired, true, 'EVENT_SLOT_ENDED callback was fired');
89+
assert.strictEqual(
90+
receivedEventType,
91+
SDK.EVENT_SLOT_ENDED,
92+
'event.type matches SDK.EVENT_SLOT_ENDED',
93+
);
94+
done();
95+
}, 10);
96+
});
97+
98+
test('Mocked - submitRequest does not throw without listener', (assert) => {
99+
evalWrapper(redirects.getRedirect(name).content);
100+
101+
const { SDK } = window.tv.freewheel;
102+
const adManager = new SDK.AdManager();
103+
104+
try {
105+
adManager.submitRequest();
106+
assert.ok(true, 'submitRequest does not throw when no listener is registered');
107+
} catch (error) {
108+
assert.ok(false, `submitRequest threw an error when no listener was registered: ${error && error.message}`);
109+
}
110+
});
111+
112+
test('Mocked - newContext returns context object', (assert) => {
113+
evalWrapper(redirects.getRedirect(name).content);
114+
115+
const adManager = new window.tv.freewheel.SDK.AdManager();
116+
const context = adManager.newContext();
117+
118+
assert.strictEqual(context, adManager, 'newContext returns this');
119+
});

tests/redirects/index.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ import './prebid-ads.test';
1919
import './naver-wcslog.test';
2020
import './pardot-1.0.test';
2121
import './noeval.test';
22+
import './freewheel-admanager.test';

0 commit comments

Comments
 (0)