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