TopSiteImpressionWrapper.test.jsx (4557B)
1 import { 2 TopSiteImpressionWrapper, 3 INTERSECTION_RATIO, 4 } from "content-src/components/TopSites/TopSiteImpressionWrapper"; 5 import { actionTypes as at } from "common/Actions.mjs"; 6 import React from "react"; 7 import { shallow } from "enzyme"; 8 9 describe("<TopSiteImpressionWrapper>", () => { 10 const FullIntersectEntries = [ 11 { isIntersecting: true, intersectionRatio: INTERSECTION_RATIO }, 12 ]; 13 const ZeroIntersectEntries = [ 14 { isIntersecting: false, intersectionRatio: 0 }, 15 ]; 16 const PartialIntersectEntries = [ 17 { isIntersecting: true, intersectionRatio: INTERSECTION_RATIO / 2 }, 18 ]; 19 20 // Build IntersectionObserver class with the arg `entries` for the intersect callback. 21 function buildIntersectionObserver(entries) { 22 return class { 23 constructor(callback) { 24 this.callback = callback; 25 } 26 27 observe() { 28 this.callback(entries); 29 } 30 31 unobserve() {} 32 }; 33 } 34 35 const DEFAULT_PROPS = { 36 actionType: at.TOP_SITES_SPONSORED_IMPRESSION_STATS, 37 tile: { 38 tile_id: 1, 39 position: 1, 40 reporting_url: "https://test.reporting.com", 41 advertiser: "test_advertiser", 42 }, 43 IntersectionObserver: buildIntersectionObserver(FullIntersectEntries), 44 document: { 45 visibilityState: "visible", 46 addEventListener: sinon.stub(), 47 removeEventListener: sinon.stub(), 48 }, 49 }; 50 51 const InnerEl = () => <div>Inner Element</div>; 52 53 function renderTopSiteImpressionWrapper(props = {}) { 54 return shallow( 55 <TopSiteImpressionWrapper {...DEFAULT_PROPS} {...props}> 56 <InnerEl /> 57 </TopSiteImpressionWrapper> 58 ); 59 } 60 61 it("should render props.children", () => { 62 const wrapper = renderTopSiteImpressionWrapper(); 63 assert.ok(wrapper.contains(<InnerEl />)); 64 }); 65 it("should not send impression when the wrapped item is visbible but below the ratio", () => { 66 const dispatch = sinon.spy(); 67 const props = { 68 dispatch, 69 IntersectionObserver: buildIntersectionObserver(PartialIntersectEntries), 70 }; 71 renderTopSiteImpressionWrapper(props); 72 73 assert.notCalled(dispatch); 74 }); 75 it("should send an impression when the page is visible and the wrapped item meets the visibility ratio", () => { 76 const dispatch = sinon.spy(); 77 const props = { 78 dispatch, 79 IntersectionObserver: buildIntersectionObserver(FullIntersectEntries), 80 }; 81 renderTopSiteImpressionWrapper(props); 82 83 assert.calledOnce(dispatch); 84 85 let [action] = dispatch.firstCall.args; 86 assert.equal(action.type, at.TOP_SITES_SPONSORED_IMPRESSION_STATS); 87 assert.deepEqual(action.data, { 88 type: "impression", 89 ...DEFAULT_PROPS.tile, 90 }); 91 }); 92 it("should send an impression when the wrapped item transiting from invisible to visible", () => { 93 const dispatch = sinon.spy(); 94 const props = { 95 dispatch, 96 IntersectionObserver: buildIntersectionObserver(ZeroIntersectEntries), 97 }; 98 const wrapper = renderTopSiteImpressionWrapper(props); 99 100 assert.notCalled(dispatch); 101 102 dispatch.resetHistory(); 103 wrapper.instance().impressionObserver.callback(FullIntersectEntries); 104 105 // For the impression 106 assert.calledOnce(dispatch); 107 108 const [action] = dispatch.firstCall.args; 109 assert.equal(action.type, at.TOP_SITES_SPONSORED_IMPRESSION_STATS); 110 assert.deepEqual(action.data, { 111 type: "impression", 112 ...DEFAULT_PROPS.tile, 113 }); 114 }); 115 it("should remove visibility change listener when the wrapper is removed", () => { 116 const props = { 117 dispatch: sinon.spy(), 118 document: { 119 visibilityState: "hidden", 120 addEventListener: sinon.spy(), 121 removeEventListener: sinon.spy(), 122 }, 123 IntersectionObserver, 124 }; 125 126 const wrapper = renderTopSiteImpressionWrapper(props); 127 assert.calledWith(props.document.addEventListener, "visibilitychange"); 128 const [, listener] = props.document.addEventListener.firstCall.args; 129 130 wrapper.unmount(); 131 assert.calledWith( 132 props.document.removeEventListener, 133 "visibilitychange", 134 listener 135 ); 136 }); 137 it("should unobserve the intersection observer when the wrapper is removed", () => { 138 // eslint-disable-next-line no-shadow 139 const IntersectionObserver = 140 buildIntersectionObserver(ZeroIntersectEntries); 141 const spy = sinon.spy(IntersectionObserver.prototype, "unobserve"); 142 const props = { dispatch: sinon.spy(), IntersectionObserver }; 143 144 const wrapper = renderTopSiteImpressionWrapper(props); 145 wrapper.unmount(); 146 147 assert.calledOnce(spy); 148 }); 149 });