import React from 'react'
import { act, render, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { inspector, InspectorShell, SegmentEvent } from '../src/index'
jest.mock('@segment/analytics-react', () => ({
useAnalytics: () => ({
track: jest.fn(),
page: jest.fn(),
addSourceMiddleware: jest.fn()
})
}))
describe('Event explorer', () => {
const runMockPageTrace = () =>
inspector.trace({
stage: 'triggered',
id: 'uuid1234',
timestamp: '2021-04-20T19:44:14.701Z',
event: {
type: 'page',
messageId: 'jgiex11',
anonymousId: 'jgik1kmd',
userId: null,
properties: {
path: '/users',
title: 'Hubspot | Users',
url: 'https://hubspot.com/users'
}
}
})
const runMockIdentifyTrace = () =>
inspector.trace({
stage: 'triggered',
id: 'uuid5678',
timestamp: '2021-04-20T19:48:16.701Z',
event: {
type: 'identify',
messageId: 'nvk213',
anonymousId: 'jgik1kmd',
userId: 'steven.smith',
traits: {
email: 'steven.smith@hubspot.com',
firstName: 'Steven',
lastName: 'Smith'
}
}
})
const runMockTrackTrace1 = () =>
inspector.trace({
stage: 'triggered',
id: 'uuid5679',
timestamp: '2021-04-20T19:52:16.701Z',
event: {
type: 'track',
event: 'Add to cart',
messageId: 'oqjdi21',
anonymousId: 'jgik1kmd',
userId: 'steven.smith',
properties: {
productId: 8589814,
name: 'Nike running shoes',
giftwrap: false
}
}
})
const runMockTrackTrace2 = () =>
inspector.trace({
stage: 'triggered',
id: 'uuid5680',
timestamp: '2021-04-20T19:54:16.701Z',
event: {
type: 'track',
event: 'Add to cart',
messageId: 'jciwk34',
anonymousId: 'jgik1kmd',
userId: 'steven.smith',
properties: {
productId: 980921,
name: 'Nike trainer shoes',
giftwrap: false
}
}
})
it('shows a placeholder message until events arrive', () => {
inspector.start()
const screen = render()
expect(screen.getByText(/waiting for events/i)).toBeInTheDocument()
})
it('shows an event list as traces arrive', async () => {
inspector.start()
const screen = render()
act(runMockPageTrace)
expect(screen.queryByText(/waiting for events/i)).not.toBeInTheDocument()
const eventList = screen.getByRole('list', { name: /events/i })
expect(eventList).toBeInTheDocument()
const eventItem1 = within(eventList).getByRole('listitem', {
name: /page event/
})
expect(within(eventItem1).getByText(/hubspot \| users/i)).toBeInTheDocument()
expect(within(eventItem1).getByText(/occurrence time/i)).toBeInTheDocument()
expect(within(eventItem1).getByText(/Apr 20, \d\d:\d\d:\d\d/i)).toBeInTheDocument()
act(runMockIdentifyTrace)
const eventItem2 = await waitFor(() => within(eventList).getByRole('listitem', { name: /identify event/ }))
expect(within(eventItem2).getByText(/steven\.smith/i)).toBeInTheDocument()
expect(within(eventItem2).getByText(/occurrence time/i)).toBeInTheDocument()
expect(within(eventItem2).getByText(/Apr 20, \d\d:\d\d:\d\d/i)).toBeInTheDocument()
})
it('shows event list sorted by most recent events on top', () => {
inspector.start()
const screen = render()
act(runMockPageTrace)
act(runMockIdentifyTrace)
act(runMockTrackTrace1)
const eventList = screen.getByRole('list', { name: /events/i })
expect(eventList.children[0]).toHaveTextContent(/add to cart/i)
expect(eventList.children[1]).toHaveTextContent(/steven\.smith/i)
expect(eventList.children[2]).toHaveTextContent(/hubspot \| users/i)
})
describe('Event details', () => {
it('shows detailed event anatomy upon selecting an event from the list', async () => {
inspector.start()
const screen = render()
act(runMockPageTrace)
act(runMockIdentifyTrace)
const eventList = await waitFor(() => screen.getByRole('list', { name: /events/i }))
expect(eventList).toBeTruthy()
const eventItem1 = within(eventList).getByRole('listitem', {
name: /page event/
})
expect(screen.getByText(/Select an event to view its anatomy/i)).toBeInTheDocument()
userEvent.click(eventItem1)
await waitFor(() => expect(screen.queryByText(/Select an event to view its anatomy/i)).not.toBeInTheDocument())
expect(screen.getByRole('heading', { name: /page event/i })).toBeInTheDocument()
expect(screen.getByRole('rowheader', { name: /occurrence time/i })).toBeInTheDocument()
expect(screen.getByRole('rowheader', { name: /status/i })).toBeInTheDocument()
expect(screen.getByRole('cell', { name: /enqueued/i })).toBeInTheDocument()
expect(screen.getByRole('rowheader', { name: /problems/i })).toBeInTheDocument()
expect(screen.getByRole('cell', { name: /all clear/i })).toBeInTheDocument()
})
it('shows reported event errors in the detail panel', async () => {
inspector.start()
const screen = render()
const traceId = 'uuid919e1'
const event: SegmentEvent = {
type: 'track',
messageId: 'jgiex11',
anonymousId: 'jgik1kmd',
event: 'Add to cart',
userId: null,
properties: {
path: '/users',
title: 'Hubspot | Users',
url: 'https://hubspot.com/users'
}
}
act(() =>
inspector.trace({
id: traceId,
timestamp: '2021-04-20T19:44:14.701Z',
stage: 'triggered',
event
})
)
act(() =>
inspector.trace({
id: traceId,
timestamp: '2021-04-20T19:44:14.701Z',
type: 'error',
fatal: true,
message: 'Tracking plan violation',
description: 'This event does not match with any of the pre-defined events',
event
})
)
const eventList = await waitFor(() => screen.getByRole('list', { name: /events/i }))
expect(eventList).toBeTruthy()
const eventItem1 = within(eventList).getByRole('listitem', {
name: /track event/
})
userEvent.click(eventItem1)
await waitFor(() => expect(screen.queryByText(/Select an event to view its anatomy/i)).not.toBeInTheDocument())
expect(screen.getByRole('rowheader', { name: /status/i })).toBeInTheDocument()
expect(screen.getByRole('cell', { name: /undeliverable/i })).toBeInTheDocument()
expect(screen.getByRole('rowheader', { name: /problems/i })).toBeInTheDocument()
expect(screen.getByRole('cell', { name: /1 error/i })).toBeInTheDocument()
expect(screen.getByRole('heading', { name: 'Tracking plan violation' })).toBeInTheDocument()
expect(screen.getByText(/this event does not match with any of the pre-defined/i)).toBeInTheDocument()
})
})
describe('Event filtering', () => {
it('filters events list by search query', async () => {
inspector.start()
const screen = render()
act(runMockPageTrace)
act(runMockIdentifyTrace)
act(runMockTrackTrace1)
act(runMockTrackTrace2)
const eventList = await waitFor(() => screen.getByRole('list', { name: /events/i }))
expect(
within(eventList).getAllByRole('listitem', {
name: /^\w+ event$/i
})
).toHaveLength(4)
const queryInput = screen.getByRole('textbox', { name: /filter events by query/i })
expect(queryInput).toHaveDisplayValue('')
userEvent.type(queryInput, 'shoes')
await waitFor(() => {
expect(
within(eventList).getAllByRole('listitem', {
name: /^\w+ event$/i
})
).toHaveLength(2)
// the query matched text should be highlighted
expect(screen.getAllByText('shoes', { selector: 'mark' })).toHaveLength(2)
})
userEvent.clear(queryInput)
expect(
within(eventList).getAllByRole('listitem', {
name: /^\w+ event$/i
})
).toHaveLength(4)
userEvent.click(screen.getByRole('button', { name: /all events/i }))
await waitFor(() => userEvent.click(screen.getByRole('checkbox', { name: /identify event/i })))
// only 1 matching event
expect(
within(eventList).getByRole('listitem', {
name: /^\w+ event$/i
})
).toHaveAccessibleName(/identify event/)
})
it('filters on query by doing a lookup only in the relevant attributes of events', async () => {
inspector.start()
const screen = render()
act(() =>
inspector.trace({
stage: 'triggered',
id: 'uuid5245',
timestamp: '2021-04-20T19:52:16.701Z',
event: {
type: 'identify',
traits: {
firstName: 'smith'
},
// irrelevant - these are all not an area of interest
messageId: 'abc',
anonymousId: 'abc',
userId: 'abc',
integrations: {
abc: 'abc'
},
context: {
abc: 'abc'
}
}
})
)
act(() =>
inspector.trace({
stage: 'triggered',
id: 'uuid5680',
timestamp: '2021-04-20T19:52:16.701Z',
event: {
type: 'track',
event: 'Add to cart',
// irrelevant - these are all not an area of interest
messageId: 'abc',
anonymousId: 'abc',
userId: 'abc',
integrations: {
abc: 'abc'
},
context: {
abc: 'abc'
}
}
})
)
const eventList = await waitFor(() => screen.getByRole('list', { name: /events/i }))
expect(
within(eventList).getAllByRole('listitem', {
name: /^\w+ event$/i
})
).toHaveLength(2)
const queryInput = screen.getByRole('textbox', { name: /filter events by query/i })
userEvent.type(queryInput, 'abc')
await waitFor(() => {
expect(screen.getByText(/No events match your filters/)).toBeInTheDocument()
expect(eventList).not.toBeInTheDocument()
})
})
})
})