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() }) }) }) })