Source code

Revision control

Copy as Markdown

Other Tools

import { combineReducers, createStore } from "redux";
import { actionTypes as at } from "common/Actions.mjs";
import { GlobalOverrider } from "test/unit/utils";
import { reducers } from "common/Reducers.sys.mjs";
import { selectLayoutRender } from "content-src/lib/selectLayoutRender";
const FAKE_LAYOUT = [
{
width: 3,
components: [
{ type: "foo", feed: { url: "foo.com" }, properties: { items: 2 } },
],
},
];
const FAKE_FEEDS = {
"foo.com": { data: { recommendations: [{ id: "foo" }, { id: "bar" }] } },
};
describe("selectLayoutRender", () => {
let store;
let globals;
beforeEach(() => {
globals = new GlobalOverrider();
store = createStore(combineReducers(reducers));
});
afterEach(() => {
globals.restore();
});
it("should return an empty array given initial state", () => {
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
prefs: {},
rollCache: [],
});
assert.deepEqual(layoutRender, []);
});
it("should add .data property from feeds to each compontent in .layout", () => {
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: FAKE_LAYOUT },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: { feed: FAKE_FEEDS["foo.com"], url: "foo.com" },
});
store.dispatch({ type: at.DISCOVERY_STREAM_FEEDS_UPDATE });
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
});
assert.lengthOf(layoutRender, 1);
assert.propertyVal(layoutRender[0], "width", 3);
assert.deepEqual(layoutRender[0].components[0], {
type: "foo",
feed: { url: "foo.com" },
properties: { items: 2 },
data: {
recommendations: [
{ id: "foo", pos: 0 },
{ id: "bar", pos: 1 },
],
sections: [],
},
});
});
it("should return layout with placeholder data if feed doesn't have data", () => {
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: FAKE_LAYOUT },
});
store.dispatch({ type: at.DISCOVERY_STREAM_FEEDS_UPDATE });
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
});
assert.lengthOf(layoutRender, 1);
assert.propertyVal(layoutRender[0], "width", 3);
assert.deepEqual(layoutRender[0].components[0].data.recommendations, [
{ placeholder: true },
{ placeholder: true },
]);
});
it("should return layout with empty spocs data if feed isn't defined but spocs is", () => {
const fakeLayout = [
{
width: 3,
components: [{ type: "foo", spocs: { positions: [{ index: 2 }] } }],
},
];
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: fakeLayout },
});
store.dispatch({ type: at.DISCOVERY_STREAM_FEEDS_UPDATE });
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
});
assert.lengthOf(layoutRender, 1);
assert.propertyVal(layoutRender[0], "width", 3);
assert.deepEqual(layoutRender[0].components[0].data.spocs, []);
});
it("should return layout with spocs data if feed isn't defined but spocs is", () => {
const fakeLayout = [
{
width: 3,
components: [{ type: "foo", spocs: { positions: [{ index: 0 }] } }],
},
];
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: fakeLayout },
});
store.dispatch({ type: at.DISCOVERY_STREAM_FEEDS_UPDATE });
store.dispatch({
type: at.DISCOVERY_STREAM_SPOCS_UPDATE,
data: {
lastUpdated: 0,
spocs: {
newtab_spocs: {
items: [{ id: 1 }, { id: 2 }, { id: 3 }],
},
},
},
});
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
});
assert.lengthOf(layoutRender, 1);
assert.propertyVal(layoutRender[0], "width", 3);
assert.deepEqual(layoutRender[0].components[0].data.spocs, [
{ id: 1, pos: 0 },
{ id: 2, pos: 1 },
{ id: 3, pos: 2 },
]);
});
it("should return layout with no spocs data if feed and spocs are unavailable", () => {
const fakeLayout = [
{
width: 3,
components: [{ type: "foo", spocs: { positions: [{ index: 0 }] } }],
},
];
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: fakeLayout },
});
store.dispatch({ type: at.DISCOVERY_STREAM_FEEDS_UPDATE });
store.dispatch({
type: at.DISCOVERY_STREAM_SPOCS_UPDATE,
data: {
lastUpdated: 0,
spocs: {
spocs: {
items: [],
},
},
},
});
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
});
assert.lengthOf(layoutRender, 1);
assert.propertyVal(layoutRender[0], "width", 3);
assert.equal(layoutRender[0].components[0].data.spocs.length, 0);
});
it("should return feed data offset by layout set prop", () => {
const fakeLayout = [
{
width: 3,
components: [
{ type: "foo", properties: { offset: 1 }, feed: { url: "foo.com" } },
],
},
];
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: fakeLayout },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: { feed: FAKE_FEEDS["foo.com"], url: "foo.com" },
});
store.dispatch({ type: at.DISCOVERY_STREAM_FEEDS_UPDATE });
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
});
assert.deepEqual(layoutRender[0].components[0].data, {
recommendations: [{ id: "bar" }],
sections: [],
});
});
it("should return spoc result when there are more positions than spocs", () => {
const fakeSpocConfig = {
positions: [{ index: 0 }, { index: 1 }, { index: 2 }],
};
const fakeLayout = [
{
width: 3,
components: [
{ type: "foo", feed: { url: "foo.com" }, spocs: fakeSpocConfig },
],
},
];
const fakeSpocsData = {
lastUpdated: 0,
spocs: { newtab_spocs: { items: ["fooSpoc", "barSpoc"] } },
};
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: fakeLayout },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: { feed: FAKE_FEEDS["foo.com"], url: "foo.com" },
});
store.dispatch({ type: at.DISCOVERY_STREAM_FEEDS_UPDATE });
store.dispatch({
type: at.DISCOVERY_STREAM_SPOCS_UPDATE,
data: fakeSpocsData,
});
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
});
assert.lengthOf(layoutRender, 1);
assert.deepEqual(
layoutRender[0].components[0].data.recommendations[0],
"fooSpoc"
);
assert.deepEqual(
layoutRender[0].components[0].data.recommendations[1],
"barSpoc"
);
assert.deepEqual(layoutRender[0].components[0].data.recommendations[2], {
id: "foo",
});
assert.deepEqual(layoutRender[0].components[0].data.recommendations[3], {
id: "bar",
});
});
it("should return a layout with feeds of items length with positions", () => {
const fakeLayout = [
{
width: 3,
components: [
{ type: "foo", properties: { items: 3 }, feed: { url: "foo.com" } },
],
},
];
const fakeRecommendations = [
{ name: "item1" },
{ name: "item2" },
{ name: "item3" },
{ name: "item4" },
];
const fakeFeeds = {
"foo.com": { data: { recommendations: fakeRecommendations } },
};
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: fakeLayout },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: { feed: fakeFeeds["foo.com"], url: "foo.com" },
});
store.dispatch({ type: at.DISCOVERY_STREAM_FEEDS_UPDATE });
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
});
const { recommendations } = layoutRender[0].components[0].data;
assert.equal(recommendations.length, 4);
assert.equal(recommendations[0].pos, 0);
assert.equal(recommendations[1].pos, 1);
assert.equal(recommendations[2].pos, 2);
assert.equal(recommendations[3].pos, undefined);
});
it("should render everything if everything is ready", () => {
const fakeLayout = [
{
width: 3,
components: [
{ type: "foo1" },
{ type: "foo2", properties: { items: 3 }, feed: { url: "foo2.com" } },
{ type: "foo3", properties: { items: 3 }, feed: { url: "foo3.com" } },
{ type: "foo4", properties: { items: 3 }, feed: { url: "foo4.com" } },
{ type: "foo5" },
],
},
];
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: fakeLayout },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: { feed: { data: { recommendations: [] } }, url: "foo2.com" },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: { feed: { data: { recommendations: [] } }, url: "foo3.com" },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: { feed: { data: { recommendations: [] } }, url: "foo4.com" },
});
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
});
assert.equal(layoutRender[0].components[0].type, "foo1");
assert.equal(layoutRender[0].components[1].type, "foo2");
assert.equal(layoutRender[0].components[2].type, "foo3");
assert.equal(layoutRender[0].components[3].type, "foo4");
assert.equal(layoutRender[0].components[4].type, "foo5");
});
it("should stop rendering feeds if we hit a not ready spoc", () => {
const fakeLayout = [
{
width: 3,
components: [
{ type: "foo1" },
{ type: "foo2", properties: { items: 3 }, feed: { url: "foo2.com" } },
{
type: "foo3",
properties: { items: 3 },
feed: { url: "foo3.com" },
spocs: { positions: [{ index: 0 }] },
},
{ type: "foo4", properties: { items: 3 }, feed: { url: "foo4.com" } },
{ type: "foo5" },
],
},
];
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: fakeLayout },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: { feed: { data: { recommendations: [] } }, url: "foo2.com" },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: { feed: { data: { recommendations: [] } }, url: "foo3.com" },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: { feed: { data: { recommendations: [] } }, url: "foo4.com" },
});
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
});
assert.equal(layoutRender[0].components[0].type, "foo1");
assert.equal(layoutRender[0].components[1].type, "foo2");
assert.deepEqual(layoutRender[0].components[2].data.recommendations, [
{ placeholder: true },
{ placeholder: true },
{ placeholder: true },
]);
});
it("should not render a spoc if there are no available spocs", () => {
const fakeLayout = [
{
width: 3,
components: [
{ type: "foo1" },
{ type: "foo2", properties: { items: 3 }, feed: { url: "foo2.com" } },
{
type: "foo3",
properties: { items: 3 },
feed: { url: "foo3.com" },
spocs: { positions: [{ index: 0 }] },
},
{ type: "foo4", properties: { items: 3 }, feed: { url: "foo4.com" } },
{ type: "foo5" },
],
},
];
const fakeSpocsData = { lastUpdated: 0, spocs: { spocs: [] } };
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: fakeLayout },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: { feed: { data: { recommendations: [] } }, url: "foo2.com" },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: {
feed: { data: { recommendations: [{ name: "rec" }] } },
url: "foo3.com",
},
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: { feed: { data: { recommendations: [] } }, url: "foo4.com" },
});
store.dispatch({
type: at.DISCOVERY_STREAM_SPOCS_UPDATE,
data: fakeSpocsData,
});
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
});
assert.deepEqual(layoutRender[0].components[2].data.recommendations[0], {
name: "rec",
pos: 0,
});
});
it("should not render a row if no components exist after filter in that row", () => {
const fakeLayout = [
{
width: 3,
components: [{ type: "TopSites" }],
},
{
width: 3,
components: [{ type: "Message" }],
},
];
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: fakeLayout },
});
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
prefs: { "feeds.topsites": true },
});
assert.equal(layoutRender[0].components[0].type, "TopSites");
assert.equal(layoutRender[1], undefined);
});
it("should not render a component if filtered", () => {
const fakeLayout = [
{
width: 3,
components: [{ type: "Message" }, { type: "TopSites" }],
},
];
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: fakeLayout },
});
const { layoutRender } = selectLayoutRender({
state: store.getState().DiscoveryStream,
prefs: { "feeds.topsites": true },
});
assert.equal(layoutRender[0].components[0].type, "TopSites");
assert.equal(layoutRender[0].components[1], undefined);
});
it("should skip rendering a spoc in position if that spoc is blocked for that session", () => {
const fakeLayout = [
{
width: 3,
components: [
{
type: "foo1",
properties: { items: 3 },
feed: { url: "foo1.com" },
spocs: { positions: [{ index: 0 }] },
},
],
},
];
const fakeSpocsData = {
lastUpdated: 0,
spocs: {
newtab_spocs: { items: [{ name: "spoc", url: "https://foo.com" }] },
},
};
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: fakeLayout },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: {
feed: { data: { recommendations: [{ name: "rec" }] } },
url: "foo1.com",
},
});
store.dispatch({
type: at.DISCOVERY_STREAM_SPOCS_UPDATE,
data: fakeSpocsData,
});
const { layoutRender: layout1 } = selectLayoutRender({
state: store.getState().DiscoveryStream,
});
store.dispatch({
type: at.DISCOVERY_STREAM_SPOC_BLOCKED,
data: { url: "https://foo.com" },
});
const { layoutRender: layout2 } = selectLayoutRender({
state: store.getState().DiscoveryStream,
});
assert.deepEqual(layout1[0].components[0].data.recommendations[0], {
name: "spoc",
url: "https://foo.com",
pos: 0,
});
assert.deepEqual(layout2[0].components[0].data.recommendations[0], {
name: "rec",
pos: 0,
});
});
});