1import {
2  _List as List,
3  ListItem,
4  PlaceholderListItem,
5} from "content-src/components/DiscoveryStreamComponents/List/List";
6import { actionCreators as ac } from "common/Actions.jsm";
7import {
8  DSContextFooter,
9  StatusMessage,
10} from "content-src/components/DiscoveryStreamComponents/DSContextFooter/DSContextFooter";
11import { DSEmptyState } from "content-src/components/DiscoveryStreamComponents/DSEmptyState/DSEmptyState";
12import { DSLinkMenu } from "content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu";
13import { GlobalOverrider } from "test/unit/utils";
14import React from "react";
15import { shallow } from "enzyme";
16
17describe("<List> presentation component", () => {
18  const ValidRecommendations = [
19    { url: 1 },
20    { url: 2 },
21    { context: "test spoc", url: 3 },
22  ];
23  const ValidListProps = {
24    data: {
25      recommendations: ValidRecommendations,
26    },
27    feed: {
28      url: "fakeFeedUrl",
29    },
30    header: {
31      title: "fakeFeedTitle",
32    },
33  };
34
35  it("should return null if feed.data is falsy", () => {
36    const ListProps = {
37      data: { feeds: { a: "stuff" } },
38    };
39
40    const wrapper = shallow(<List {...ListProps} />);
41    assert.isNull(wrapper.getElement());
42  });
43
44  it("should return Empty State for no recommendations", () => {
45    const ListProps = {
46      data: { recommendations: [] },
47      header: { title: "headerTitle" },
48    };
49
50    const wrapper = shallow(<List {...ListProps} />);
51    const dsEmptyState = wrapper.find(DSEmptyState);
52    const dsHeader = wrapper.find(".ds-header");
53    const dsList = wrapper.find(".ds-list.empty");
54
55    assert.ok(wrapper.exists());
56    assert.lengthOf(dsEmptyState, 1);
57    assert.lengthOf(dsHeader, 1);
58    assert.lengthOf(dsList, 1);
59  });
60
61  it("should return something containing a <ul> if props are valid", () => {
62    const wrapper = shallow(<List {...ValidListProps} />);
63
64    const list = wrapper.find("ul");
65    assert.ok(wrapper.exists());
66    assert.lengthOf(list, 1);
67  });
68
69  it("should return the right number of ListItems if props are valid", () => {
70    const wrapper = shallow(<List {...ValidListProps} />);
71
72    const listItem = wrapper.find(ListItem);
73    assert.lengthOf(listItem, ValidRecommendations.length);
74  });
75
76  it("should return fewer ListItems for fewer items", () => {
77    const wrapper = shallow(<List {...ValidListProps} items={1} />);
78
79    const listItem = wrapper.find(ListItem);
80    assert.lengthOf(listItem, 1);
81  });
82
83  it("should return PlaceHolderListItem for recommendations less than items", () => {
84    const wrapper = shallow(<List {...ValidListProps} items={4} />);
85
86    const listItem = wrapper.find(ListItem);
87    assert.lengthOf(listItem, 3);
88
89    const placeholderListItem = wrapper.find(PlaceholderListItem);
90    assert.lengthOf(placeholderListItem, 1);
91  });
92
93  it("should return fewer ListItems for starting point", () => {
94    const wrapper = shallow(<List {...ValidListProps} recStartingPoint={1} />);
95
96    const listItem = wrapper.find(ListItem);
97    assert.lengthOf(listItem, ValidRecommendations.length - 1);
98  });
99
100  it("should return expected ListItems when offset", () => {
101    const wrapper = shallow(
102      <List {...ValidListProps} items={2} recStartingPoint={1} />
103    );
104
105    const listItemUrls = wrapper.find(ListItem).map(i => i.prop("url"));
106    assert.sameOrderedMembers(listItemUrls, [
107      ValidRecommendations[1].url,
108      ValidRecommendations[2].url,
109    ]);
110  });
111
112  it("should return expected spoc ListItem", () => {
113    const wrapper = shallow(
114      <List {...ValidListProps} items={3} recStartingPoint={0} />
115    );
116
117    const listItemContext = wrapper.find(ListItem).map(i => i.prop("context"));
118    assert.sameOrderedMembers(listItemContext, [
119      undefined,
120      undefined,
121      ValidRecommendations[2].context,
122    ]);
123  });
124});
125
126describe("<ListItem> presentation component", () => {
127  const ValidListItemProps = {
128    url: "FAKE_URL",
129    title: "FAKE_TITLE",
130    domain: "example.com",
131    image_src: "FAKE_IMAGE_SRC",
132    context_type: "pocket",
133  };
134  const ValidSpocListItemProps = {
135    url: "FAKE_URL",
136    title: "FAKE_TITLE",
137    domain: "example.com",
138    image_src: "FAKE_IMAGE_SRC",
139    context_type: "pocket",
140    context: "FAKE_CONTEXT",
141  };
142  let globals;
143
144  beforeEach(() => {
145    globals = new GlobalOverrider();
146  });
147
148  afterEach(() => {
149    globals.sandbox.restore();
150  });
151
152  it("should contain 'a.ds-list-item-link' with the props.url set", () => {
153    const wrapper = shallow(<ListItem {...ValidListItemProps} />);
154
155    const anchors = wrapper.find(
156      `SafeAnchor.ds-list-item-link[url="${ValidListItemProps.url}"]`
157    );
158    assert.lengthOf(anchors, 1);
159  });
160
161  it("should render badges for pocket, bookmark when not a spoc element ", () => {
162    const wrapper = shallow(<ListItem {...ValidListItemProps} />);
163    const contextFooter = wrapper.find(DSContextFooter).shallow();
164
165    assert.lengthOf(contextFooter.find(StatusMessage), 1);
166  });
167
168  it("should render Sponsored Context for a spoc element", () => {
169    const wrapper = shallow(<ListItem {...ValidSpocListItemProps} />);
170    const contextFooter = wrapper.find(DSContextFooter).shallow();
171
172    assert.lengthOf(contextFooter.find(StatusMessage), 0);
173    assert.equal(
174      contextFooter.find(".story-sponsored-label").text(),
175      ValidSpocListItemProps.context
176    );
177  });
178
179  describe("onLinkClick", () => {
180    let dispatch;
181    let sandbox;
182    let wrapper;
183
184    beforeEach(() => {
185      sandbox = sinon.createSandbox();
186      dispatch = sandbox.stub();
187      wrapper = shallow(
188        <ListItem dispatch={dispatch} {...ValidListItemProps} />
189      );
190    });
191
192    afterEach(() => {
193      sandbox.restore();
194    });
195
196    it("should call dispatch with the correct events", () => {
197      wrapper.setProps({ id: "foo-id", pos: 1, type: "foo" });
198
199      wrapper.instance().onLinkClick();
200
201      assert.calledTwice(dispatch);
202      assert.calledWith(
203        dispatch,
204        ac.UserEvent({
205          event: "CLICK",
206          source: "FOO",
207          action_position: 1,
208          value: { card_type: "organic" },
209        })
210      );
211      assert.calledWith(
212        dispatch,
213        ac.ImpressionStats({
214          click: 0,
215          source: "FOO",
216          tiles: [{ id: "foo-id", pos: 1 }],
217        })
218      );
219    });
220
221    it("should set the right card_type on spocs", () => {
222      wrapper.setProps({ id: "foo-id", pos: 1, type: "foo", flightId: 12345 });
223
224      wrapper.instance().onLinkClick();
225
226      assert.calledTwice(dispatch);
227      assert.calledWith(
228        dispatch,
229        ac.UserEvent({
230          event: "CLICK",
231          source: "FOO",
232          action_position: 1,
233          value: { card_type: "spoc" },
234        })
235      );
236      assert.calledWith(
237        dispatch,
238        ac.ImpressionStats({
239          click: 0,
240          source: "FOO",
241          tiles: [{ id: "foo-id", pos: 1 }],
242        })
243      );
244    });
245  });
246});
247
248describe("<PlaceholderListItem> component", () => {
249  it("should have placeholder prop", () => {
250    const wrapper = shallow(<PlaceholderListItem />);
251    const listItem = wrapper.find(ListItem);
252    assert.lengthOf(listItem, 1);
253
254    const placeholder = wrapper.find(ListItem).prop("placeholder");
255    assert.isTrue(placeholder);
256  });
257
258  it("should contain placeholder listitem", () => {
259    const wrapper = shallow(<ListItem placeholder={true} />);
260    const listItem = wrapper.find("li.ds-list-item.placeholder");
261    assert.lengthOf(listItem, 1);
262  });
263
264  it("should not be clickable", () => {
265    const wrapper = shallow(<ListItem placeholder={true} />);
266    const anchor = wrapper.find("SafeAnchor.ds-list-item-link");
267    assert.lengthOf(anchor, 1);
268
269    const linkClick = anchor.prop("onLinkClick");
270    assert.isUndefined(linkClick);
271  });
272
273  it("should not have context menu", () => {
274    const wrapper = shallow(<ListItem placeholder={true} />);
275    const linkMenu = wrapper.find(DSLinkMenu);
276    assert.lengthOf(linkMenu, 0);
277  });
278});
279