Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test runs only with pattern: os != 'android'
- Manifest: browser/extensions/newtab/test/xpcshell/xpcshell.toml
/* Any copyright is dedicated to the Public Domain.
"use strict";
ChromeUtils.defineESModuleGetters(this, {
actionCreators: "resource://newtab/common/Actions.mjs",
actionTypes: "resource://newtab/common/Actions.mjs",
sinon: "resource://testing-common/Sinon.sys.mjs",
SectionsLayoutFeed: "resource://newtab/lib/SectionsLayoutFeed.sys.mjs",
});
const PREF_CLIENT_LAYOUT_ENABLED =
"discoverystream.sections.clientLayout.enabled";
const PREF_TOPSTORIES_ENABLED = "feeds.section.topstories";
const VALID_RECORD = {
name: "7-double-row-2-ad",
responsiveLayouts: [
{ columnCount: 1, tiles: [] },
{ columnCount: 2, tiles: [] },
{ columnCount: 3, tiles: [] },
{ columnCount: 4, tiles: [] },
],
};
function getFeedForTest({
clientLayoutEnabled = true,
topStoriesEnabled = true,
} = {}) {
let feed = new SectionsLayoutFeed();
feed.store = {
dispatch: sinon.spy(),
getState: () => ({
Prefs: {
values: {
[PREF_CLIENT_LAYOUT_ENABLED]: clientLayoutEnabled,
[PREF_TOPSTORIES_ENABLED]: topStoriesEnabled,
},
},
}),
};
return feed;
}
add_task(async function test_construction() {
let feed = new SectionsLayoutFeed();
info("SectionsLayoutFeed constructor should create initial values");
Assert.ok(feed, "Could construct a SectionsLayoutFeed");
Assert.strictEqual(feed._rsClient, null, "_rsClient is initialized as null");
});
add_task(async function test_onAction_INIT() {
let sandbox = sinon.createSandbox();
let feed = getFeedForTest();
sandbox.stub(feed, "RemoteSettings").returns({
get: () => [VALID_RECORD],
on: () => {},
});
info(
"SectionsLayoutFeed.onAction INIT should fetch layouts and dispatch SECTIONS_LAYOUT_UPDATE"
);
await feed.onAction({ type: actionTypes.INIT });
const matchingCall = feed.store.dispatch
.getCalls()
.find(call => call.args[0].type === actionTypes.SECTIONS_LAYOUT_UPDATE);
Assert.ok(matchingCall, "Expected a SECTIONS_LAYOUT_UPDATE dispatch call");
Assert.deepEqual(
matchingCall.args[0],
actionCreators.BroadcastToContent({
type: actionTypes.SECTIONS_LAYOUT_UPDATE,
data: { configs: { "7-double-row-2-ad": VALID_RECORD } },
meta: { isStartup: true },
})
);
sandbox.restore();
});
add_task(async function test_onAction_INIT_invalid_record() {
let sandbox = sinon.createSandbox();
let feed = getFeedForTest();
const invalidRecord = {
name: "incomplete-layout",
responsiveLayouts: [{ columnCount: 1, tiles: [] }],
};
sandbox.stub(feed, "RemoteSettings").returns({
get: () => [VALID_RECORD, invalidRecord],
on: () => {},
});
info(
"SectionsLayoutFeed should only dispatch valid records with all 4 column counts"
);
await feed.onAction({ type: actionTypes.INIT });
const matchingCall = feed.store.dispatch
.getCalls()
.find(call => call.args[0].type === actionTypes.SECTIONS_LAYOUT_UPDATE);
Assert.ok(matchingCall, "Expected a SECTIONS_LAYOUT_UPDATE dispatch call");
Assert.ok(
"7-double-row-2-ad" in matchingCall.args[0].data.configs,
"Valid record should be included"
);
Assert.ok(
!("incomplete-layout" in matchingCall.args[0].data.configs),
"Invalid record should be excluded"
);
sandbox.restore();
});
add_task(async function test_onAction_INIT_disabled() {
let sandbox = sinon.createSandbox();
let feed = getFeedForTest({ clientLayoutEnabled: false });
sandbox.stub(feed, "RemoteSettings");
info(
"SectionsLayoutFeed.onAction INIT should not fetch layouts when disabled"
);
await feed.onAction({ type: actionTypes.INIT });
Assert.ok(
!feed.RemoteSettings.called,
"RemoteSettings should not be called when disabled"
);
Assert.ok(
!feed.store.dispatch.called,
"dispatch should not be called when disabled"
);
sandbox.restore();
});
add_task(async function test_onAction_PREF_CHANGED() {
let sandbox = sinon.createSandbox();
let feed = getFeedForTest();
sandbox.stub(feed, "init");
info("SectionsLayoutFeed.onAction PREF_CHANGED should call init");
for (const name of [PREF_CLIENT_LAYOUT_ENABLED, PREF_TOPSTORIES_ENABLED]) {
await feed.onAction({
type: actionTypes.PREF_CHANGED,
data: { name },
});
}
Assert.equal(
feed.init.callCount,
2,
"init should be called for each pref change"
);
Assert.ok(
feed.init.alwaysCalledWith(false),
"init should always be called with isStartup=false"
);
sandbox.restore();
});
add_task(async function test_onAction_PREF_CHANGED_trainhopConfig_enabled() {
let sandbox = sinon.createSandbox();
let feed = getFeedForTest();
sandbox.stub(feed, "init");
info(
"SectionsLayoutFeed.onAction PREF_CHANGED should call init when trainhopConfig has clientLayout"
);
await feed.onAction({
type: actionTypes.PREF_CHANGED,
data: {
name: "trainhopConfig",
value: { clientLayout: { enabled: true } },
},
});
Assert.ok(
feed.init.calledOnce,
"init should be called when trainhopConfig has clientLayout"
);
sandbox.restore();
});