Bug 1918442 - Add tests for list feed cards r=home-newtab-reviewers,amy

Differential Revision: https://phabricator.services.mozilla.com/D223607
This commit is contained in:
Nathan Barrett 2024-09-25 17:50:23 +00:00
parent a948eb9576
commit 08ef5afc9e
7 changed files with 233 additions and 79 deletions

View File

@ -89,6 +89,7 @@ export const DefaultMeta = ({
mayHaveThumbsUpDown,
onThumbsUpClick,
onThumbsDownClick,
isListCard,
state,
}) => (
<div className="meta">
@ -106,7 +107,7 @@ export const DefaultMeta = ({
<header className="title clamp">{title}</header>
{excerpt && <p className="excerpt clamp">{excerpt}</p>}
</div>
{mayHaveThumbsUpDown && (
{!isListCard && mayHaveThumbsUpDown && (
<DSThumbsUpDownButtons
onThumbsDownClick={onThumbsDownClick}
onThumbsUpClick={onThumbsUpClick}

View File

@ -19,69 +19,71 @@ function ListFeed({ type, firstVisibleTimestamp, recs }) {
// determine if the list should take up all availible height or not
const fullList = listLength >= 5;
return (
<div
className={`list-feed ${fullList ? "full-height" : ""} ${
listLength > 2 ? "span-2" : "span-1"
}`}
>
<div className="list-feed-inner-wrapper">
<h1 className="list-feed-title" id="list-feed-title">
<span className="icon icon-trending"></span>
{listFeedTitle}
</h1>
<div
className="list-feed-content"
role="menu"
aria-labelledby="list-feed-title"
>
{listFeedRecs.map((rec, index) => {
if (!rec || rec.placeholder) {
listLength > 0 && (
<div
className={`list-feed ${fullList ? "full-height" : ""} ${
listLength > 2 ? "span-2" : "span-1"
}`}
>
<div className="list-feed-inner-wrapper">
<h1 className="list-feed-title" id="list-feed-title">
<span className="icon icon-trending"></span>
{listFeedTitle}
</h1>
<div
className="list-feed-content"
role="menu"
aria-labelledby="list-feed-title"
>
{listFeedRecs.map((rec, index) => {
if (!rec || rec.placeholder) {
return (
<DSCard
key={`list-card-${index}`}
placeholder={true}
isListCard={true}
/>
);
}
return (
<DSCard
key={`list-card-${index}`}
placeholder={true}
pos={rec.pos}
flightId={rec.flight_id}
image_src={rec.image_src}
raw_image_src={rec.raw_image_src}
word_count={rec.word_count}
time_to_read={rec.time_to_read}
title={rec.title}
topic={rec.topic}
excerpt={rec.excerpt}
url={rec.url}
id={rec.id}
shim={rec.shim}
fetchTimestamp={rec.fetchTimestamp}
type={type}
context={rec.context}
sponsor={rec.sponsor}
sponsored_by_override={rec.sponsored_by_override}
dispatch={dispatch}
source={rec.domain}
publisher={rec.publisher}
pocket_id={rec.pocket_id}
context_type={rec.context_type}
bookmarkGuid={rec.bookmarkGuid}
recommendation_id={rec.recommendation_id}
firstVisibleTimestamp={firstVisibleTimestamp}
scheduled_corpus_item_id={rec.scheduled_corpus_item_id}
recommended_at={rec.recommended_at}
received_rank={rec.received_rank}
isListCard={true}
/>
);
}
return (
<DSCard
key={`list-card-${rec.id}`}
pos={rec.pos}
flightId={rec.flight_id}
image_src={rec.image_src}
raw_image_src={rec.raw_image_src}
word_count={rec.word_count}
time_to_read={rec.time_to_read}
title={rec.title}
topic={rec.topic}
excerpt={rec.excerpt}
url={rec.url}
id={rec.id}
shim={rec.shim}
fetchTimestamp={rec.fetchTimestamp}
type={type}
context={rec.context}
sponsor={rec.sponsor}
sponsored_by_override={rec.sponsored_by_override}
dispatch={dispatch}
source={rec.domain}
publisher={rec.publisher}
pocket_id={rec.pocket_id}
context_type={rec.context_type}
bookmarkGuid={rec.bookmarkGuid}
recommendation_id={rec.recommendation_id}
firstVisibleTimestamp={firstVisibleTimestamp}
scheduled_corpus_item_id={rec.scheduled_corpus_item_id}
recommended_at={rec.recommended_at}
received_rank={rec.received_rank}
isListCard={true}
/>
);
})}
})}
</div>
</div>
</div>
</div>
)
);
}

View File

@ -2856,6 +2856,7 @@ const DefaultMeta = ({
mayHaveThumbsUpDown,
onThumbsUpClick,
onThumbsDownClick,
isListCard,
state
}) => /*#__PURE__*/external_React_default().createElement("div", {
className: "meta"
@ -2872,7 +2873,7 @@ const DefaultMeta = ({
className: "title clamp"
}, title), excerpt && /*#__PURE__*/external_React_default().createElement("p", {
className: "excerpt clamp"
}, excerpt)), mayHaveThumbsUpDown && /*#__PURE__*/external_React_default().createElement(DSThumbsUpDownButtons, {
}, excerpt)), !isListCard && mayHaveThumbsUpDown && /*#__PURE__*/external_React_default().createElement(DSThumbsUpDownButtons, {
onThumbsDownClick: onThumbsDownClick,
onThumbsUpClick: onThumbsUpClick,
sponsor: sponsor,
@ -3675,7 +3676,7 @@ function ListFeed({
} = listFeedRecs;
// determine if the list should take up all availible height or not
const fullList = listLength >= 5;
return /*#__PURE__*/external_React_default().createElement("div", {
return listLength > 0 && /*#__PURE__*/external_React_default().createElement("div", {
className: `list-feed ${fullList ? "full-height" : ""} ${listLength > 2 ? "span-2" : "span-1"}`
}, /*#__PURE__*/external_React_default().createElement("div", {
className: "list-feed-inner-wrapper"
@ -3697,7 +3698,7 @@ function ListFeed({
});
}
return /*#__PURE__*/external_React_default().createElement(DSCard, {
key: `list-card-${rec.id}`,
key: `list-card-${index}`,
pos: rec.pos,
flightId: rec.flight_id,
image_src: rec.image_src,

View File

@ -197,26 +197,6 @@ module.exports = function (config) {
functions: 100,
branches: 74.63,
},
/**
* ListFeed.jsx tests will be added in #1907372
*/
"content-src/components/DiscoveryStreamComponents/ListFeed/ListFeed.jsx":
{
statements: 0,
lines: 0,
functions: 0,
branches: 0,
},
/**
* CardGrid tests will be updated in #1907372
*/
"content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx":
{
statements: 90.48,
lines: 90.48,
functions: 84,
branches: 68.75,
},
"content-src/components/DiscoveryStreamComponents/**/*.jsx": {
statements: 90.48,
lines: 90.48,

View File

@ -126,6 +126,41 @@ describe("<CardGrid>", () => {
assert.ok(wrapper.find(TopicsWidget).exists());
});
it("should create a list feed", () => {
const commonProps = {
essentialReadsHeader: true,
editorsPicksHeader: true,
items: 12,
data: {
recommendations: [
{ feedName: "foo" },
{ feedName: "foo" },
{ feedName: "foo" },
{ feedName: "foo" },
{ feedName: "foo" },
{ feedName: "foo" },
],
},
Prefs: {
...INITIAL_STATE.Prefs,
values: {
...INITIAL_STATE.Prefs.values,
"discoverystream.contextualContent.enabled": true,
"discoverystream.contextualContent.selectedFeed": "foo",
},
},
DiscoveryStream: INITIAL_STATE.DiscoveryStream,
};
wrapper = mount(
<WrapWithProvider>
<CardGrid {...commonProps} />
</WrapWithProvider>
);
assert.ok(wrapper.find(".list-feed").exists());
});
});
// Build IntersectionObserver class with the arg `entries` for the intersect callback.

View File

@ -763,6 +763,50 @@ describe("<PlaceholderDSCard> component", () => {
});
});
describe("Listfeed <DSCard />", () => {
let wrapper;
let sandbox;
let dispatch;
beforeEach(() => {
sandbox = sinon.createSandbox();
dispatch = sandbox.stub();
wrapper = shallow(
<DSCard dispatch={dispatch} {...DEFAULT_PROPS} isListFeed={true} />
);
wrapper.setState({ isSeen: true });
});
afterEach(() => {
sandbox.restore();
});
it("should not show save to pocket UI", () => {
wrapper.setState({ saveToPocketCard: true });
let stpButton = wrapper.find(".card-stp-button");
assert.ok(!stpButton.exists());
});
it("should not render thumbs up/down UI", () => {
wrapper.setState({ mayHaveThumbsUpDown: true });
const thumbs_up_down_buttons_component = wrapper.find(
DSThumbsUpDownButtons
);
const thumbs_up_down_buttons = thumbs_up_down_buttons_component.find(
".card-stp-thumbs-buttons"
);
assert.ok(!thumbs_up_down_buttons.exists());
});
it("should not render the excerpt UI", () => {
const excerpt_element = wrapper.find(".excerpt");
assert.ok(!excerpt_element.exists());
});
});
describe("<DSSource> component", () => {
it("should return a default source without compact", () => {
const wrapper = shallow(<DSSource source="Mozilla" />);

View File

@ -0,0 +1,91 @@
import { mount } from "enzyme";
import { INITIAL_STATE, reducers } from "common/Reducers.sys.mjs";
import { ListFeed } from "content-src/components/DiscoveryStreamComponents/ListFeed/ListFeed";
import { combineReducers, createStore } from "redux";
import { Provider } from "react-redux";
import React from "react";
import { DSCard } from "../../../../../content-src/components/DiscoveryStreamComponents/DSCard/DSCard";
const DEFAULT_PROPS = {
type: "foo",
firstVisibleTimestamp: new Date("March 21, 2024 10:11:12").getTime(),
recs: [{}, {}, {}],
};
// Wrap this around any component that uses useSelector,
// or any mount that uses a child that uses redux.
function WrapWithProvider({ children, state = INITIAL_STATE }) {
let store = createStore(combineReducers(reducers), state);
return <Provider store={store}>{children}</Provider>;
}
describe("Discovery Stream <ListFeed>", () => {
let wrapper;
let sandbox;
let dispatch;
beforeEach(() => {
sandbox = sinon.createSandbox();
dispatch = sandbox.stub();
wrapper = mount(
<WrapWithProvider>
<ListFeed dispatch={dispatch} {...DEFAULT_PROPS} />
</WrapWithProvider>
);
});
afterEach(() => {
sandbox.restore();
});
it("should render", () => {
assert.ok(wrapper.exists());
assert.ok(wrapper.find(".list-feed").exists());
});
it("should not render if rec prop is an empty array", () => {
wrapper = mount(
<WrapWithProvider>
<ListFeed dispatch={dispatch} {...DEFAULT_PROPS} recs={[]} />
</WrapWithProvider>
);
assert.ok(!wrapper.find(".list-feed").exists());
});
it("should render a maximum of 5 cards", () => {
wrapper = mount(
<WrapWithProvider>
<ListFeed
dispatch={dispatch}
{...DEFAULT_PROPS}
recs={[{}, {}, {}, {}, {}, {}, {}]}
/>
</WrapWithProvider>
);
assert.lengthOf(wrapper.find(DSCard), 5);
});
it("should render placeholder cards if `rec` is undefined or `rec.placeholder` is true", () => {
wrapper = mount(
<WrapWithProvider>
<ListFeed
dispatch={dispatch}
type={"foo"}
firstVisibleTimestamp={new Date("March 21, 2024 10:11:12").getTime()}
recs={[
{ placeholder: true },
{ placeholder: true },
{ placeholder: true },
{ placeholder: true },
{ placeholder: true },
{ placeholder: true },
]}
/>
</WrapWithProvider>
);
assert.ok(wrapper.find(".list-card-placeholder").exists());
assert.lengthOf(wrapper.find(".list-card-placeholder"), 5);
});
});