@benshi.ai/js-sdk
Version:
Benshi SDK
710 lines (551 loc) • 25.1 kB
text/typescript
/**
* @jest-environment jsdom
*/
import BsCore from "../../../core/BsCore"
import CatalogRepository from "../../../core/repositories/catalog/CatalogRepository";
import ECommerce from "../../ECommerce";
import { DeliveryAction, ECommerceTypes } from "../../ECommerce/typings";
import Navigation from ".."
import {
AppAction,
AudioVideoType,
ContentBlock,
IdentifyAction,
ImageType,
MediaActionForImage,
MediaActionForVideo,
NavigationTypes,
NudgeAction,
NudgeResponseType,
RateType,
TrackActions,
TrackTypes,
UserInfo,
} from "../typings";
import { ItemType } from "../../ECommerce/typings";
let windowSpy
let mockSender;
class MockImpressionsDetector {
start() { }
stop() { }
restart() { }
}
describe('Navigation', () => {
beforeEach(() => {
mockSender = {
add() { return false }
}
windowSpy = jest.spyOn(window, "window", "get");
})
afterEach(() => {
// to remove the singleton instance
(BsCore as any).instance = null
})
describe('Identification', () => {
it('Should deliver events with deviceId instead of userId before identification', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
Navigation.logPageEvent({ path: '/mypath', title: 'page-title' })
expect(senderSpy).toBeCalledWith(
expect.stringMatching(DEFAULT_DEVICE_ID),
expect.anything(),
expect.anything(),
expect.anything())
})
it('Should deliver events with userId once the user has been logged', () => {
const DEFAULT_USER_ID = 'dummy_user'
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
bsCore.login(DEFAULT_USER_ID)
Navigation.logPageEvent({ path: '/mypath', title: 'page-title' })
expect(senderSpy).toBeCalledWith(
expect.stringMatching(DEFAULT_USER_ID),
expect.anything(),
expect.anything(),
expect.anything())
})
it('Should stop using user_id after logout', () => {
const DEFAULT_USER_ID = 'dummy_user'
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
bsCore.login(DEFAULT_USER_ID)
Navigation.logPageEvent({ path: '/mypath', title: 'page-title' })
bsCore.logout()
Navigation.logPageEvent({ path: '/mypath', title: 'page-title' })
expect(senderSpy).toBeCalledWith(
expect.stringMatching(DEFAULT_USER_ID),
expect.anything(),
expect.anything(),
expect.anything())
})
})
describe('Navigation', () => {
it('Should deliver an "App" event', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = {
action: AppAction.Background,
start_time: 0
}
Navigation.logAppEvent(event)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
type: NavigationTypes.App,
props: event
}),
expect.anything())
})
it('Should deliver a "Media" event', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = {
type: ImageType.Image,
id: 'id-media-event',
action: MediaActionForImage.Play,
}
const sentEvent = {
type: ImageType.Image,
time: 0,
id_source: 'id-media-event',
id: 'image_id-media-event',
action: MediaActionForImage.Play,
}
Navigation.logMediaEvent(event)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
type: NavigationTypes.Media,
props: sentEvent
}),
expect.anything())
})
it('Should deliver a "Media" event with default type', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = {
type: ImageType.Image,
id: 'id-media-event',
action: MediaActionForImage.Play,
}
Navigation.logMediaEvent(event)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
block: ContentBlock.Core
}),
expect.anything())
})
it('Should deliver a "Rate" event', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = {
rate_value: 4,
type: RateType.App,
subject_id: 'app_id'
}
Navigation.logRateEvent(event)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
block: ContentBlock.Core
}),
expect.anything())
})
it('Should deliver a "Rate" event', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = {
rate_value: 7,
type: RateType.App,
subject_id: 'app_id'
}
expect(() => Navigation.logRateEvent(event)).toThrowError()
})
it('Should deliver a "Track" event', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = {
action: TrackActions.ViewItem,
item_id: "111",
}
const sentEvent = {
action: TrackActions.ViewItem,
item_id: "111",
type: TrackTypes.ReferenceGuide
}
Navigation.logTrackEvent(event)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
type: NavigationTypes.Track,
props: sentEvent
}),
expect.anything())
})
it('Should failed when sending a "Track" event with missing fields', async () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = {
action: TrackActions.ViewItem,
}
await expect(Navigation.logTrackEvent(event)).rejects.toThrowError()
})
it('Should complain when mandatory fields are missing in media catalog', async () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const catalogRepository = new CatalogRepository(
`dummy-url`,
mockSender
)
Navigation.init(catalogRepository)
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = {
type: AudioVideoType.Video,
time: 1000,
id: 'id-media-event',
action: MediaActionForVideo.Play,
}
await expect(Navigation.logMediaEvent(event, { resolution: '1080i' })).rejects.toThrowError()
})
it('Should deliver a "Media" event with e-elearning type', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = {
contentBlock: ContentBlock.ELearning,
type: ImageType.Image,
id: 'id-media-event',
action: MediaActionForImage.Play,
}
Navigation.logMediaEvent(event)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
block: ContentBlock.ELearning
}),
expect.anything())
})
it('Should deliver a "Page" event', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = { path: '/mypath', title: 'page-title' }
Navigation.logPageEvent(event)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
type: NavigationTypes.Page,
props: event
}),
expect.anything())
})
it('Should deliver a "Login" event', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const props: UserInfo = {
action: IdentifyAction.Login,
user_props: {}
}
Navigation.logIdentifyEvent(props)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
type: NavigationTypes.Identify,
props
}),
expect.anything())
})
it('Should deliver a "Delivery" event with the content block set to ECommerce - no call to setCurrentBlock', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = {
id: "1",
action: DeliveryAction.Delivered,
order_id: '11',
}
ECommerce.logDeliveryEvent(event)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
type: ECommerceTypes.Delivery,
block: ContentBlock.ECommerce
}),
expect.anything())
})
it('Should deliver a "Delivery" event with the contentBlock set to ECommerce - call to setCurrentBlock', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = {
id: "1",
action: DeliveryAction.Delivered,
order_id: '11',
}
Navigation.setCurrentBlock(ContentBlock.ELearning)
ECommerce.logDeliveryEvent(event)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
type: ECommerceTypes.Delivery,
block: ContentBlock.ECommerce
}),
expect.anything())
})
it('Should deliver a "Page" event with a default contentBlock that have been overwritten', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.ELearning, DEFAULT_DEVICE_ID)
const event = { path: '/mypath', title: 'page-title' }
Navigation.logPageEvent(event)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
type: NavigationTypes.Page,
block: ContentBlock.ELearning
}),
expect.anything())
})
it('Should deliver a "Page" event with a default contentBlock that have been overwritten', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.ELearning, DEFAULT_DEVICE_ID)
const event = { path: '/mypath', title: 'page-title' }
Navigation.logPageEvent(event)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
type: NavigationTypes.Page,
block: ContentBlock.ELearning
}),
expect.anything())
})
it('Should deliver a "Page" event after setting explicitily the block name', async () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = { path: '/mypath', title: 'page-title' }
Navigation.setCurrentBlock(ContentBlock.ECommerce)
await new Promise((r) => setTimeout(r, 20));
Navigation.logPageEvent(event)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
type: NavigationTypes.Page,
block: ContentBlock.ECommerce
}),
expect.anything())
})
it('Should discard page events where duration is 0', async () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = { path: '/mypath', title: 'page-title', duration: 0 }
Navigation.logPageEvent(event)
expect(senderSpy).toBeCalledTimes(0)
})
it('Should not discard page events if duration is not present', async () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = { path: '/mypath', title: 'page-title' }
Navigation.logPageEvent(event)
expect(senderSpy).toBeCalledTimes(1)
})
it('Should deliver a "PushNotification" event', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = {
nudge_id: 111,
type: NudgeResponseType.Push,
response: {
action: NudgeAction.Discard
}
}
Navigation.logPushNotificationEvent(event)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
type: NavigationTypes.NudgeResponse,
props: event
}),
expect.anything())
})
it('Should deliver a "Search" event with a specific content block', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const userEvent = {
contentBlock: ContentBlock.ECommerce,
page: 0,
query: 'query-string',
results_ids: [{id:'1', type: ItemType.Drug}, {id: '2', type: ItemType.Drug}],
filter: { param: '11' }
}
const internalEvent = {
page: 0,
query: 'query-string',
results_list: [{id:'1', type: ItemType.Drug}, {id: '2', type: ItemType.Drug}],
filter: { param: '11' }
}
Navigation.logSearchEvent(userEvent, '11',true)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
type: NavigationTypes.Search,
props: expect.objectContaining(internalEvent) // we don't care the id in this assertion
}),
expect.anything())
// assert that the SDK has added the search ID
expect((senderSpy.mock.calls[0][2] as any).props.id).toBeDefined()
const eventDelivered = {
block: ContentBlock.ECommerce
}
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(eventDelivered),
expect.anything())
})
it('Should deliver a "Search" event for the default content block', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const event = {
page: 0,
query: 'query-string',
results_ids: ['1', '2'],
filter: { param: '11' }
}
const eventDelivered = {
block: ContentBlock.Core
}
Navigation.logSearchEvent(event, '11', true)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(eventDelivered),
expect.anything())
// assert that the SDK has added the search ID
expect((senderSpy.mock.calls[0][2] as any).props.id).toBeDefined()
})
it('Should deliver a "Search" event adapting list of ID to list of TypedItems', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const userEvent = {
contentBlock: ContentBlock.ECommerce,
page: 0,
query: 'query-string',
results_ids: ['1', '2'],
filter: { param: '11' }
}
const internalEvent = {
page: 0,
query: 'query-string',
results_list: [{id:'1', type: ItemType.Drug}, {id: '2', type: ItemType.Drug}],
filter: { param: '11' }
}
Navigation.logSearchEvent(userEvent, '11',true)
expect(senderSpy).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.objectContaining(
{
type: NavigationTypes.Search,
props: expect.objectContaining(internalEvent) // we don't care the id in this assertion
}),
expect.anything())
})
// it('Should throw an exception on "Search" - malformed list of result_ids, first string, rest objects', () => {
// const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
//
// const senderSpy = jest.spyOn(mockSender, 'add')
// const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
//
// const userEvent = {
// contentBlock: ContentBlock.ECommerce,
// page: 0,
// query: 'query-string',
// results_ids: ['1', {id: '2', type: ItemType.Drug}],
// filter: { param: '11' }
// }
//
//
// expect(async () => Navigation.logSearchEvent(userEvent, '11',true)).rejects.toThrowError()
// })
//
//
// it('Should throw an exception on "Search" - malformed list of result_ids, first object, rest strings', () => {
// const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
//
// const senderSpy = jest.spyOn(mockSender, 'add')
// const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
//
// const userEvent = {
// contentBlock: ContentBlock.ECommerce,
// page: 0,
// query: 'query-string',
// results_ids: [{id: '2', type: ItemType.Drug}, '1'],
// filter: { param: '11' }
// }
//
// expect(async () => Navigation.logSearchEvent(userEvent, '11',true)).rejects.toThrowError()
// })
it('Should throw an exception on "Search" - repeated ids', () => {
const DEFAULT_DEVICE_ID = 'web_sdk_device_id'
const senderSpy = jest.spyOn(mockSender, 'add')
const bsCore = BsCore.createInstance(mockSender as any, MockImpressionsDetector, false, ContentBlock.Core, DEFAULT_DEVICE_ID)
const userEvent = {
contentBlock: ContentBlock.ECommerce,
page: 0,
query: 'query-string',
results_ids: ['1', '1'],
filter: { param: '11' }
}
expect(async () => Navigation.logSearchEvent(userEvent, '11',true)).rejects.toThrowError()
})
})
})