/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict" ;
ChromeUtils.defineESModuleGetters(
this , {
AdsFeed:
"resource://newtab/lib/AdsFeed.sys.mjs",
actionCreators:
"resource://newtab/common/Actions.mjs",
actionTypes:
"resource://newtab/common/Actions.mjs",
ContextId:
"moz-src:///browser/modules/ContextId.sys.mjs",
sinon:
"resource://testing-common/Sinon.sys.mjs",
});
const { ObliviousHTTP } = ChromeUtils.importESModule(
"resource://gre/modules/ObliviousHTTP.sys.mjs"
);
// Prefs to enable UAPI ad types
const PREF_UNIFIED_ADS_ADSFEED_ENABLED =
"unifiedAds.adsFeed.enabled" ;
const PREF_UNIFIED_ADS_TILES_ENABLED =
"unifiedAds.tiles.enabled" ;
const PREF_UNIFIED_ADS_SPOCS_ENABLED =
"unifiedAds.spocs.enabled" ;
const PREF_UNIFIED_ADS_ENDPOINT =
"unifiedAds.endpoint" ;
const PREF_TILES_PLACEMENTS =
"discoverystream.placements.tiles" ;
const PREF_TILES_COUNTS =
"discoverystream.placements.tiles.counts" ;
const PREF_SPOC_COUNTS =
"discoverystream.placements.spocs.counts" ;
const PREF_SPOC_PLACEMENTS =
"discoverystream.placements.spocs" ;
const PREF_UNIFIED_ADS_BLOCKED_LIST =
"unifiedAds.blockedAds" ;
// Note: Full pref path required by Services.prefs.setBoolPref
const PREF_UNIFIED_ADS_OHTTP_ENABLED =
"browser.newtabpage.activity-stream.unifiedAds.ohttp.enabled" ;
const PREF_UNIFIED_ADS_OHTTP_RELAY_URL =
"browser.newtabpage.activity-stream.discoverystream.ohttp.relayURL" ;
const PREF_UNIFIED_ADS_OHTTP_CONFIG_URL =
"browser.newtabpage.activity-stream.discoverystream.ohttp.configURL" ;
// Primary pref that is toggled when enabling top site sponsored tiles
const PREF_FEED_TOPSITES =
"feeds.topsites" ;
const PREF_SYSTEM_TOPSITES =
"feeds.system.topsites" ;
const PREF_SHOW_SPONSORED_TOPSITES =
"showSponsoredTopSites" ;
// Primary pref that is toggled when enabling sponsored stories
const PREF_FEED_SECTIONS_TOPSTORIES =
"feeds.section.topstories" ;
const PREF_SYSTEM_TOPSTORIES =
"feeds.system.topstories" ;
const PREF_SHOW_SPONSORED =
"showSponsored" ;
const PREF_SYSTEM_SHOW_SPONSORED =
"system.showSponsored" ;
const mockedTileData = [
{
url:
"https://www.test.com ",
image_url:
"images/test-com.png" ,
click_url:
"https://www.test-click.com ",
impression_url:
"https://www.test-impression.com ",
name:
"test" ,
},
{
url:
"https://www.test1.com ",
image_url:
"images/test1-com.png" ,
click_url:
"https://www.test1-click.com ",
impression_url:
"https://www.test1-impression.com ",
name:
"test1" ,
},
];
const mockedFetchTileData = {
newtab_tile_1: [
{
format:
"tile" ,
url:
"https://www.test1.com ",
callbacks: {
click:
"https://www.test1-click.com ",
impression:
"https://www.test1-impression.com ",
report:
"https://www.test1-report.com ",
},
image_url:
"images/test1-com.png" ,
name:
"test1" ,
block_key:
"test1" ,
},
],
newtab_tile_2: [
{
format:
"tile" ,
url:
"https://www.test2.com ",
callbacks: {
click:
"https://www.test2-click.com ",
impression:
"https://www.test2-impression.com ",
report:
"https://www.test2-report.com ",
},
image_url:
"images/test2-com.png" ,
name:
"test2" ,
block_key:
"test2" ,
},
],
newtab_tile_3: [
{
format:
"tile" ,
url:
"https://www.test3.com ",
callbacks: {
click:
"https://www.test3-click.com ",
impression:
"https://www.test3-impression.com ",
report:
"https://www.test3-report.com ",
},
image_url:
"images/test3-com.png" ,
name:
"test3" ,
block_key:
"test3" ,
},
],
};
const mockedSpocPlacements = [
{
placement:
"newtab_spocs" ,
count:
6 ,
},
];
const mockedSpocsData = [
{
format:
"spoc" ,
url:
"https://www.test.com ",
callbacks: {
click:
"https://www.test-click.com ",
impression:
"https://www.test-impression.com ",
report:
"https://www.test-report.com ",
},
image_url:
"images/test-com.png" ,
title:
"test" ,
domain:
"test.com" ,
excerpt:
"Lorem ipsum dolor sit amet." ,
sponsor:
"Test" ,
block_key:
"test" ,
caps: {
cap_key:
"123456789" ,
day:
10 ,
},
ranking: {
priority:
9 ,
personalization_models: {},
item_score:
1 ,
},
},
{
format:
"spoc" ,
url:
"https://www.test1.com ",
callbacks: {
click:
"https://www.test1-click.com ",
impression:
"https://www.test1-impression.com ",
report:
"https://www.test1-report.com ",
},
image_url:
"images/test1-com.png" ,
title:
"test1" ,
domain:
"test1.com" ,
excerpt:
"Lorem ipsum dolor sit amet." ,
sponsor:
"Test1" ,
block_key:
"test1" ,
caps: {
cap_key:
"234567891" ,
day:
10 ,
},
ranking: {
priority:
9 ,
personalization_models: {},
item_score:
1 ,
},
},
{
format:
"spoc" ,
url:
"https://www.test2.com ",
callbacks: {
click:
"https://www.test2-click.com ",
impression:
"https://www.test2-impression.com ",
report:
"https://www.test2-report.com ",
},
image_url:
"images/test2-com.png" ,
title:
"test2" ,
domain:
"test2.com" ,
excerpt:
"Lorem ipsum dolor sit amet." ,
sponsor:
"Test2" ,
block_key:
"test2" ,
caps: {
cap_key:
"345678912" ,
day:
10 ,
},
ranking: {
priority:
9 ,
personalization_models: {},
item_score:
1 ,
},
},
{
format:
"spoc" ,
url:
"https://www.test3.com ",
callbacks: {
click:
"https://www.test3-click.com ",
impression:
"https://www.test3-impression.com ",
report:
"https://www.test3-report.com ",
},
image_url:
"images/test3-com.png" ,
title:
"test3" ,
domain:
"test3.com" ,
excerpt:
"Lorem ipsum dolor sit amet." ,
sponsor:
"Test3" ,
block_key:
"test3" ,
caps: {
cap_key:
"456789123" ,
day:
10 ,
},
ranking: {
priority:
9 ,
personalization_models: {},
item_score:
1 ,
},
},
{
format:
"spoc" ,
url:
"https://www.test4.com ",
callbacks: {
click:
"https://www.test4-click.com ",
impression:
"https://www.test4-impression.com ",
report:
"https://www.test4-report.com ",
},
image_url:
"images/test4-com.png" ,
title:
"test4" ,
domain:
"test4.com" ,
excerpt:
"Lorem ipsum dolor sit amet." ,
sponsor:
"Test4" ,
block_key:
"test4" ,
caps: {
cap_key:
"567891234" ,
day:
10 ,
},
ranking: {
priority:
9 ,
personalization_models: {},
item_score:
1 ,
},
},
{
format:
"spoc" ,
url:
"https://www.test5.com ",
callbacks: {
click:
"https://www.test5-click.com ",
impression:
"https://www.test5-impression.com ",
report:
"https://www.test5-report.com ",
},
image_url:
"images/test5-com.png" ,
title:
"test5" ,
domain:
"test5.com" ,
excerpt:
"Lorem ipsum dolor sit amet." ,
sponsor:
"Test5" ,
block_key:
"test5" ,
caps: {
cap_key:
"678912345" ,
day:
10 ,
},
ranking: {
priority:
9 ,
personalization_models: {},
item_score:
1 ,
},
},
];
function getAdsFeedForTest() {
let feed =
new AdsFeed();
let tiles = mockedTileData;
let spocs = mockedSpocsData;
let spocPlacements = mockedSpocPlacements;
feed.store = {
dispatch: sinon.spy(),
getState() {
return this .state;
},
state: {
tiles,
spocs,
spocPlacements,
lastUpdated:
1 ,
Prefs: {
values: {
[PREF_TILES_PLACEMENTS]:
"newtab_tile_1, newtab_tile_2, newtab_tile_3" ,
[PREF_TILES_COUNTS]:
"1, 1, 1" ,
[PREF_SPOC_PLACEMENTS]:
"newtab_spocs" ,
[PREF_SPOC_COUNTS]:
"6" ,
[PREF_UNIFIED_ADS_BLOCKED_LIST]:
"" ,
[PREF_UNIFIED_ADS_ENDPOINT]:
"https://example.com/ ",
// AdsFeed/UAPI specific prefs to test
[PREF_UNIFIED_ADS_TILES_ENABLED]:
false ,
[PREF_UNIFIED_ADS_SPOCS_ENABLED]:
false ,
[PREF_UNIFIED_ADS_ADSFEED_ENABLED]:
false ,
// Default display prefs for tiles/spocs
[PREF_FEED_TOPSITES]:
true ,
[PREF_SYSTEM_TOPSITES]:
true ,
[PREF_SHOW_SPONSORED_TOPSITES]:
true ,
[PREF_FEED_SECTIONS_TOPSTORIES]:
true ,
[PREF_SYSTEM_TOPSTORIES]:
true ,
[PREF_SHOW_SPONSORED]:
true ,
[PREF_SYSTEM_SHOW_SPONSORED]:
true ,
},
},
},
};
return feed;
}
add_task(async
function test_construction() {
let sandbox = sinon.createSandbox();
sandbox.stub(AdsFeed.prototype,
"PersistentCache" ).returns({
set: () => {},
get: () => {},
});
let feed =
new AdsFeed();
info(
"AdsFeed constructor should create initial values" );
Assert .ok(feed,
"Could construct a AdsFeed" );
Assert .strictEqual(feed.loaded,
false ,
"AdsFeed is not loaded" );
Assert .strictEqual(feed.enabled,
false ,
"AdsFeed is not enabled" );
Assert .strictEqual(
feed.lastUpdated,
null ,
"AdsFeed has no lastUpdated record"
);
Assert .strictEqual(
feed.tiles.length,
0 ,
"tiles is initialized as a array with length of 0"
);
sandbox.restore();
});
add_task(async
function test_isEnabled_tiles() {
let sandbox = sinon.createSandbox();
sandbox.stub(AdsFeed.prototype,
"PersistentCache" ).returns({
set: () => {},
get: () => {},
});
const dateNowTestValue =
1 ;
sandbox.stub(AdsFeed.prototype,
"Date" ).returns({
now: () => dateNowTestValue,
});
let feed = getAdsFeedForTest(sandbox);
feed.store.state.Prefs.values[PREF_UNIFIED_ADS_ADSFEED_ENABLED] =
true ;
feed.store.state.Prefs.values[PREF_UNIFIED_ADS_TILES_ENABLED] =
true ;
feed.store.state.Prefs.values[PREF_UNIFIED_ADS_SPOCS_ENABLED] =
true ;
feed.store.state.Prefs.values[PREF_FEED_TOPSITES] =
true ;
feed.store.state.Prefs.values[PREF_SYSTEM_TOPSITES] =
true ;
feed.store.state.Prefs.values[PREF_SHOW_SPONSORED_TOPSITES] =
true ;
feed.store.state.Prefs.values[PREF_FEED_SECTIONS_TOPSTORIES] =
true ;
feed.store.state.Prefs.values[PREF_SYSTEM_TOPSTORIES] =
true ;
feed.store.state.Prefs.values[PREF_SHOW_SPONSORED] =
true ;
feed.store.state.Prefs.values[PREF_SYSTEM_SHOW_SPONSORED] =
true ;
Assert .ok(feed.isEnabled());
sandbox.restore();
});
add_task(async
function test_onAction_INIT_tiles_and_spocs() {
let sandbox = sinon.createSandbox();
sandbox.stub(AdsFeed.prototype,
"PersistentCache" ).returns({
set: () => {},
get: () => {},
});
const dateNowTestValue =
1 ;
sandbox.stub(AdsFeed.prototype,
"Date" ).returns({
now: () => dateNowTestValue,
});
let feed = getAdsFeedForTest(sandbox);
feed.store.state.Prefs.values[PREF_UNIFIED_ADS_ADSFEED_ENABLED] =
true ;
feed.store.state.Prefs.values[PREF_UNIFIED_ADS_TILES_ENABLED] =
true ;
feed.store.state.Prefs.values[PREF_UNIFIED_ADS_SPOCS_ENABLED] =
true ;
sandbox.stub(feed,
"isEnabled" ).returns(
true );
sandbox.stub(feed,
"fetchData" ).returns({
tiles: mockedTileData,
spocs: mockedSpocsData,
spocPlacements: mockedSpocPlacements,
lastUpdated: dateNowTestValue,
});
info(
"AdsFeed.onAction INIT should initialize Ads" );
await feed.onAction({
type: actionTypes.INIT,
});
Assert .ok(feed.store.dispatch.calledTwice);
Assert .ok(
feed.store.dispatch.firstCall.calledWithExactly(
actionCreators.BroadcastToContent({
type:
"ADS_UPDATE_TILES" ,
data: {
tiles: mockedTileData,
},
meta: {
isStartup:
true ,
},
})
)
);
Assert .ok(
feed.store.dispatch.secondCall.calledWithExactly(
actionCreators.BroadcastToContent({
type:
"ADS_UPDATE_SPOCS" ,
data: {
spocs: mockedSpocsData,
spocPlacements: mockedSpocPlacements,
},
meta: {
isStartup:
true ,
},
})
)
);
sandbox.restore();
});
add_task(async
function test_onAction_INIT_spocs_only() {
let sandbox = sinon.createSandbox();
sandbox.stub(AdsFeed.prototype,
"PersistentCache" ).returns({
set: () => {},
get: () => {},
});
const dateNowTestValue =
1 ;
sandbox.stub(AdsFeed.prototype,
"Date" ).returns({
now: () => dateNowTestValue,
});
let feed = getAdsFeedForTest(sandbox);
feed.store.state.Prefs.values[PREF_UNIFIED_ADS_ADSFEED_ENABLED] =
true ;
feed.store.state.Prefs.values[PREF_UNIFIED_ADS_SPOCS_ENABLED] =
true ;
sandbox.stub(feed,
"isEnabled" ).returns(
true );
sandbox.stub(feed,
"fetchData" ).returns({
tiles: [],
spocs: mockedSpocsData,
spocPlacements: mockedSpocPlacements,
lastUpdated: dateNowTestValue,
});
info(
"AdsFeed.onAction INIT should return only spocs when tiles are disabled"
);
await feed.onAction({
type: actionTypes.INIT,
});
Assert .ok(feed.store.dispatch.calledOnce);
Assert .ok(
feed.store.dispatch.calledWithExactly(
actionCreators.BroadcastToContent({
type:
"ADS_UPDATE_SPOCS" ,
data: {
spocs: mockedSpocsData,
spocPlacements: mockedSpocPlacements,
},
meta: {
isStartup:
true ,
},
})
)
);
sandbox.restore();
});
add_task(async
function test_onAction_INIT_tiles_only() {
let sandbox = sinon.createSandbox();
sandbox.stub(AdsFeed.prototype,
"PersistentCache" ).returns({
set: () => {},
get: () => {},
});
const dateNowTestValue =
1 ;
sandbox.stub(AdsFeed.prototype,
"Date" ).returns({
now: () => dateNowTestValue,
});
let feed = getAdsFeedForTest(sandbox);
feed.store.state.Prefs.values[PREF_UNIFIED_ADS_ADSFEED_ENABLED] =
true ;
feed.store.state.Prefs.values[PREF_UNIFIED_ADS_TILES_ENABLED] =
true ;
sandbox.stub(feed,
"isEnabled" ).returns(
true );
sandbox.stub(feed,
"fetchData" ).returns({
tiles: mockedTileData,
spocs: [],
spocPlacements: [],
lastUpdated: dateNowTestValue,
});
info(
"AdsFeed.onAction INIT should return only tiles when spocs are disabled"
);
await feed.onAction({
type: actionTypes.INIT,
});
Assert .ok(feed.store.dispatch.calledOnce);
Assert .ok(
feed.store.dispatch.calledWithExactly(
actionCreators.BroadcastToContent({
type:
"ADS_UPDATE_TILES" ,
data: {
tiles: mockedTileData,
},
meta: {
isStartup:
true ,
},
})
)
);
sandbox.restore();
});
add_task(async
function test_fetchData_noOHTTP() {
const sandbox = sinon.createSandbox();
const feed = getAdsFeedForTest();
sandbox
.stub(AdsFeed.prototype,
"PersistentCache" )
.returns({ get: () => {}, set: () => {} });
sandbox.stub(feed,
"fetch" ).resolves(
new Response(
JSON.stringify({
tile1: [
{
block_key:
"foo" ,
name:
"bar" ,
url:
"https://test.com ",
image_url:
"image.png" ,
callbacks: { click:
"click" , impression:
"impression" },
},
],
})
)
);
sandbox.stub(feed,
"Date" ).returns({ now: () =>
123 });
// Simulate OHTTP being disabled
Services.prefs.setBoolPref(PREF_UNIFIED_ADS_OHTTP_ENABLED,
false );
const supportedAdTypes = { tiles:
true , spocs:
false };
await feed.fetchData(supportedAdTypes);
info(
"AdsFeed: fetchData() should fetch normally via HTTP" );
Assert .ok(feed.fetch.calledOnce,
"Fallback fetch called" );
sandbox.restore();
});
add_task(async
function test_fetchData_OHTTP() {
const sandbox = sinon.createSandbox();
const CONTEXT_ID =
"ContextId" ;
sandbox.stub(ContextId,
"request" ).returns(CONTEXT_ID);
const feed = getAdsFeedForTest();
Services.prefs.setBoolPref(PREF_UNIFIED_ADS_OHTTP_ENABLED,
true );
Services.prefs.setStringPref(
PREF_UNIFIED_ADS_OHTTP_RELAY_URL,
"https://relay.test "
);
Services.prefs.setStringPref(
PREF_UNIFIED_ADS_OHTTP_CONFIG_URL,
"https://config.test "
);
const mockConfig = { config:
"mocked" };
sandbox
.stub(AdsFeed.prototype,
"PersistentCache" )
.returns({ get: () => {}, set: () => {} });
sandbox.stub(feed,
"Date" ).returns({ now: () =>
123 });
sandbox.stub(ObliviousHTTP,
"getOHTTPConfig" ).resolves(mockConfig);
sandbox.stub(ObliviousHTTP,
"ohttpRequest" ).resolves({
status:
200 ,
json: () => {
return Promise.resolve(mockedFetchTileData);
},
});
const result = await feed.fetchData({ tiles:
true , spocs:
false });
info(
"AdsFeed: fetchData() should fetch via OHTTP when enabled" );
Assert .ok(ObliviousHTTP.getOHTTPConfig.calledOnce);
Assert .ok(ObliviousHTTP.ohttpRequest.calledOnce);
Assert .deepEqual(result.tiles[
0 ].id,
"test1" );
info(
"AdsFeed: fetchData() should not send cookies" );
Assert .equal(
ObliviousHTTP.ohttpRequest.firstCall.args[
3 ].credentials,
"omit" ,
"should not send cookies"
);
info(
"AdsFeed: fetchData() should construct request body" );
Assert .equal(
ObliviousHTTP.ohttpRequest.firstCall.args[
3 ].body,
JSON.stringify({
context_id: CONTEXT_ID,
flags: {},
placements: [
{
placement:
"newtab_tile_1" ,
count:
1 ,
},
{
placement:
"newtab_tile_2" ,
count:
1 ,
},
{
placement:
"newtab_tile_3" ,
count:
1 ,
},
],
blocks: [
"" ],
})
);
sandbox.restore();
});
add_task(async
function test_fetchData_OHTTP_with_adsBackendConfig() {
const sandbox = sinon.createSandbox();
const CONTEXT_ID =
"ContextId" ;
sandbox.stub(ContextId,
"request" ).returns(CONTEXT_ID);
const feed = getAdsFeedForTest();
Services.prefs.setBoolPref(PREF_UNIFIED_ADS_OHTTP_ENABLED,
true );
Services.prefs.setStringPref(
PREF_UNIFIED_ADS_OHTTP_RELAY_URL,
"https://relay.test "
);
Services.prefs.setStringPref(
PREF_UNIFIED_ADS_OHTTP_CONFIG_URL,
"https://config.test "
);
feed.store.state.Prefs.values.adsBackendConfig = {
feature1:
false ,
feature2:
true ,
};
const mockConfig = { config:
"mocked" };
sandbox
.stub(AdsFeed.prototype,
"PersistentCache" )
.returns({ get: () => {}, set: () => {} });
sandbox.stub(feed,
"Date" ).returns({ now: () =>
123 });
sandbox.stub(ObliviousHTTP,
"getOHTTPConfig" ).resolves(mockConfig);
sandbox.stub(ObliviousHTTP,
"ohttpRequest" ).resolves({
status:
200 ,
json: () => {
return Promise.resolve(mockedFetchTileData);
},
});
const result = await feed.fetchData({ tiles:
true , spocs:
false });
info(
"AdsFeed: fetchData() should fetch via OHTTP when enabled" );
Assert .ok(ObliviousHTTP.getOHTTPConfig.calledOnce);
Assert .ok(ObliviousHTTP.ohttpRequest.calledOnce);
Assert .deepEqual(result.tiles[
0 ].id,
"test1" );
info(
"AdsFeed: fetchData() should not send cookies" );
Assert .equal(
ObliviousHTTP.ohttpRequest.firstCall.args[
3 ].credentials,
"omit" ,
"should not send cookies"
);
info(
"AdsFeed: fetchData() should construct request body" );
Assert .equal(
ObliviousHTTP.ohttpRequest.firstCall.args[
3 ].body,
JSON.stringify({
context_id: CONTEXT_ID,
flags: {
feature1:
false ,
feature2:
true ,
},
placements: [
{
placement:
"newtab_tile_1" ,
count:
1 ,
},
{
placement:
"newtab_tile_2" ,
count:
1 ,
},
{
placement:
"newtab_tile_3" ,
count:
1 ,
},
],
blocks: [
"" ],
})
);
sandbox.restore();
});
Messung V0.5 in Prozent C=94 H=98 G=95
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.1Bemerkung:
(vorverarbeitet am 2026-10-11)
¤
*© Formatika GbR, Deutschland