import { _CardGrid as CardGrid, IntersectionObserver, RecentSavesContainer, OnboardingExperience, DSSubHeader, } from "content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid"; import { combineReducers, createStore } from "redux"; import { INITIAL_STATE, reducers } from "common/Reducers.sys.mjs"; import { Provider } from "react-redux"; import { DSCard, PlaceholderDSCard, } from "content-src/components/DiscoveryStreamComponents/DSCard/DSCard"; import { TopicsWidget } from "content-src/components/DiscoveryStreamComponents/TopicsWidget/TopicsWidget"; import { actionCreators as ac, actionTypes as at, } from "common/Actions.sys.mjs"; import React from "react"; import { shallow, mount } from "enzyme"; // 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 {children}; } describe("", () => { let wrapper; beforeEach(() => { wrapper = shallow( ); }); it("should render an empty div", () => { assert.ok(wrapper.exists()); assert.lengthOf(wrapper.children(), 0); }); it("should render DSCards", () => { wrapper.setProps({ items: 2, data: { recommendations: [{}, {}] } }); assert.lengthOf(wrapper.find(".ds-card-grid").children(), 2); assert.equal(wrapper.find(".ds-card-grid").children().at(0).type(), DSCard); }); it("should add 4 card classname to card grid", () => { wrapper.setProps({ fourCardLayout: true, data: { recommendations: [{}, {}] }, }); assert.ok(wrapper.find(".ds-card-grid-four-card-variant").exists()); }); it("should add no description classname to card grid", () => { wrapper.setProps({ hideCardBackground: true, data: { recommendations: [{}, {}] }, }); assert.ok(wrapper.find(".ds-card-grid-hide-background").exists()); }); it("should render sub header in the middle of the card grid for both regular and compact", () => { const commonProps = { essentialReadsHeader: true, editorsPicksHeader: true, items: 12, data: { recommendations: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}], }, Prefs: INITIAL_STATE.Prefs, DiscoveryStream: INITIAL_STATE.DiscoveryStream, }; wrapper = mount( ); assert.ok(wrapper.find(DSSubHeader).exists()); wrapper.setProps({ compact: true, }); wrapper = mount( ); assert.ok(wrapper.find(DSSubHeader).exists()); }); it("should add/hide description classname to card grid", () => { wrapper.setProps({ data: { recommendations: [{}, {}] }, }); assert.ok(wrapper.find(".ds-card-grid-include-descriptions").exists()); wrapper.setProps({ hideDescriptions: true, data: { recommendations: [{}, {}] }, }); assert.ok(!wrapper.find(".ds-card-grid-include-descriptions").exists()); }); it("should create a widget card", () => { wrapper.setProps({ widgets: { positions: [{ index: 1 }], data: [{ type: "TopicsWidget" }], }, data: { recommendations: [{}, {}, {}], }, }); assert.ok(wrapper.find(TopicsWidget).exists()); }); }); // Build IntersectionObserver class with the arg `entries` for the intersect callback. function buildIntersectionObserver(entries) { return class { constructor(callback) { this.callback = callback; } observe() { this.callback(entries); } unobserve() {} disconnect() {} }; } describe("", () => { let wrapper; let fakeWindow; let intersectEntries; beforeEach(() => { intersectEntries = [{ isIntersecting: true }]; fakeWindow = { IntersectionObserver: buildIntersectionObserver(intersectEntries), }; wrapper = mount(); }); it("should render an empty div", () => { assert.ok(wrapper.exists()); assert.equal(wrapper.children().at(0).type(), "div"); }); it("should fire onIntersecting", () => { const onIntersecting = sinon.stub(); wrapper = mount( ); assert.calledOnce(onIntersecting); }); }); describe("", () => { let wrapper; let fakeWindow; let intersectEntries; let dispatch; beforeEach(() => { dispatch = sinon.stub(); intersectEntries = [{ isIntersecting: true }]; fakeWindow = { IntersectionObserver: buildIntersectionObserver(intersectEntries), }; wrapper = mount( ).find(RecentSavesContainer); }); it("should render an IntersectionObserver when not visible", () => { intersectEntries = [{ isIntersecting: false }]; fakeWindow = { IntersectionObserver: buildIntersectionObserver(intersectEntries), }; wrapper = mount( ).find(RecentSavesContainer); assert.ok(wrapper.exists()); assert.ok(wrapper.find(IntersectionObserver).exists()); }); it("should render nothing if visible until we log in", () => { assert.ok(!wrapper.find(IntersectionObserver).exists()); assert.calledOnce(dispatch); assert.calledWith( dispatch, ac.AlsoToMain({ type: at.DISCOVERY_STREAM_POCKET_STATE_INIT, }) ); }); it("should render a grid if visible and logged in", () => { assert.lengthOf(wrapper.find(".ds-card-grid"), 1); assert.lengthOf(wrapper.find(DSSubHeader), 1); assert.lengthOf(wrapper.find(PlaceholderDSCard), 2); assert.lengthOf(wrapper.find(DSCard), 3); }); it("should render a my list link with proper utm params", () => { assert.equal( wrapper.find(".section-sub-link").at(0).prop("url"), "https://getpocket.com/a?utm_source=utmSource&utm_content=utmContent&utm_campaign=utmCampaign" ); }); it("should fire a UserEvent for my list clicks", () => { wrapper.find(".section-sub-link").at(0).simulate("click"); assert.calledWith( dispatch, ac.DiscoveryStreamUserEvent({ event: "CLICK", source: `CARDGRID_RECENT_SAVES_VIEW_LIST`, }) ); }); }); describe("", () => { let wrapper; let fakeWindow; let intersectEntries; let dispatch; let resizeCallback; let fakeResizeObserver = class { constructor(callback) { resizeCallback = callback; } observe() {} unobserve() {} disconnect() {} }; beforeEach(() => { dispatch = sinon.stub(); intersectEntries = [{ isIntersecting: true, intersectionRatio: 1 }]; fakeWindow = { ResizeObserver: fakeResizeObserver, IntersectionObserver: buildIntersectionObserver(intersectEntries), document: { visibilityState: "visible", addEventListener: () => {}, removeEventListener: () => {}, }, }; wrapper = mount( ).find(OnboardingExperience); }); it("should render a ds-onboarding", () => { assert.ok(wrapper.exists()); assert.lengthOf(wrapper.find(".ds-onboarding"), 1); }); it("should dismiss on dismiss click", () => { wrapper.find(".ds-dismiss-button").simulate("click"); assert.calledWith( dispatch, ac.DiscoveryStreamUserEvent({ event: "BLOCK", source: "POCKET_ONBOARDING", }) ); assert.calledWith( dispatch, ac.SetPref("discoverystream.onboardingExperience.dismissed", true) ); assert.equal(wrapper.getDOMNode().style["max-height"], "0px"); assert.equal(wrapper.getDOMNode().style.opacity, "0"); }); it("should update max-height on resize", () => { sinon .stub(wrapper.find(".ds-onboarding-ref").getDOMNode(), "offsetHeight") .get(() => 123); resizeCallback(); assert.equal(wrapper.getDOMNode().style["max-height"], "123px"); }); it("should fire intersection events", () => { assert.calledWith( dispatch, ac.DiscoveryStreamUserEvent({ event: "IMPRESSION", source: "POCKET_ONBOARDING", }) ); }); });