/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import { render, act } from "@testing-library/react";
import { Provider } from "react-redux";
import { combineReducers, createStore } from "redux";
import { actionTypes as at, actionCreators as ac } from "common/Actions.mjs";
import { INITIAL_STATE, reducers } from "common/Reducers.sys.mjs";
import { WrapWithProvider } from "test/jest/test-utils";
import {
Base as ConnectedBase,
_Base as Base,
BaseContent,
} from "content-src/components/Base/Base";
const weatherSuggestion = {
current_conditions: {
icon_id: 3,
summary: "Partly Cloudy",
temperature: { c: 20, f: 68 },
},
forecast: {
high: { c: 25, f: 77 },
low: { c: 15, f: 59 },
url: "https://example.com",
},
};
describe("", () => {
it("should not render without App.initialized", () => {
const props = {
App: { initialized: false },
Prefs: { values: {} },
dispatch: jest.fn(),
};
const { container } = render(
);
expect(
container.querySelector(".base-content-fallback")
).not.toBeInTheDocument();
});
});
describe(" Nova startup layout stability", () => {
it("keeps the centered-logo layout stable while the small weather widget initializes", () => {
const store = createStore(combineReducers(reducers), {
...INITIAL_STATE,
App: {
...INITIAL_STATE.App,
initialized: true,
},
Prefs: {
...INITIAL_STATE.Prefs,
values: {
...INITIAL_STATE.Prefs.values,
"nova.enabled": true,
showWeather: true,
"widgets.enabled": true,
"widgets.system.enabled": true,
"widgets.system.weather.enabled": true,
"widgets.weather.enabled": true,
"widgets.weather.size": "small",
},
},
Weather: {
...INITIAL_STATE.Weather,
initialized: false,
locationData: { city: "Testville" },
},
});
const { container } = render(
);
expect(
container.querySelector(".container.nova-enabled.logo-in-content")
).toBeInTheDocument();
expect(
container.querySelector(
".sidebar-inline-start .logo-and-wordmark-wrapper"
)
).not.toBeInTheDocument();
expect(
container.querySelector(".sidebar-inline-end .weather-widget")
).not.toBeInTheDocument();
act(() => {
store.dispatch({
type: at.WEATHER_UPDATE,
data: {
suggestions: [weatherSuggestion],
hourlyForecasts: [],
lastUpdated: Date.now(),
locationData: { city: "Testville" },
},
});
});
expect(
container.querySelector(".container.nova-enabled.logo-in-content")
).toBeInTheDocument();
expect(
container.querySelector(
".sidebar-inline-start .logo-and-wordmark-wrapper"
)
).not.toBeInTheDocument();
expect(
container.querySelector(".sidebar-inline-end .weather-widget")
).toBeInTheDocument();
});
});
function renderNova(overrides = {}) {
const store = createStore(combineReducers(reducers), {
...INITIAL_STATE,
App: { ...INITIAL_STATE.App, initialized: true },
Prefs: {
...INITIAL_STATE.Prefs,
values: {
...INITIAL_STATE.Prefs.values,
"nova.enabled": true,
showSearch: false,
"feeds.topsites": false,
"feeds.section.topstories": false,
"feeds.system.topstories": false,
"feeds.section.highlights": false,
"widgets.enabled": false,
"widgets.system.enabled": false,
"widgets.system.weather.enabled": false,
"widgets.weather.enabled": false,
"widgets.system.lists.enabled": false,
"widgets.lists.enabled": false,
"widgets.system.focusTimer.enabled": false,
"widgets.focusTimer.enabled": false,
"widgets.system.clocks.enabled": false,
"widgets.clocks.enabled": false,
"widgets.system.sportsWidget.enabled": false,
"widgets.sportsWidget.enabled": false,
...overrides,
},
},
});
return render(
);
}
describe(" Nova hides Logo when no sections are enabled", () => {
it("does not render the Logo when every section is disabled", () => {
const { container } = renderNova();
expect(
container.querySelector(".logo-and-wordmark-wrapper")
).not.toBeInTheDocument();
});
it("renders the Logo when only search is enabled", () => {
const { container } = renderNova({ showSearch: true });
expect(
container.querySelector(".logo-and-wordmark-wrapper")
).toBeInTheDocument();
});
it("renders the Logo when only topsites is enabled", () => {
const { container } = renderNova({ "feeds.topsites": true });
expect(
container.querySelector(".logo-and-wordmark-wrapper")
).toBeInTheDocument();
});
it("renders the Logo when only Highlights (Recent Activity) is enabled", () => {
const { container } = renderNova({ "feeds.section.highlights": true });
expect(
container.querySelector(".logo-and-wordmark-wrapper")
).toBeInTheDocument();
});
it("renders the Logo when only Pocket is enabled", () => {
const { container } = renderNova({
"feeds.section.topstories": true,
"feeds.system.topstories": true,
});
expect(
container.querySelector(".logo-and-wordmark-wrapper")
).toBeInTheDocument();
});
it("renders the Logo when a content widget (lists) is enabled", () => {
const { container } = renderNova({
"widgets.enabled": true,
"widgets.system.enabled": true,
"widgets.system.lists.enabled": true,
"widgets.lists.enabled": true,
});
expect(
container.querySelector(".logo-and-wordmark-wrapper")
).toBeInTheDocument();
});
it("renders the Logo when a widget is enabled via trainhopConfig (system pref off)", () => {
const { container } = renderNova({
"widgets.enabled": true,
"widgets.system.enabled": true,
"widgets.system.lists.enabled": false,
"widgets.lists.enabled": true,
trainhopConfig: { widgets: { listsEnabled: true } },
});
expect(
container.querySelector(".logo-and-wordmark-wrapper")
).toBeInTheDocument();
});
it("hides the Logo when per-widget prefs are on but widgets.enabled is off", () => {
const { container } = renderNova({
"widgets.enabled": false,
"widgets.system.enabled": true,
"widgets.system.lists.enabled": true,
"widgets.lists.enabled": true,
"widgets.system.clocks.enabled": true,
"widgets.clocks.enabled": true,
"widgets.system.focusTimer.enabled": true,
"widgets.focusTimer.enabled": true,
"widgets.system.sportsWidget.enabled": true,
"widgets.sportsWidget.enabled": true,
});
expect(
container.querySelector(".logo-and-wordmark-wrapper")
).not.toBeInTheDocument();
});
it("centers the Logo in .content when widgets.enabled is off and only search is on", () => {
const { container } = renderNova({
showSearch: true,
"widgets.enabled": false,
"widgets.system.enabled": true,
"widgets.system.lists.enabled": true,
"widgets.lists.enabled": true,
"widgets.system.clocks.enabled": true,
"widgets.clocks.enabled": true,
"widgets.system.focusTimer.enabled": true,
"widgets.focusTimer.enabled": true,
"widgets.system.sportsWidget.enabled": true,
"widgets.sportsWidget.enabled": true,
});
expect(
container.querySelector(".container.nova-enabled.logo-in-content")
).toBeInTheDocument();
expect(
container.querySelector(".content .logo-and-wordmark-wrapper")
).toBeInTheDocument();
expect(
container.querySelector(
".sidebar-inline-start .logo-and-wordmark-wrapper"
)
).not.toBeInTheDocument();
});
it("renders the Logo when only the sidebar weather widget is enabled", () => {
const { container } = renderNova({
"widgets.enabled": true,
"widgets.system.enabled": true,
"widgets.system.weather.enabled": true,
"widgets.weather.enabled": true,
"widgets.weather.size": "small",
});
expect(
container.querySelector(".logo-and-wordmark-wrapper")
).toBeInTheDocument();
});
});
describe(" Nova logo placement with many topSitesRows", () => {
it("anchors the Logo to the sidebar when topSitesRows > 2 (3 rows)", () => {
const { container } = renderNova({
"feeds.topsites": true,
topSitesRows: 3,
});
expect(
container.querySelector(".container.nova-enabled.logo-in-content")
).not.toBeInTheDocument();
expect(
container.querySelector(
".sidebar-inline-start .logo-and-wordmark-wrapper"
)
).toBeInTheDocument();
expect(
container.querySelector(".content .logo-and-wordmark-wrapper")
).not.toBeInTheDocument();
});
it("anchors the Logo to the sidebar when topSitesRows > 2 (4 rows)", () => {
const { container } = renderNova({
"feeds.topsites": true,
topSitesRows: 4,
});
expect(
container.querySelector(
".sidebar-inline-start .logo-and-wordmark-wrapper"
)
).toBeInTheDocument();
});
it("centers the Logo when topSitesRows is 2", () => {
const { container } = renderNova({
"feeds.topsites": true,
topSitesRows: 2,
});
expect(
container.querySelector(".container.nova-enabled.logo-in-content")
).toBeInTheDocument();
expect(
container.querySelector(".content .logo-and-wordmark-wrapper")
).toBeInTheDocument();
});
it("centers the Logo when topSitesRows > 2 but topsites are disabled", () => {
const { container } = renderNova({
"feeds.topsites": false,
showSearch: true,
topSitesRows: 4,
});
expect(
container.querySelector(".container.nova-enabled.logo-in-content")
).toBeInTheDocument();
expect(
container.querySelector(".content .logo-and-wordmark-wrapper")
).toBeInTheDocument();
});
});
describe(" Nova hideLogo pref", () => {
it("renders the Logo by default (hideLogo unset, topsites enabled)", () => {
const { container } = renderNova({ "feeds.topsites": true });
expect(
container.querySelector(".logo-and-wordmark-wrapper")
).toBeInTheDocument();
});
it("hides the Logo when hideLogo is true (sidebar layout)", () => {
const { container } = renderNova({
"feeds.topsites": true,
hideLogo: true,
});
expect(
container.querySelector(".logo-and-wordmark-wrapper")
).not.toBeInTheDocument();
});
it("hides the Logo when hideLogo is true (centered layout)", () => {
const { container } = renderNova({ showSearch: true, hideLogo: true });
expect(
container.querySelector(".logo-and-wordmark-wrapper")
).not.toBeInTheDocument();
});
});
describe(" weather opt-in dialog trigger", () => {
function makeInstance(currentPrefs, dispatch = jest.fn()) {
const inst = Object.create(BaseContent.prototype);
inst.props = {
Prefs: { values: currentPrefs },
dispatch,
App: { isForStartupCache: {} },
DiscoveryStream: { spocs: {} },
};
inst.state = { visible: false };
inst.applyBodyClasses = jest.fn();
inst.spocsOnDemandUpdated = jest.fn();
inst.trackSpocPlaceholderDuration = jest.fn();
Object.defineProperty(inst, "isSpocsOnDemandExpired", { get: () => false });
return { inst, dispatch };
}
function makePrevProps(prefs) {
return {
Prefs: { values: prefs },
DiscoveryStream: { spocs: {} },
App: { isForStartupCache: {} },
};
}
it("triggers weather.optInDisplayed in classic mode when showWeather transitions to true", () => {
const { inst, dispatch } = makeInstance({
"nova.enabled": false,
showWeather: true,
});
inst.componentDidUpdate(
makePrevProps({ "nova.enabled": false, showWeather: false })
);
expect(dispatch).toHaveBeenCalledWith(
ac.SetPref("weather.optInDisplayed", true)
);
});
it("does not trigger weather.optInDisplayed in classic mode when showWeather was already true", () => {
const { inst, dispatch } = makeInstance({
"nova.enabled": false,
showWeather: true,
});
inst.componentDidUpdate(
makePrevProps({ "nova.enabled": false, showWeather: true })
);
expect(dispatch).not.toHaveBeenCalledWith(
ac.SetPref("weather.optInDisplayed", true)
);
});
it("triggers weather.optInDisplayed in Nova mode when widgets.weather.enabled transitions to true", () => {
const { inst, dispatch } = makeInstance({
"nova.enabled": true,
"widgets.weather.enabled": true,
});
inst.componentDidUpdate(
makePrevProps({ "nova.enabled": true, "widgets.weather.enabled": false })
);
expect(dispatch).toHaveBeenCalledWith(
ac.SetPref("weather.optInDisplayed", true)
);
});
it("does not trigger weather.optInDisplayed in Nova mode when only showWeather transitions to true", () => {
const { inst, dispatch } = makeInstance({
"nova.enabled": true,
"widgets.weather.enabled": true,
showWeather: true,
});
inst.componentDidUpdate(
makePrevProps({
"nova.enabled": true,
"widgets.weather.enabled": true,
showWeather: false,
})
);
expect(dispatch).not.toHaveBeenCalledWith(
ac.SetPref("weather.optInDisplayed", true)
);
});
});