UNPKG

@ricepuddin/redux-segment

Version:

Segment.io analytics integration for redux.

463 lines (400 loc) 12.9 kB
import test from 'tape'; import React from 'react'; import ReactDOM from 'react-dom'; import { compose, createStore, combineReducers, applyMiddleware } from 'redux'; import { Provider } from 'react-redux'; import { Router, Route, IndexRoute, Link, useRouterHistory } from 'react-router'; import { createHashHistory } from 'history'; import { syncReduxAndRouter, routeReducer } from 'redux-simple-router'; import { ReduxRouter, routerStateReducer, reduxReactRouter } from 'redux-router'; import createAnalyticsStub from './helpers/segment-stub'; import { createTracker, EventTypes } from '../src/index'; test('Page - router support', t => { t.test('redux-simple-router', st => { st.plan(2); window.analytics = createAnalyticsStub(); const node = global.document.getElementById('app'); const tracker = createTracker(); const reducer = combineReducers({ routing: routeReducer, }); const store = compose( applyMiddleware(tracker) )(createStore)(reducer); const Component = () => <div> <h1>Hello!</h1> <ul> <li><Link to="/foo">Foo</Link></li> <li><Link to="/bar">Bar</Link></li> </ul> </div>; const history = useRouterHistory(createHashHistory)({ queryKey: false }); syncReduxAndRouter(history, store); ReactDOM.render( <Provider store={store}> <div> <Router history={history}> <Route path="/" component={Component}> <IndexRoute component={Component}/> <Route path="foo" component={Component}/> <Route path="bar" component={Component}/> </Route> </Router> </div> </Provider>, node ); const initialEvent = window.analytics[0] && window.analytics[0][0]; st.equal(initialEvent, 'page', 'triggers page event on load'); const fooLink = node.querySelector('a[href$="foo"]'); // Wait for route change to propagate to store store.subscribe(() => { const fooLinkEvent = window.analytics[1] && window.analytics[1][0]; st.equal(fooLinkEvent, 'page', 'triggers page event on navigation'); window.analytics = null; }); fooLink.click(); ReactDOM.unmountComponentAtNode(node); }); t.test('redux-router', st => { st.plan(2); window.analytics = createAnalyticsStub(); const node = document.createElement('div'); const tracker = createTracker(); const reducer = combineReducers({ router: routerStateReducer, }); const history = useRouterHistory(createHashHistory)({ queryKey: false }); const store = compose( reduxReactRouter({ history }), applyMiddleware(tracker) )(createStore)(reducer); const Component = () => <div> <h1>Hello!</h1> <ul> <li><Link to="/foo">Foo</Link></li> <li><Link to="/bar">Bar</Link></li> </ul> </div>; ReactDOM.render( <Provider store={store}> <div> <ReduxRouter history={history}> <Route path="/" component={Component}> <IndexRoute component={Component}/> <Route path="foo" component={Component}/> <Route path="bar" component={Component}/> </Route> </ReduxRouter> </div> </Provider>, node ); const initialEvent = window.analytics[0] && window.analytics[0][0]; st.equal(initialEvent, 'page', 'triggers page event on load'); const fooLink = node.querySelector('a[href$="foo"]'); fooLink.click(); const fooLinkEvent = window.analytics[1] && window.analytics[1][0]; st.equal(fooLinkEvent, 'page', 'triggers page event on navigation'); window.analytics = null; ReactDOM.unmountComponentAtNode(node); }); }); test('Page - spec', t => { t.test('default', st => { st.plan(2); window.analytics = createAnalyticsStub(); const explicitAction = { type: 'CHANGE_VIEW', meta: { analytics: { eventType: EventTypes.page, }, }, }; const implicitAction = { type: 'CHANGE_VIEW', meta: { analytics: EventTypes.page, }, }; const identity = val => val; const tracker = createTracker(); const store = compose( applyMiddleware(tracker) )(createStore)(identity); store.dispatch(explicitAction); const defaultExplicitEvent = window.analytics[0] && window.analytics[0][0]; st.equal(defaultExplicitEvent, 'page', 'emits a page event on explicit actions'); store.dispatch(implicitAction); const defaultImplicitEvent = window.analytics[1] && window.analytics[1][0]; st.equal(defaultImplicitEvent, 'page', 'emits a page event on implicit actions'); window.analytics = null; }); t.test('name', st => { st.plan(1); window.analytics = createAnalyticsStub(); const PAGE_NAME = 'Home'; const action = { type: 'CHANGE_VIEW', to: 'home', meta: { analytics: { eventType: EventTypes.page, eventPayload: { name: PAGE_NAME, }, }, }, }; const identity = val => val; const tracker = createTracker(); const store = compose( applyMiddleware(tracker) )(createStore)(identity); store.dispatch(action); const event = [ window.analytics[0] && window.analytics[0][0], window.analytics[0] && window.analytics[0][1], ]; st.deepEqual(event, ['page', PAGE_NAME], 'passes along the name of the page'); window.analytics = null; }); t.test('category', st => { st.plan(2); window.analytics = createAnalyticsStub(); const PAGE_NAME = 'Home'; const CAT_NAME = 'Landing'; const action = { type: 'CHANGE_VIEW', to: 'home', meta: { analytics: { eventType: EventTypes.page, eventPayload: { name: PAGE_NAME, category: CAT_NAME, }, }, }, }; const missingNameAction = { type: 'CHANGE_VIEW', to: 'home', meta: { analytics: { eventType: EventTypes.page, eventPayload: { category: CAT_NAME, }, }, }, }; const identity = val => val; const tracker = createTracker(); const store = compose( applyMiddleware(tracker) )(createStore)(identity); store.dispatch(action); const event = [ window.analytics[0] && window.analytics[0][0], window.analytics[0] && window.analytics[0][1], window.analytics[0] && window.analytics[0][2], ]; st.deepEqual(event, ['page', CAT_NAME, PAGE_NAME], 'passes along the category of the page'); const invalidAction = () => store.dispatch(missingNameAction); st.throws(invalidAction, /missing name/, 'throws error when name prop is missing'); window.analytics = null; }); t.test('properties', st => { st.plan(3); window.analytics = createAnalyticsStub(); const PAGE_NAME = 'Home'; const CAT_NAME = 'Landing'; const TITLE_NAME = 'Homepage'; const action = { type: 'CHANGE_VIEW', to: 'home', meta: { analytics: { eventType: EventTypes.page, eventPayload: { name: PAGE_NAME, category: CAT_NAME, properties: { title: TITLE_NAME, }, }, }, }, }; const noCategoryAction = { type: 'CHANGE_VIEW', to: 'home', meta: { analytics: { eventType: EventTypes.page, eventPayload: { name: PAGE_NAME, properties: { title: TITLE_NAME, }, }, }, }, }; const justPropertiesAction = { type: 'CHANGE_VIEW', to: 'home', meta: { analytics: { eventType: EventTypes.page, eventPayload: { properties: { title: TITLE_NAME, }, }, }, }, }; const identity = val => val; const tracker = createTracker(); const store = compose( applyMiddleware(tracker) )(createStore)(identity); store.dispatch(action); const event = [ window.analytics[0] && window.analytics[0][0], window.analytics[0] && window.analytics[0][1], window.analytics[0] && window.analytics[0][2], window.analytics[0] && window.analytics[0][3], ]; st.deepEqual(event, ['page', CAT_NAME, PAGE_NAME, { title: TITLE_NAME }], 'passes along the properties of the page when category is present'); store.dispatch(noCategoryAction); const noCatEvent = [ window.analytics[1] && window.analytics[1][0], window.analytics[1] && window.analytics[1][1], window.analytics[1] && window.analytics[1][2], ]; st.deepEqual(noCatEvent, ['page', PAGE_NAME, { title: TITLE_NAME }], 'passes along the properties of the page when category is not present'); store.dispatch(justPropertiesAction); const justPropertiesEvent = [ window.analytics[2] && window.analytics[2][0], window.analytics[2] && window.analytics[2][1], ]; st.deepEqual(justPropertiesEvent, ['page', { title: TITLE_NAME }], 'passes along the properties of the page when category and name are not present'); window.analytics = null; }); t.test('options', st => { st.plan(4); window.analytics = createAnalyticsStub(); const PAGE_NAME = 'Home'; const CAT_NAME = 'Landing'; const TITLE_NAME = 'Homepage'; const PROPERTIES = { title: TITLE_NAME, }; const OPTIONS = { 'All': false, 'Mixpanel': true, 'KISSmetrics': true, }; const action = { type: 'CHANGE_VIEW', to: 'home', meta: { analytics: { eventType: EventTypes.page, eventPayload: { name: PAGE_NAME, category: CAT_NAME, properties: PROPERTIES, options: OPTIONS, }, }, }, }; const noCategoryAction = { type: 'CHANGE_VIEW', to: 'home', meta: { analytics: { eventType: EventTypes.page, eventPayload: { name: PAGE_NAME, properties: { title: TITLE_NAME, }, options: OPTIONS, }, }, }, }; const startAtPropertiesAction = { type: 'CHANGE_VIEW', to: 'home', meta: { analytics: { eventType: EventTypes.page, eventPayload: { properties: { title: TITLE_NAME, }, options: OPTIONS, }, }, }, }; const justOptionsAction = { type: 'CHANGE_VIEW', to: 'home', meta: { analytics: { eventType: EventTypes.page, eventPayload: { options: OPTIONS, }, }, }, }; const identity = val => val; const tracker = createTracker(); const store = compose( applyMiddleware(tracker) )(createStore)(identity); store.dispatch(action); const event = [ window.analytics[0] && window.analytics[0][0], window.analytics[0] && window.analytics[0][1], window.analytics[0] && window.analytics[0][2], window.analytics[0] && window.analytics[0][3], window.analytics[0] && window.analytics[0][4], ]; st.deepEqual(event, ['page', CAT_NAME, PAGE_NAME, PROPERTIES, OPTIONS], 'passes along the options of the page event when category is present'); store.dispatch(noCategoryAction); const noCatEvent = [ window.analytics[1] && window.analytics[1][0], window.analytics[1] && window.analytics[1][1], window.analytics[1] && window.analytics[1][2], window.analytics[1] && window.analytics[1][3], ]; st.deepEqual(noCatEvent, ['page', PAGE_NAME, PROPERTIES, OPTIONS], 'passes along the options of the page event when category is not present'); store.dispatch(startAtPropertiesAction); const startAtPropertiesEvent = [ window.analytics[2] && window.analytics[2][0], window.analytics[2] && window.analytics[2][1], window.analytics[2] && window.analytics[2][2], ]; st.deepEqual(startAtPropertiesEvent, ['page', PROPERTIES, OPTIONS], 'passes along the options of the page when only properties are present'); store.dispatch(justOptionsAction); const optionsOnlyEvent = [ window.analytics[3] && window.analytics[3][0], window.analytics[3] && window.analytics[3][1], window.analytics[3] && window.analytics[3][2], ]; st.deepEqual(optionsOnlyEvent, ['page', {}, OPTIONS], 'passes along the options of the page when other properties are not present'); window.analytics = null; }); });