diff --git a/src/lib/ddl/event-handlers.js b/src/lib/ddl/event-handlers.js deleted file mode 100644 index 4365de09..00000000 --- a/src/lib/ddl/event-handlers.js +++ /dev/null @@ -1,60 +0,0 @@ -/* @flow */ -const noop = (e) => e === undefined; - -type EventHandlerType = {| - id : string, - predicate : (e : Object) => boolean, - handle : (e : Object) => void -|}; - -export const buildEventHandlers = (shoppingAnalytics : any) : $ReadOnlyArray => { - const shoppingSendEventHandler : EventHandlerType = { - id: 'send_event_handler', - predicate: (e) => { - return e.event !== undefined; - }, - handle: (e) => { - if (e.event !== 'snippetRun') { - shoppingAnalytics.send(e.event, e.payload); - } - } - }; - - const configurationEventHandler : EventHandlerType = { - id: 'set_handler', - predicate: (e) => { - return e.set !== undefined; - }, - handle: (e) => { - shoppingAnalytics.set(e.set); - } - }; - - return [ - shoppingSendEventHandler, - configurationEventHandler - ]; -}; - -export type EventConsumerType = {| - consume : (e : Object) => void -|}; - -export const createEventHandler = (shoppingAnalytics : any) : EventConsumerType => { - const handlers = buildEventHandlers(shoppingAnalytics); - const findEventHandler = (e) => { - function handlerMatch(handler) : boolean { - return handler.predicate(e); - } - const handler = handlers.filter(handlerMatch); - return handler[0] !== undefined ? handler[0].handle : noop; - }; - - const consume = (e : Object) => { - const handler = findEventHandler(e); - handler(e); - }; - return { - consume - }; -}; diff --git a/src/lib/ddl/shopping-ddl.js b/src/lib/ddl/shopping-ddl.js deleted file mode 100644 index 68a0e625..00000000 --- a/src/lib/ddl/shopping-ddl.js +++ /dev/null @@ -1,42 +0,0 @@ -/* @flow */ -import { createEventHandler } from './event-handlers'; - -const snippetRunEvent = { event: 'snippetRun', t: new Date().getTime() }; -export const DDL_NAME = 'shoppingDDL'; - -const _getDDL = (w) => { - let ddl = w[DDL_NAME]; - - if (!ddl) { - ddl = [ snippetRunEvent ]; - } - - return ddl; -}; - -export const setupShoppingDDL = (w : any, shoppingAnalytics : any) => { - const unprocessedEvents = []; - const eventHandler = createEventHandler(shoppingAnalytics); - - const processEvents = () => { - while (unprocessedEvents.length) { - const event = unprocessedEvents.shift(); - eventHandler.consume(event); - } - }; - - const shoppingDDL = _getDDL(w); - unprocessedEvents.push.apply(unprocessedEvents, shoppingDDL); - // $FlowFixMe - shoppingDDL.push = (...args) => { - unprocessedEvents.push(...args); - processEvents(); - }; - shoppingDDL.length = 0; - - processEvents(); - return { - unprocessedEvents, - shoppingDDL - }; -}; diff --git a/src/lib/shopping-analytics.js b/src/lib/shopping-analytics.js index b2d6632f..c184e02d 100644 --- a/src/lib/shopping-analytics.js +++ b/src/lib/shopping-analytics.js @@ -1,84 +1,13 @@ /* @flow */ -// $FlowFixMe -import 'whatwg-fetch'; // eslint-disable-line import/no-unassigned-import - -import type { Config } from '../types/config'; -import type { ContainerSummary } from '../types'; -import type { VisitorInfo } from '../types/user'; - -import { setupTrackers } from './shopping-trackers'; -import { setupUserDetails } from './user-configuration'; -import { setupContainer } from './get-property-id'; -// $FlowFixMe -import { capturePageData } from './tag-parsers/capture-page-data'; -import { debugLogger } from './debug-console-logger'; - -// $FlowFixMe -export const shoppingAnalyticsSetup = (config : Config = {}) => { - const shoppingTracker = setupTrackers(config); - let identityFetchCompleted : boolean = false; - let containerFetchCompleted : boolean = false; - let eventQueue = []; - let inMemoryIdentity: VisitorInfo = null; - const queue_limit = 100; - - function isReadyToPublish() : boolean { - return identityFetchCompleted && containerFetchCompleted; - } - - function flushEventQueueIfReady () { - if (isReadyToPublish()) { - for (const params of eventQueue) { - shoppingTracker.send(...params, inMemoryIdentity); - } - eventQueue = []; - } - } - - function onUserIdentityFetch(identity: VisitorInfo) { - inMemoryIdentity = identity; - debugLogger.log('[shopping-analytics:onUserIdentityFetch] Received identity fetch notification.'); - identityFetchCompleted = true; - flushEventQueueIfReady(); - } - - // $FlowFixMe - function onContainerFetch(containerSummary : ContainerSummary) : void { - debugLogger.log('[shopping-analytics:onUserIdentityFetch] Received container fetch notification. Container summary: ', containerSummary); - config.propertyId = config.propertyId || (containerSummary && containerSummary.id); - config.containerSummary = containerSummary; - containerFetchCompleted = true; - flushEventQueueIfReady(); - } - - function enqueueEvent(...args) { - debugLogger.log('[shopping-analytics:enqueueEvent] Event is enqueued (wating on initialization). Event(s):', args); - eventQueue.push(args); - if (eventQueue.length > queue_limit) { - eventQueue.shift(); - } - } - - // $FlowFixMe - function sendOrEnqueue(...args) { - if (!isReadyToPublish()) { - enqueueEvent(...args); - } else { - shoppingTracker.send(...args); - } - } - - debugLogger.log('[shopping-analytics:shoppingAnalyticsSetup] Initialized shopping analytics with the following configuration', config); - - setupUserDetails(config, onUserIdentityFetch); - setupContainer(config, onContainerFetch); +const noop = () => {}; +export const shoppingAnalyticsSetup = () => { // return publicly accessible return { - send: sendOrEnqueue, - set: shoppingTracker.set, - getPageSkuData: capturePageData, - enableDebugLogging: () => { debugLogger.setDebugEnabled(true); }, - disableDebugLogging: () => { debugLogger.setDebugEnabled(false); } + send: noop, + set: noop, + getPageSkuData: noop, + enableDebugLogging: noop, + disableDebugLogging: noop }; }; diff --git a/src/lib/shopping-attributes.js b/src/lib/shopping-attributes.js deleted file mode 100644 index 985fe7c0..00000000 --- a/src/lib/shopping-attributes.js +++ /dev/null @@ -1,21 +0,0 @@ -/* @flow */ -import type { Config } from '../types'; - - -/** - * Sets up a page context object that uses Config object to store variables within the page scope. - * Currently, there is no constraint on the varables (name,value). the set of supported vairables will be described - * in the SDK documentation. Primary reason, for not having any restriction is to build a generic interafce for vairables and - * once we clear about the supported list of vairables, the constraint maybe added. - * - * @param config - * @returns {{viewPage: (function(...[*]=))}} - */ -export function shoppingAttributes (config : Config) : Object { - return { - updateShoppingAttributes: (variables : Object) => { - const priorAttributes = config.shoppingAttributes || {}; - config.shoppingAttributes = { ...priorAttributes, ...variables }; - } - }; -} diff --git a/src/lib/shopping-fpti/event-handlers.js b/src/lib/shopping-fpti/event-handlers.js deleted file mode 100644 index 9bf2341e..00000000 --- a/src/lib/shopping-fpti/event-handlers.js +++ /dev/null @@ -1,159 +0,0 @@ -/* @flow */ -// $FlowFixMe -import { capturePageData } from '../tag-parsers/capture-page-data'; -import type { Config } from '../../types'; -import type { EventType } from '../../types/shopping-events'; -import { isConfigFalse } from '../utils'; -import { debugLogger } from '../debug-console-logger'; - -// eslint-disable-next-line default-param-last -function findConfigurationAttribute(config : Config, payload : Object = {}, attribName : string) : ?string { - const shopperConfig = config.shoppingAttributes || {}; - return shopperConfig[attribName] || payload[attribName]; -} - -export const allowedAttributes = [ - // page view - 'page_type', - 'page_name', - 'page_id', - 'page_path', - 'page_category_name', - 'page_category_id', - 'deal_id', - 'deal_name', - 'deal_value', - 'search_results_count', - 'cart_products', - // product view - 'product_id', - 'product_name', - 'product_url', - 'product_price', - 'product_brand', - 'product_category_name', - 'product_category_id', - 'product_discount', - // purchase - 'amount', - // set properties - 'currency' -]; - -export function eventSinfoBuilderInit(config : Config) : Object { - function filterAttributesForSinfoPayload(event : Object) : Object { - const filteredAttributes = Object.keys(event) - .filter((key) => allowedAttributes.includes(key)) - .reduce((obj, key) => { - obj[key] = event[key]; - return obj; - }, {}); - - const excludedAttributes = Object.keys(event) - .filter((key) => !allowedAttributes.includes(key)) - .reduce((obj, key) => { - obj[key] = event[key]; - return obj; - }, {}); - - if (Object.keys(excludedAttributes).length) { - debugLogger.log('[event-handler:filterAttributesForSinfoPayload] Following attributes will be excluded from event sinfo payload:', excludedAttributes); - } - - return filteredAttributes; - } - - function constructSinfoPayload(payload : Object) : ?string { - const shopperConfig = config.shoppingAttributes || {}; - - const enrichedPayload = filterAttributesForSinfoPayload({ - ...payload, - ...shopperConfig - }); - const shouldCaptureData = !isConfigFalse(shopperConfig.parse_page); - const capturedData = shouldCaptureData ? capturePageData() : {}; - if (shouldCaptureData) { - enrichedPayload.capturedData = capturedData; - } - - const json = JSON.stringify(enrichedPayload); - return json === '{}' ? null : json; - } - - return { - constructSinfoPayload - }; -} - -export function customEventMappingInit(config : Config) : Object { - function enrichPageViewEvent(payload : Object) : Object { - const disableStoreCash = findConfigurationAttribute(config, payload, 'disable_storecash') || 'false'; - const containerSummary = config.containerSummary || {}; - const offerId = containerSummary.programId; - if ((disableStoreCash === 'false') && offerId) { - return { - fltp: 'analytics', - offer_id: offerId, - sub_flow: 'store-cash', - sub_component: 'analytics' - }; - } - return {}; - } - - function enrichPurchaseEvent() : Object { - return { - fltp: 'analytics', - es: 'txnSuccess' - }; - } - - function enrichStoreCashExcusionEvent() : Object { - return { - fltp: 'analytics', - mru: 'true', - es: 'merchantRecognizedUser' - }; - } - - const eventMap = { - page_view: enrichPageViewEvent, - purchase: enrichPurchaseEvent, - store_cash_exclusion: enrichStoreCashExcusionEvent - }; - - function getEventSpecificFptiAttributes(eventType : EventType) : Object { - const event = eventType.toString(); - return eventMap[event] ? eventMap[event]() : {}; - } - - return { - getEventSpecificFptiAttributes - }; -} - -export function eventToFptiMapperInit(config : Config) : Object { - const customEventMapper = customEventMappingInit(config); - const eventSinfoBuilder = eventSinfoBuilderInit(config); - - function eventToFptiAttributes( - eventType : EventType, - payload : Object = {} - ) : Object { - const user_id = findConfigurationAttribute(config, payload, 'user_id'); - const eventSpecificAttributes = customEventMapper.getEventSpecificFptiAttributes( - eventType, payload - ); - return { - eventName: eventType, - eventData: eventSinfoBuilder.constructSinfoPayload(payload), - merchantProvidedUserId: user_id, - page: `ppshopping:${ eventType }`, - ...eventSpecificAttributes - }; - } - - return { - eventToFptiAttributes - }; -} diff --git a/src/lib/shopping-fpti/shopping-event-conversions.js b/src/lib/shopping-fpti/shopping-event-conversions.js deleted file mode 100644 index 5efd7dc8..00000000 --- a/src/lib/shopping-fpti/shopping-event-conversions.js +++ /dev/null @@ -1,83 +0,0 @@ -/* @flow */ -import type { - PageView, - EventType -} from '../../types/shopping-events'; -import type { FptiInput, Config } from '../../types'; -import { getDeviceInfo } from '../get-device-info'; -import { getIdentity, getUserId } from '../local-storage'; -import type { VisitorInfo } from '../../types/user'; - -import { eventToFptiMapperInit } from './event-handlers'; - -export type EventToFptiInputMapping = (event : Object) => FptiInput; - -function getStoredUserIds() : Object { - const storedUserIds = getUserId(); - if (storedUserIds) { - return { - shopperId: storedUserIds.userId, - merchantProvidedUserId: storedUserIds.merchantProvidedUserId - }; - } else { - return {}; - } -} - - -export const eventToFptiConverters = (config : Config) => { - const eventToFptiMapper = eventToFptiMapperInit(config); - - function constructFptiInput( - eventType : EventType, - event : Object, - inMemoryIdentity: ?VisitorInfo - ) : Object { - const containerSummary = config.containerSummary || {}; - const applicationContext = containerSummary.applicationContext || {}; - const deviceInfo : any = getDeviceInfo() || {}; - const identity : any = inMemoryIdentity || getIdentity() || {}; - - const storedUserIds = getStoredUserIds(); - const eventFptiAttributes = eventToFptiMapper.eventToFptiAttributes(eventType, event); - - let { location } = deviceInfo; - - if (applicationContext.limitUrlCapture) { - location = document.location.host; - } - - return { - ...eventFptiAttributes, - e: 'im', - flag_consume: 'yes', - mrid: containerSummary.mrid, - item: containerSummary.id, - shopperId: storedUserIds.shopperId, - t: new Date().getTime(), - g: new Date().getTimezoneOffset(), - deviceWidth: deviceInfo.deviceWidth, - deviceHeight: deviceInfo.deviceHeight, - screenWidth: deviceInfo.screenWidth, - screenHeight: deviceInfo.screenHeight, - colorDepth: deviceInfo.colorDepth, - rosettaLanguage: deviceInfo.rosettaLanguage, - location, - deviceType: deviceInfo.deviceType, - browserHeight: deviceInfo.browserHeight, - browserWidth: deviceInfo.browserWidth, - confidenceScore: identity.confidenceScore, - encryptedAccountNumber: identity.encryptedAccountNumber, - identificationType: identity.identificationType - }; - } - - return { - viewPageToFpti: (viewData : PageView) : Object => { - return constructFptiInput('page_view', viewData); - }, - eventToFpti: constructFptiInput - }; -}; - - diff --git a/src/lib/shopping-fpti/shopping-fpti.js b/src/lib/shopping-fpti/shopping-fpti.js deleted file mode 100644 index 0de3d612..00000000 --- a/src/lib/shopping-fpti/shopping-fpti.js +++ /dev/null @@ -1,131 +0,0 @@ -/* @flow */ -import { - getClientID, - getMerchantID, - getPartnerAttributionID -} from '@paypal/sdk-client/src'; - -import type { FptiVariables } from '../../types'; -import { sendBeacon } from '../fpti'; - -export const filterFalsyValues = (source : Object) : FptiVariables => { - Object.keys(source).forEach(key => { - const val = source[key]; - - if (val === '' || val === undefined || val === null || Number.isNaN(val)) { - delete source[key]; - } - }); - - return source; -}; - -export const resolveTrackingVariables = (data : any) : FptiVariables => ({ - // Device height - dh: data.deviceHeight, - - // Device width - dw: data.deviceWidth, - - // Browser height - bh: data.browserHeight, - - // Browser width - bw: data.browserWidth, - - // Color depth - cd: data.colorDepth, - - // Screen height - sh: data.screenHeight, - - // Screen width - sw: data.screenWidth, - - // Device type - dvis: data.deviceType, - - // Browser type - btyp: data.browserType, - - // Rosetta language - rosetta_language: data.rosettaLanguage, - - // Page domain & path - completeurl: data.location, - - // Identification confidence score - unsc: data.confidenceScore, - - // Identification type returned by VPNS - identifier_used: data.identificationType, - - // Unverified encrypted customer account number - cust: data.encryptedAccountNumber, - - // Analytics identifier associated with the merchant site. XO container id. - item: data.item, - - // Merchant encrypted account number - mrid: data.mrid || getMerchantID()[0], - - // ClientID - client_id: getClientID(), - - // Partner AttributionId - bn_code: getPartnerAttributionID(), - - // Event Name - event_name: data.eventName, - - // Event Data - sinfo: JSON.stringify(data.eventData), - - // Legacy value for filtering events in Herald - page: data.page, - - // Legacy value for filtering events in Herald - pgrp: data.page, - - // Application name - comp: 'tagmanagernodeweb', - - // Legacy impression event - // TODO: currently hard-coded to 'im'. When additional events (add-to-cart, purchase, etc) - // are moved to fpti this value will need to be updated. - e: data.e, - - // Timestamp - t: data.t, - - // Timestamp relative to user - g: data.g, - - external_id: data.merchantProvidedUserId, - - shopper_id: data.shopperId, - - merchant_cart_id: data.cartId, - - product: 'ppshopping_v2', - - es: data.es, - - fltp: data.fltp, - - offer_id: data.offer_id, - - sub_component: data.sub_component, - - sub_flow: data.sub_flow, - - mru: data.mru, - - flag_consume: data.flag_consume -}); - -export const trackFpti = (data : any) => { - const fptiServer = 'https://t.paypal.com/ts'; - const trackingVariables = resolveTrackingVariables(data); - sendBeacon(fptiServer, filterFalsyValues(trackingVariables)); -}; diff --git a/src/lib/shopping-trackers.js b/src/lib/shopping-trackers.js deleted file mode 100644 index 07678fa1..00000000 --- a/src/lib/shopping-trackers.js +++ /dev/null @@ -1,43 +0,0 @@ -/* @flow */ -import type { FptiInput, Config } from '../types'; -import type { EventType } from '../types/shopping-events'; -import type { VisitorInfo } from '../types/user'; - -import { debugLogger } from './debug-console-logger'; -import { trackFpti } from './shopping-fpti/shopping-fpti'; -import { - eventToFptiConverters -} from './shopping-fpti/shopping-event-conversions'; -import { shoppingAttributes } from './shopping-attributes'; - -function initGenericEventPublisher(config : Config) : Object { - const convertEvent = eventToFptiConverters(config).eventToFpti; - return { - publishEvent: (event : EventType, payload : Object, inMemoryIdentity: VisitorInfo) => { - const fptiInput : FptiInput = convertEvent(event, payload, inMemoryIdentity); - debugLogger.log('[shopping-tracker:publishEvent] Publishing FPTI event:', fptiInput); - trackFpti(fptiInput); - } - }; -} - -/** - * Setup the various trackers which are a part of the shopping analytics - * api. - * - * There are two phases : - * 1) Converter - converts from the SDK object to an fpti object. - * There are specific converters for different SDK endpoint and payload objects - * 2) Publisher - publishes event to fpti taking care of fetching necessary container data from the server - * before publishing - * There is a single fpti publisher that takes the FPTI payload from any converter - * and sends to FPTI - * - * @param config - * @returns {{viewPage: (function(...[*]=))}} - */ -export const setupTrackers = (config : Config) => { - const send = initGenericEventPublisher(config).publishEvent; - const set = shoppingAttributes(config).updateShoppingAttributes; - return { send, set }; -}; diff --git a/src/shopping-component.js b/src/shopping-component.js index b883aa9a..3ebb6be3 100644 --- a/src/shopping-component.js +++ b/src/shopping-component.js @@ -1,42 +1,13 @@ /* @flow */ -// $FlowFixMe -import 'whatwg-fetch'; // eslint-disable-line import/no-unassigned-import - -import constants from './lib/constants'; import { shoppingAnalyticsSetup } from './lib/shopping-analytics'; -import { setupShoppingDDL, DDL_NAME } from './lib/ddl/shopping-ddl'; -import type { Config } from './types'; - -const { defaultTrackerConfig } = constants; -/** - * Parse merchant provided config and cache the user details passed - * in by the merchant if any as well as try to identify the user by calling VPNS. - * - * Further, fetch and cache the container associated with the propertyId or the url. - * @param config - * @returns {{viewPage: function(Object): void}} - * @constructor - */ // $FlowFixMe -export const ShoppingAnalytics = (config : Config = {}) => { - // $FlowFixMe - config = { ...defaultTrackerConfig, ...config }; - const shoppingAnalytics = shoppingAnalyticsSetup(config); +export const ShoppingAnalytics = () => { + const shoppingAnalytics = shoppingAnalyticsSetup(); window.__pp__trackers__ = window.__pp__trackers__ || []; window.__pp__trackers__.push(shoppingAnalytics); return shoppingAnalytics; }; -function init() { - if (window[DDL_NAME]) { - const analytics = new ShoppingAnalytics(); - setupShoppingDDL(window, analytics); - } -} - - -export function setup() { - init(); -} +export function setup() {} diff --git a/test/lib/ddl/event-handlers.test.js b/test/lib/ddl/event-handlers.test.js deleted file mode 100644 index 6360012d..00000000 --- a/test/lib/ddl/event-handlers.test.js +++ /dev/null @@ -1,42 +0,0 @@ -/* global expect jest */ -/* @flow */ -import { createEventHandler } from '../../../src/lib/ddl/event-handlers'; - -const setMock = jest.fn(); -const sendMock = jest.fn(); - -const shoppingAnalytics = { - send: sendMock, - set: setMock -}; - -describe('test eventTracker setup', () => { - beforeEach(() => { - setMock.mockClear(); - sendMock.mockClear(); - }); - - it('should handle send event', () => { - const event = { event: 'page_view', payload: { type: 'Test' } }; - const handler = createEventHandler(shoppingAnalytics); - handler.consume(event); - expect(sendMock).toBeCalledWith(event.event, event.payload); - expect(setMock).not.toHaveBeenCalled(); - }); - - it('should handle set event', () => { - const event = { set: { currency: 'USD' } }; - const handler = createEventHandler(shoppingAnalytics); - handler.consume(event); - expect(sendMock).not.toHaveBeenCalled(); - expect(setMock).toBeCalledWith(event.set); - }); - - it('should skip unknown event', () => { - const event = { testUnknown: 1 }; - const handler = createEventHandler(shoppingAnalytics); - handler.consume(event); - expect(sendMock).not.toHaveBeenCalled(); - expect(setMock).not.toHaveBeenCalled(); - }); -}); diff --git a/test/lib/ddl/shopping-ddl.test.js b/test/lib/ddl/shopping-ddl.test.js deleted file mode 100644 index 296acbe2..00000000 --- a/test/lib/ddl/shopping-ddl.test.js +++ /dev/null @@ -1,43 +0,0 @@ -/* global expect jest */ -/* @flow */ -import { createEventHandler } from '../../../src/lib/ddl/event-handlers'; -import { setupShoppingDDL } from '../../../src/lib/ddl/shopping-ddl'; - -jest.mock('../../../src/lib/ddl/event-handlers'); -const consumeMock = jest.fn(); -const shoppingAnalytics = {}; -const event1 = { event: 'page_view', payload: { type: 'Test' } }; -const event2 = { event: 'page_view', payload: { type: 'Test2' } }; - - -describe('test eventTracker setup', () => { - beforeEach(() => { - createEventHandler.mockClear(); - createEventHandler.mockReturnValue({ - consume: consumeMock - }); - consumeMock.mockClear(); - }); - - it('should process events in the window.shoppingDDL', () => { - const window = { shoppingDDL: [ event1 ] }; - setupShoppingDDL(window, shoppingAnalytics); - expect(consumeMock).toBeCalledWith(event1); - expect(window.shoppingDDL.length).toEqual(0); - - window.shoppingDDL.push(event2); - expect(consumeMock).toBeCalledWith(event2); - expect(window.shoppingDDL.length).toEqual(0); - }); - - - it('should process events after window.shoppingDDL is overwritten', () => { - const window = { shoppingDDL: [] }; - setupShoppingDDL(window, shoppingAnalytics); - expect(window.shoppingDDL.length).toEqual(0); - - window.shoppingDDL.push(event1); - expect(consumeMock).toBeCalledWith(event1); - expect(window.shoppingDDL.length).toEqual(0); - }); -}); diff --git a/test/lib/shopping-analytics.test.js b/test/lib/shopping-analytics.test.js deleted file mode 100644 index ea9cb32a..00000000 --- a/test/lib/shopping-analytics.test.js +++ /dev/null @@ -1,72 +0,0 @@ -/* global expect jest */ -/* @flow */ -import { setupTrackers } from '../../src/lib/shopping-trackers'; -import { shoppingAnalyticsSetup } from '../../src/lib/shopping-analytics'; -import { setupUserDetails } from '../../src/lib/user-configuration'; -import { setupContainer } from '../../src/lib/get-property-id'; - -jest.mock('../../src/lib/shopping-trackers'); -jest.mock('../../src/lib/user-configuration'); -jest.mock('../../src/lib/get-property-id'); - -const setMock = jest.fn(); -const sendMock = jest.fn(); -const config = {}; -const containerSummary = { - programId: 'A87KPP4KFVC8J', - mrid: 'JTJ8GTMKP7V3A' -}; - -describe('test eventTracker setup', () => { - beforeEach(() => { - setupTrackers.mockClear(); - - setupTrackers.mockReturnValue({ - set: setMock, - send: sendMock - }); - - setMock.mockClear(); - sendMock.mockClear(); - - setupUserDetails.mockClear(); - setupContainer.mockClear(); - }); - - it('should enqueue event if identity has not updated status', () => { - const shopping = shoppingAnalyticsSetup(config); - shopping.send('page_view', {}); - expect(sendMock).not.toHaveBeenCalled(); - }); - - it('should send events after identity has updated status', () => { - const shopping = shoppingAnalyticsSetup(config); - shopping.send('page_view', { id: 1 }); - shopping.send('page_view', { id: 2 }); - expect(sendMock).not.toHaveBeenCalled(); - - // simmulate identity and conatienr fetch callbacks - // shopping.onUserIdentityFetch(); - // shopping.onContainerFetch(containerSummary); - // - // const sendCall1 = sendMock.mock.calls[0]; - // expect(sendCall1[0]).toBe('page_view'); - // expect(JSON.stringify(sendCall1[1])).toBe(JSON.stringify({ id: 1 })); - }); - - it('should send event if identity already set status', () => { - setupUserDetails.mockImplementation((conf, callback) => callback()); - setupContainer.mockImplementation((conf, callback) => callback(containerSummary)); - - const shopping = shoppingAnalyticsSetup(config); - shopping.send('page_view', { id: 1 }); - expect(sendMock).toHaveBeenCalledTimes(1); - expect(sendMock).toBeCalledWith('page_view', { id: 1 }); - }); - - it('should pass set to Shopping tracker', () => { - const shopping = shoppingAnalyticsSetup(config); - shopping.set({ currency: 'USD' }); - expect(setMock).toBeCalledWith({ currency: 'USD' }); - }); -}); diff --git a/test/lib/shopping-fpti.test.js b/test/lib/shopping-fpti.test.js deleted file mode 100644 index 8dc66552..00000000 --- a/test/lib/shopping-fpti.test.js +++ /dev/null @@ -1,91 +0,0 @@ -/* globals jest expect */ -/* @flow */ -import { - resolveTrackingVariables, trackFpti -} from '../../src/lib/shopping-fpti/shopping-fpti'; -import { sendBeacon } from '../../src/lib/fpti'; - -jest.mock('../../src/lib/fpti'); - -const fptiInput = { - deviceHeight: 134, - deviceWidth: 124, - browserHeight: 23, - browserWidth: 34, - colorDepth: 234, - screenHeight: 24, - screenWidth: 243, - deviceType: 'Desktop', - browserType: 'Chrome', - rosettaLanguage: 'en-US', - location: 'test.business.us', - confidenceScore: 100, - identificationType: 'RMUC', - encryptedAccountNumber: 'F4CKEENF625EL', - eventName: 'page_view', - eventType: 'page_view', - eventData: { id: 123 }, - page: 'ppshopping::page_view', - e: 'im', - t: 1625011681839, - g: 420, - merchantProvidedUserId: '78f83452-550b-447d-8043-44e836608810', - shopperId: '8b44b326-9885-4e0c-bf0c-36c64d6a8521', - sub_component: 'smart_incentives', - sub_flow: 'store-cash', - offer_id: 'KASTDGDHFTQF8', - mru: 'true', - flag_consume: 'yes', - item: '91388d62-3964-43be-a022-a5ff76d43798' -}; - -describe('should map tracking data', () => { - it.skip('should map tracking data with default values', () => { - const trackingData = resolveTrackingVariables(fptiInput); - expect(trackingData.product).toEqual('ppshopping_v2'); - expect(trackingData.e).toEqual('im'); - expect(trackingData.comp).toEqual('tagmanagernodeweb'); - expect(trackingData.page).toEqual('ppshopping::page_view'); - - expect(trackingData.dh).toEqual(fptiInput.deviceHeight); - expect(trackingData.dw).toEqual(fptiInput.deviceWidth); - expect(trackingData.bh).toEqual(fptiInput.browserHeight); - expect(trackingData.bw).toEqual(fptiInput.browserWidth); - expect(trackingData.cd).toEqual(fptiInput.colorDepth); - expect(trackingData.sh).toEqual(fptiInput.screenHeight); - expect(trackingData.sw).toEqual(fptiInput.screenWidth); - expect(trackingData.dvis).toEqual(fptiInput.deviceType); - expect(trackingData.btyp).toEqual(fptiInput.browserType); - expect(trackingData.rosetta_language).toEqual(fptiInput.rosettaLanguage); - expect(trackingData.completeurl).toEqual(fptiInput.location); - expect(trackingData.unsc).toEqual(fptiInput.confidenceScore); - expect(trackingData.identifier_used).toEqual(fptiInput.identificationType); - expect(trackingData.cust).toEqual(fptiInput.encryptedAccountNumber); - expect(trackingData.event_name).toEqual(fptiInput.eventName); - expect(trackingData.sinfo).toEqual(JSON.stringify(fptiInput.eventData)); - expect(trackingData.page).toEqual(fptiInput.page); - expect(trackingData.pgrp).toEqual(fptiInput.page); - expect(trackingData.external_id).toEqual(fptiInput.merchantProvidedUserId); - expect(trackingData.shopper_id).toEqual(fptiInput.shopperId); - expect(trackingData.dh).toEqual(fptiInput.deviceHeight); - expect(trackingData.sub_component).toEqual(fptiInput.sub_component); - expect(trackingData.sub_flow).toEqual(fptiInput.sub_flow); - expect(trackingData.offer_id).toEqual(fptiInput.offer_id); - expect(trackingData.mru).toEqual(fptiInput.mru); - expect(trackingData.flag_consume).toEqual(fptiInput.flag_consume); - expect(trackingData.item).toEqual(fptiInput.item); - }); -}); - -describe('trackFpti should send FPTI event', () => { - beforeEach(() => { - sendBeacon.mockClear(); - }); - it.skip('trackFpti should send FPTI event', () => { - const fptiPayload = resolveTrackingVariables(fptiInput); - - trackFpti(fptiInput); - - expect(sendBeacon).toBeCalledWith('https://t.paypal.com/ts', fptiPayload); - }); -}); diff --git a/test/lib/shopping-fpti/event-handlers.test.js b/test/lib/shopping-fpti/event-handlers.test.js deleted file mode 100644 index eca64da2..00000000 --- a/test/lib/shopping-fpti/event-handlers.test.js +++ /dev/null @@ -1,221 +0,0 @@ -/* @flow */ -import { - eventToFptiMapperInit, - allowedAttributes -} from '../../../src/lib/shopping-fpti/event-handlers'; - -const { expect } = global; - -describe('IF ANY OF THESE FAIL, UPDATE DOC WHEN YOU FIX -- https://go/shopping-sdk-events', () => { - describe('test FPTI attribute mappings for page_view', () => { - const eventName = 'page_view'; - const eventPayload = { - user_id: '123', - page_id: 'HOME' - }; - - it('should return page_view event mappings if container is missing', () => { - const config = {}; - const eventFpti = eventToFptiMapperInit(config).eventToFptiAttributes( - eventName, - eventPayload - ); - expect(eventFpti).toEqual({ - eventName: 'page_view', - eventData: JSON.stringify({ - page_id: 'HOME' - }), - merchantProvidedUserId: '123', - page: `ppshopping:page_view` - }); - }); - - it('store-cash. should return page_view event mappings container includes offer', () => { - const config = { - containerSummary: { - programId: 'LB8HB8WNMLNU6', - mrid: 'JTJ8GTMKP7V3A' - } - }; - const eventFpti = eventToFptiMapperInit(config).eventToFptiAttributes( - eventName, - eventPayload - ); - expect(eventFpti).toEqual({ - eventName: 'page_view', - eventData: JSON.stringify({ - page_id: 'HOME' - }), - merchantProvidedUserId: '123', - page: `ppshopping:page_view`, - fltp: 'analytics', - offer_id: config.containerSummary.programId, - sub_component: 'analytics', - sub_flow: 'store-cash' - }); - }); - - it('store-cash. disable_storecash=true should not include offer', () => { - const config = { - containerSummary: { - programId: 'LB8HB8WNMLNU6', - mrid: 'JTJ8GTMKP7V3A' - }, - shoppingAttributes: { - disable_storecash: 'true' - } - }; - const eventFpti = eventToFptiMapperInit(config).eventToFptiAttributes( - eventName, - eventPayload - ); - expect(eventFpti.sub_flow).toEqual(undefined); - }); - - it('store-cash. disable_storecash=false should not include offer', () => { - const config = { - containerSummary: { - programId: 'LB8HB8WNMLNU6', - mrid: 'JTJ8GTMKP7V3A' - }, - shoppingAttributes: { - disable_storecash: 'false' - } - }; - const eventFpti = eventToFptiMapperInit(config).eventToFptiAttributes( - eventName, - eventPayload - ); - expect(eventFpti.sub_flow).toEqual('store-cash'); - }); - }); - - describe('test FPTI attribute mappings for purchase event', () => { - const eventName = 'purchase'; - const eventPayload = { user_id: '123', amount: '20.00' }; - - it('should return purchase event mappings', () => { - const config = {}; - const eventFpti = eventToFptiMapperInit(config).eventToFptiAttributes( - eventName, - eventPayload - ); - expect(eventFpti).toEqual({ - eventName: 'purchase', - eventData: JSON.stringify({ - amount: '20.00' - }), - merchantProvidedUserId: '123', - page: `ppshopping:purchase`, - fltp: 'analytics', - es: 'txnSuccess' - }); - }); - }); - - describe('test FPTI attribute mappings for store_cash exclusion event', () => { - const eventName = 'store_cash_exclusion'; - const eventPayload = {}; - - it('should return store_cash_exclusion event mappings', () => { - const config = {}; - const eventFpti = eventToFptiMapperInit(config).eventToFptiAttributes( - eventName, - eventPayload - ); - expect(eventFpti).toEqual({ - merchantProvidedUserId: undefined, - eventData: null, - eventName: 'store_cash_exclusion', - page: `ppshopping:store_cash_exclusion`, - fltp: 'analytics', - es: 'merchantRecognizedUser', - mru: 'true' - }); - }); - }); - - describe('should handle shopping attributes set in the Configuration', () => { - const eventName = 'page_view'; - const eventPayload = { - page_id: 'HOME' - }; - - it('should map attribute from shopping attributes set in the Configuration', () => { - const config = { - shoppingAttributes: { - currency: 'USD' - } - }; - - const eventFpti = eventToFptiMapperInit(config).eventToFptiAttributes( - eventName, - eventPayload - ); - const sinfo = JSON.parse(eventFpti.eventData); - expect(sinfo.currency).toEqual('USD'); - }); - - it('should exclude special attributes from Sinfo. user_id set in Shopping configuration', () => { - const config = { - shoppingAttributes: { - user_id: '15f903ad-e95c-4293-a8ef-b55f66bb9057' - } - }; - - const eventFpti = eventToFptiMapperInit(config).eventToFptiAttributes( - eventName, - eventPayload - ); - const sinfo = JSON.parse(eventFpti.eventData); - expect(sinfo.user_id).toEqual(undefined); - }); - - it('should exclude special attributes from Sinfo. user_id set in event payload', () => { - const config = {}; - const payload = { - page_id: 'HOME', - user_id: '15f903ad-e95c-4293-a8ef-b55f66bb9057' - }; - - const eventFpti = eventToFptiMapperInit(config).eventToFptiAttributes( - eventName, - payload - ); - const sinfo = JSON.parse(eventFpti.eventData); - expect(sinfo.user_id).toEqual(undefined); - }); - }); -}); - -describe('IF THIS FAILS, UPDATE DOC WHEN YOU FIX -- https://go/shopping-sinfo-props', () => { - it('included attributes in sinfo builder should match what is documented.', () => { - expect(allowedAttributes).toEqual([ - // page view - 'page_type', - 'page_name', - 'page_id', - 'page_path', - 'page_category_name', - 'page_category_id', - 'deal_id', - 'deal_name', - 'deal_value', - 'search_results_count', - 'cart_products', - // product view - 'product_id', - 'product_name', - 'product_url', - 'product_price', - 'product_brand', - 'product_category_name', - 'product_category_id', - 'product_discount', - // purchase - 'amount', - // set properties - 'currency' - ]); - }); -}); diff --git a/test/lib/shopping-fpti/shopping-event-conversions.test.js b/test/lib/shopping-fpti/shopping-event-conversions.test.js deleted file mode 100644 index ae2a69ab..00000000 --- a/test/lib/shopping-fpti/shopping-event-conversions.test.js +++ /dev/null @@ -1,93 +0,0 @@ -/* global expect jest */ -/* @flow */ - -import { eventToFptiMapperInit } from '../../../src/lib/shopping-fpti/event-handlers'; -import { eventToFptiConverters } from '../../../src/lib/shopping-fpti/shopping-event-conversions'; -import { getUserId, getIdentity } from '../../../src/lib/local-storage'; -import { getDeviceInfo } from '../../../src/lib/get-device-info'; -import { - deviceInfo, - identity, - generatedUserIds, - config, - verifyMainFptiAttributes, - verifyFptiIdentityMappings, - verifyFptiDeviceInfoMappings -} from '../fpti-test-utils'; - -jest.mock('../../../src/lib/shopping-fpti/event-handlers'); -jest.mock('../../../src/lib/local-storage'); -jest.mock('../../../src/lib/get-device-info'); - -const eventToFptiAttributesMock = jest.fn(); - -describe('test event converters to FPTI input', () => { - const eventName = 'page_view'; - const eventPayload = { - user_id: '123', - id: 'HOME' - }; - - const mappedFptiAttributes = { - eventName: 'page_view', - eventData: '{}', - merchantProvidedUserId: 'GNAFQFT5ECSTS', - page: `ppshopping:page_view` - }; - - beforeEach(() => { - eventToFptiMapperInit.mockClear(); - eventToFptiMapperInit.mockReturnValue({ - eventToFptiAttributes: eventToFptiAttributesMock - }); - - getUserId.mockClear(); - getUserId.mockReturnValue(generatedUserIds); - - getIdentity.mockClear(); - getIdentity.mockReturnValue(identity); - - getDeviceInfo.mockClear(); - getDeviceInfo.mockReturnValue(deviceInfo); - - eventToFptiAttributesMock.mockClear(); - eventToFptiAttributesMock.mockReturnValue(mappedFptiAttributes); - }); - - it('should map event data into FPTI input. Not event specific attributes found', () => { - const eventConverters = eventToFptiConverters(config); - const fptiEvent = eventConverters.eventToFpti(eventName, eventPayload); - - verifyMainFptiAttributes(fptiEvent, mappedFptiAttributes); - verifyFptiIdentityMappings(fptiEvent, identity); - verifyFptiDeviceInfoMappings(fptiEvent, deviceInfo); - expect(fptiEvent.mrid).toEqual(config.containerSummary.mrid); - expect(fptiEvent.item).toEqual(config.containerSummary.id); - expect(fptiEvent.e).toEqual('im'); - expect(fptiEvent.flag_consume).toEqual('yes'); - - expect(getIdentity).toHaveBeenCalledTimes(1); - expect(getDeviceInfo).toHaveBeenCalledTimes(1); - expect(eventToFptiAttributesMock).toHaveBeenCalledTimes(1); - }); - - it('should map event data into FPTI input. No device info found', () => { - getDeviceInfo.mockClear(); - getDeviceInfo.mockReturnValue(undefined); - - const eventConverters = eventToFptiConverters(config); - const fptiEvent = eventConverters.eventToFpti(eventName, eventPayload); - - verifyFptiDeviceInfoMappings(fptiEvent, {}); - }); - - it('should map event data into FPTI input. No identity info found', () => { - getIdentity.mockClear(); - getIdentity.mockReturnValue(undefined); - - const eventConverters = eventToFptiConverters(config); - const fptiEvent = eventConverters.eventToFpti(eventName, eventPayload); - - verifyFptiIdentityMappings(fptiEvent, {}); - }); -}); diff --git a/test/lib/shopping-trackers.test.js b/test/lib/shopping-trackers.test.js deleted file mode 100644 index 94736593..00000000 --- a/test/lib/shopping-trackers.test.js +++ /dev/null @@ -1,46 +0,0 @@ -/* global expect jest */ -/* @flow */ -import { setupTrackers } from '../../src/lib/shopping-trackers'; -import { trackFpti } from '../../src/lib/shopping-fpti/shopping-fpti'; -import { PageView } from '../../src/types/shopping-events'; -import { eventToFptiConverters } from '../../src/lib/shopping-fpti/shopping-event-conversions'; - -const config = {}; - -const pageView : PageView = { - id: '427b0021-00b3-4411-bf65-520b13841232', - page_type: 'HOME_PAGE', - category: { - id: 'b1b302f9-1ef9-4cae-b0fd-5735131c80f3', - name: 'Shoes' - } -}; - -const mockFptiInput = { eventData: JSON.stringify(pageView) }; - -jest.mock('../../src/lib/shopping-fpti/shopping-event-conversions'); -jest.mock('../../src/lib/shopping-fpti/shopping-fpti'); - -const eventToFptiMock = jest.fn(); - -describe('test eventTracker setup', () => { - beforeEach(() => { - eventToFptiConverters.mockClear(); - eventToFptiConverters.mockReturnValue({ - eventToFpti: eventToFptiMock - }); - - eventToFptiMock.mockClear(); - eventToFptiMock.mockReturnValue(mockFptiInput); - - trackFpti.mockClear(); - }); - - it('should event trackers include pageView tracker', () => { - const trackers = setupTrackers(config); - trackers.send('page_view', pageView); - expect(eventToFptiMock).toBeCalledWith('page_view', pageView, undefined); - expect(trackFpti).toBeCalledWith(mockFptiInput); - }); - -});