Fix Bug 1514337 - Activity Stream layout pref and layout data for rapid experimentation (#4593)
* Fix Bug 1514337 - Activity Stream layout pref and layout data for rapid experimentation * lint * Adding tests a a few tweaks. * bug fix * removing use_layout pref * Updates to tests * test updates * deepEqual usage
This commit is contained in:
Родитель
d46b46f488
Коммит
325b45ae96
|
@ -33,6 +33,7 @@ for (const type of [
|
|||
"AS_ROUTER_TELEMETRY_USER_EVENT",
|
||||
"BLOCK_URL",
|
||||
"BOOKMARK_URL",
|
||||
"CONTENT_LAYOUT",
|
||||
"COPY_DOWNLOAD_LINK",
|
||||
"DELETE_BOOKMARK_BY_ID",
|
||||
"DELETE_FROM_POCKET",
|
||||
|
|
|
@ -47,6 +47,7 @@ const INITIAL_STATE = {
|
|||
pocketCta: {},
|
||||
waitingForSpoc: true,
|
||||
},
|
||||
Layout: [],
|
||||
};
|
||||
|
||||
function App(prevState = INITIAL_STATE.App, action) {
|
||||
|
@ -429,10 +430,19 @@ function Pocket(prevState = INITIAL_STATE.Pocket, action) {
|
|||
}
|
||||
}
|
||||
|
||||
function Layout(prevState = INITIAL_STATE.Layout, action) {
|
||||
switch (action.type) {
|
||||
case at.CONTENT_LAYOUT:
|
||||
return action.data;
|
||||
default:
|
||||
return prevState;
|
||||
}
|
||||
}
|
||||
|
||||
this.INITIAL_STATE = INITIAL_STATE;
|
||||
this.TOP_SITES_DEFAULT_ROWS = TOP_SITES_DEFAULT_ROWS;
|
||||
this.TOP_SITES_MAX_SITES_PER_ROW = TOP_SITES_MAX_SITES_PER_ROW;
|
||||
|
||||
this.reducers = {TopSites, App, ASRouter, Snippets, Prefs, Dialog, Sections, Pocket};
|
||||
this.reducers = {TopSites, App, ASRouter, Snippets, Prefs, Dialog, Sections, Pocket, Layout};
|
||||
|
||||
const EXPORTED_SYMBOLS = ["reducers", "INITIAL_STATE", "insertPinned", "TOP_SITES_DEFAULT_ROWS", "TOP_SITES_MAX_SITES_PER_ROW"];
|
||||
|
|
|
@ -315,4 +315,4 @@ export class _Sections extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
export const Sections = connect(state => ({Sections: state.Sections, Prefs: state.Prefs}))(_Sections);
|
||||
export const Sections = connect(state => ({Sections: state.Sections, Prefs: state.Prefs, Layout: state.Layout}))(_Sections);
|
||||
|
|
|
@ -112,6 +112,13 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
this.store.dispatch(shouldBroadcast ? ac.BroadcastToContent(action) : ac.AlsoToPreloaded(action));
|
||||
}
|
||||
|
||||
maybeDispatchLayoutUpdate(data, shouldBroadcast) {
|
||||
if (data && data.length) {
|
||||
const action = {type: at.CONTENT_LAYOUT, data};
|
||||
this.store.dispatch(shouldBroadcast ? ac.BroadcastToContent(action) : ac.AlsoToPreloaded(action));
|
||||
}
|
||||
}
|
||||
|
||||
doContentUpdate(shouldBroadcast) {
|
||||
let updateProps = {};
|
||||
if (this.stories) {
|
||||
|
@ -174,6 +181,7 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
}
|
||||
|
||||
const body = await response.json();
|
||||
this.maybeDispatchLayoutUpdate(body.layout);
|
||||
this.updateSettings(body.settings);
|
||||
this.stories = this.rotate(this.transform(body.recommendations));
|
||||
this.cleanUpTopRecImpressionPref();
|
||||
|
@ -194,7 +202,9 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
async loadCachedData() {
|
||||
const data = await this.cache.get();
|
||||
let stories = data.stories && data.stories.recommendations;
|
||||
let layout = data.stories && data.stories.layout;
|
||||
let topics = data.topics && data.topics.topics;
|
||||
this.maybeDispatchLayoutUpdate(layout);
|
||||
|
||||
let affinities = data.domainAffinities;
|
||||
if (this.personalized && affinities && affinities.scores) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {INITIAL_STATE, insertPinned, reducers} from "common/Reducers.jsm";
|
||||
const {TopSites, App, Snippets, Prefs, Dialog, Sections, Pocket} = reducers;
|
||||
const {TopSites, App, Snippets, Prefs, Dialog, Sections, Pocket, Layout} = reducers;
|
||||
import {actionTypes as at} from "common/Actions.jsm";
|
||||
|
||||
describe("Reducers", () => {
|
||||
|
@ -655,4 +655,13 @@ describe("Reducers", () => {
|
|||
assert.equal(state.pocketCta.useCta, data.use_cta);
|
||||
});
|
||||
});
|
||||
describe("Layout", () => {
|
||||
it("should return INITIAL_STATE by default", () => {
|
||||
assert.equal(Layout(undefined, {type: "some_action"}), INITIAL_STATE.Layout);
|
||||
});
|
||||
it("should set layout data with layout.type CONTENT_LAYOUT", () => {
|
||||
const state = Layout(undefined, {type: at.CONTENT_LAYOUT, data: ["test"]});
|
||||
assert.equal(state[0], "test");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1460,4 +1460,40 @@ describe("Top Stories Feed", () => {
|
|||
assert.calledOnce(instance.uninit);
|
||||
assert.calledOnce(instance.init);
|
||||
});
|
||||
describe("#layout", () => {
|
||||
it("should call maybeDispatchLayoutUpdate from fetchStories", async () => {
|
||||
instance.stories_endpoint = "stories-endpoint";
|
||||
let fetchStub = globals.sandbox.stub();
|
||||
|
||||
const response = {
|
||||
"layout": [1, 2],
|
||||
};
|
||||
globals.set("fetch", fetchStub);
|
||||
fetchStub.resolves({ok: true, status: 200, json: () => Promise.resolve(response)});
|
||||
sinon.spy(instance, "maybeDispatchLayoutUpdate");
|
||||
|
||||
await instance.fetchStories();
|
||||
assert.calledOnce(instance.maybeDispatchLayoutUpdate);
|
||||
assert.calledWith(instance.maybeDispatchLayoutUpdate, [1, 2]);
|
||||
});
|
||||
it("should call maybeDispatchLayoutUpdate from loadCachedData", async () => {
|
||||
sinon.spy(instance, "maybeDispatchLayoutUpdate");
|
||||
instance.cache.get = () => ({stories: {layout: [2, 3]}});
|
||||
|
||||
await instance.loadCachedData();
|
||||
assert.calledOnce(instance.maybeDispatchLayoutUpdate);
|
||||
assert.calledWith(instance.maybeDispatchLayoutUpdate, [2, 3]);
|
||||
});
|
||||
it("should call dispatch from maybeDispatchLayoutUpdate with available data", () => {
|
||||
instance.maybeDispatchLayoutUpdate([1, 2]);
|
||||
assert.calledOnce(instance.store.dispatch);
|
||||
const [action] = instance.store.dispatch.firstCall.args;
|
||||
assert.equal(action.type, "CONTENT_LAYOUT");
|
||||
assert.deepEqual(action.data, [1, 2]);
|
||||
});
|
||||
it("should not call dispatch from maybeDispatchLayoutUpdate with no available data", () => {
|
||||
instance.maybeDispatchLayoutUpdate([]);
|
||||
assert.notCalled(instance.store.dispatch);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче