diff --git a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/AltViewOptions.test.js b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/AltViewOptions.test.js index ad8b484a0f..7a0ae849b8 100644 --- a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/AltViewOptions.test.js +++ b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/AltViewOptions.test.js @@ -31,7 +31,7 @@ describe('AltViewOptions', () => { const getLabel = (btnIndex = 0) => getBtn(btnIndex).prop('children'); const props = { traceResultsView: true, - onTraceGraphViewClicked: jest.fn(), + onDdgViewClicked: jest.fn(), }; beforeAll(() => { diff --git a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/AltViewOptions.tsx b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/AltViewOptions.tsx index 4d3254952f..8d70e335ad 100644 --- a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/AltViewOptions.tsx +++ b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/AltViewOptions.tsx @@ -20,7 +20,7 @@ import { getUrl, getUrlState } from '../../DeepDependencies/url'; import { getConfigValue } from '../../../utils/config/get-config'; type Props = { - onTraceGraphViewClicked: () => void; + onDdgViewClicked: () => void; traceResultsView: boolean; }; @@ -31,9 +31,9 @@ function viewAllDep({ ctrlKey, metaKey }: React.MouseEvent) { } export default function AltViewOptions(props: Props) { - const { onTraceGraphViewClicked, traceResultsView } = props; + const { onDdgViewClicked, traceResultsView } = props; const toggleBtn = ( - ); diff --git a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/index.js b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/index.js index 344c45deab..713307ea31 100644 --- a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/index.js +++ b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/index.js @@ -102,7 +102,7 @@ export class UnconnectedSearchResults extends React.PureComponent { + onDdgViewClicked = () => { const { location, history } = this.props; const urlState = queryString.parse(location.search); const view = urlState.view && urlState.view === 'ddg' ? 'traces' : 'ddg'; @@ -177,10 +177,7 @@ export class UnconnectedSearchResults extends React.PureComponent 1 && 's'} {traceResultsView && } - + {showStandaloneLink && ( ', () => { const push = jest.fn(); wrapper.setProps({ history: { push }, location: { search: otherSearch } }); - const toggle = wrapper.find(AltViewOptions).prop('onTraceGraphViewClicked'); + const toggle = wrapper.find(AltViewOptions).prop('onDdgViewClicked'); toggle(); expect(push).toHaveBeenLastCalledWith(getUrl({ [otherParam]: otherValue, [searchParam]: viewDdg })); expect(trackAltViewSpy).toHaveBeenLastCalledWith(viewDdg); diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.test.js b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.test.js new file mode 100644 index 0000000000..4a05c9a2b9 --- /dev/null +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.test.js @@ -0,0 +1,106 @@ +// Copyright (c) 2019 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { Button, Dropdown } from 'antd'; +import { Link } from 'react-router-dom'; + +import AltViewOptions from './AltViewOptions'; +import * as track from './TracePageHeader.track'; + +describe('AltViewOptions', () => { + let trackGanttView; + let trackGraphView; + let trackJsonView; + let trackRawJsonView; + + let wrapper; + const getLink = text => { + const menu = shallow(wrapper.find(Dropdown).prop('overlay')); + const links = menu.find(Link); + for (let i = 0; i < links.length; i++) { + const link = links.at(i); + if (link.children().text() === text) return link; + } + const link = menu.find('a'); + if (link.children().text() === text) return link; + throw new Error(`Could not find "${text}"`); + }; + const props = { + traceGraphView: true, + traceID: 'test trace ID', + onTraceGraphViewClicked: jest.fn(), + }; + + beforeAll(() => { + trackGanttView = jest.spyOn(track, 'trackGanttView'); + trackGraphView = jest.spyOn(track, 'trackGraphView'); + trackJsonView = jest.spyOn(track, 'trackJsonView'); + trackRawJsonView = jest.spyOn(track, 'trackRawJsonView'); + }); + + beforeEach(() => { + jest.clearAllMocks(); + wrapper = shallow(); + }); + + it('renders correctly', () => { + expect(wrapper).toMatchSnapshot(); + }); + + it('tracks viewing JSONs', () => { + expect(trackJsonView).not.toHaveBeenCalled(); + getLink('Trace JSON').simulate('click'); + expect(trackJsonView).toHaveBeenCalledTimes(1); + + expect(trackRawJsonView).not.toHaveBeenCalled(); + getLink('Trace JSON (unadjusted)').simulate('click'); + expect(trackRawJsonView).toHaveBeenCalledTimes(1); + + expect(trackJsonView).toHaveBeenCalledTimes(1); + expect(trackGanttView).not.toHaveBeenCalled(); + expect(trackGraphView).not.toHaveBeenCalled(); + }); + + it('toggles and tracks toggle', () => { + expect(trackGanttView).not.toHaveBeenCalled(); + expect(props.onTraceGraphViewClicked).not.toHaveBeenCalled(); + getLink('Trace Timeline').simulate('click'); + expect(trackGanttView).toHaveBeenCalledTimes(1); + expect(props.onTraceGraphViewClicked).toHaveBeenCalledTimes(1); + + wrapper.setProps({ traceGraphView: false }); + expect(trackGraphView).not.toHaveBeenCalled(); + getLink('Trace Graph').simulate('click'); + expect(trackGraphView).toHaveBeenCalledTimes(1); + expect(props.onTraceGraphViewClicked).toHaveBeenCalledTimes(2); + + wrapper.setProps({ traceGraphView: true }); + expect(trackGanttView).toHaveBeenCalledTimes(1); + wrapper.find(Button).simulate('click'); + expect(trackGanttView).toHaveBeenCalledTimes(2); + expect(props.onTraceGraphViewClicked).toHaveBeenCalledTimes(3); + + wrapper.setProps({ traceGraphView: false }); + expect(trackGraphView).toHaveBeenCalledTimes(1); + wrapper.find(Button).simulate('click'); + expect(trackGraphView).toHaveBeenCalledTimes(2); + expect(props.onTraceGraphViewClicked).toHaveBeenCalledTimes(4); + + expect(trackGanttView).toHaveBeenCalledTimes(2); + expect(trackJsonView).not.toHaveBeenCalled(); + expect(trackRawJsonView).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx index 0bc85be935..6cd41df278 100644 --- a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx @@ -16,7 +16,7 @@ import * as React from 'react'; import { Button, Dropdown, Icon, Menu } from 'antd'; import { Link } from 'react-router-dom'; -import { trackAltViewOpen } from './TracePageHeader.track'; +import { trackGanttView, trackGraphView, trackJsonView, trackRawJsonView } from './TracePageHeader.track'; import prefixUrl from '../../../utils/prefix-url'; type Props = { @@ -27,10 +27,15 @@ type Props = { export default function AltViewOptions(props: Props) { const { onTraceGraphViewClicked, traceGraphView, traceID } = props; + const handleToggleView = () => { + if (traceGraphView) trackGanttView(); + else trackGraphView(); + onTraceGraphViewClicked(); + }; const menu = ( - + {traceGraphView ? 'Trace Timeline' : 'Trace Graph'} @@ -39,7 +44,7 @@ export default function AltViewOptions(props: Props) { to={prefixUrl(`/api/traces/${traceID}?prettyPrint=true`)} rel="noopener noreferrer" target="_blank" - onClick={trackAltViewOpen} + onClick={trackJsonView} > Trace JSON @@ -49,7 +54,7 @@ export default function AltViewOptions(props: Props) { to={prefixUrl(`/api/traces/${traceID}?raw=true&prettyPrint=true`)} rel="noopener noreferrer" target="_blank" - onClick={trackAltViewOpen} + onClick={trackRawJsonView} > Trace JSON (unadjusted) @@ -58,8 +63,8 @@ export default function AltViewOptions(props: Props) { ); return ( - ); diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageHeader.track.test.js b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageHeader.track.test.js new file mode 100644 index 0000000000..3e57bdbbbd --- /dev/null +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageHeader.track.test.js @@ -0,0 +1,81 @@ +// Copyright (c) 2019 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* eslint-disable import/first */ + +jest.mock('../../../utils/tracking'); + +import * as track from './TracePageHeader.track'; /* { + CATEGORY_ALT_VIEW, + CATEGORY_SLIM_HEADER, + ACTION_GANTT, + ACTION_GRAPH, + ACTION_JSON, + ACTION_RAW_JSON, +} from './index.track'; */ +import { trackEvent } from '../../../utils/tracking'; +import { OPEN, CLOSE } from '../../../utils/tracking/common'; + +describe('TracePageHeader.track', () => { + beforeEach(trackEvent.mockClear); + + const cases = [ + { + action: track.ACTION_GANTT, + category: track.CATEGORY_ALT_VIEW, + msg: 'tracks a GA event for viewing gantt chart', + fn: 'trackGanttView', + }, + { + action: track.ACTION_GRAPH, + category: track.CATEGORY_ALT_VIEW, + msg: 'tracks a GA event for viewing trace graph', + fn: 'trackGraphView', + }, + { + action: track.ACTION_JSON, + category: track.CATEGORY_ALT_VIEW, + msg: 'tracks a GA event for viewing trace JSON', + fn: 'trackJsonView', + }, + { + action: track.ACTION_RAW_JSON, + category: track.CATEGORY_ALT_VIEW, + msg: 'tracks a GA event for viewing trace JSON (raw)', + fn: 'trackRawJsonView', + }, + { + action: OPEN, + arg: false, + category: track.CATEGORY_SLIM_HEADER, + msg: 'tracks a GA event for opening slim header', + fn: 'trackSlimHeaderToggle', + }, + { + action: CLOSE, + arg: true, + category: track.CATEGORY_SLIM_HEADER, + msg: 'tracks a GA event for closing slim header', + fn: 'trackSlimHeaderToggle', + }, + ]; + + cases.forEach(({ action, arg, msg, fn, category }) => { + it(msg, () => { + track[fn](arg); + expect(trackEvent.mock.calls.length).toBe(1); + expect(trackEvent.mock.calls[0]).toEqual([category, action]); + }); + }); +}); diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageHeader.track.tsx b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageHeader.track.tsx index 98806061f3..0e60d178c2 100644 --- a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageHeader.track.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageHeader.track.tsx @@ -12,14 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { getToggleValue, OPEN } from '../../../utils/tracking/common'; +import { getToggleValue } from '../../../utils/tracking/common'; import { trackEvent } from '../../../utils/tracking'; -const CATEGORY_ALT_VIEW = 'jaeger/ux/trace/alt-view'; -const CATEGORY_SLIM_HEADER = 'jaeger/ux/trace/slim-header'; +// export for tests +export const CATEGORY_ALT_VIEW = 'jaeger/ux/trace/alt-view'; +export const CATEGORY_SLIM_HEADER = 'jaeger/ux/trace/slim-header'; + +// export for tests +export const ACTION_GANTT = 'gantt'; +export const ACTION_GRAPH = 'graph'; +export const ACTION_JSON = 'json'; +export const ACTION_RAW_JSON = 'rawJson'; // use a closure instead of bind to prevent forwarding any arguments to trackEvent() -export const trackAltViewOpen = () => trackEvent(CATEGORY_ALT_VIEW, OPEN); +export const trackGanttView = () => trackEvent(CATEGORY_ALT_VIEW, ACTION_GANTT); +export const trackGraphView = () => trackEvent(CATEGORY_ALT_VIEW, ACTION_GRAPH); +export const trackJsonView = () => trackEvent(CATEGORY_ALT_VIEW, ACTION_JSON); +export const trackRawJsonView = () => trackEvent(CATEGORY_ALT_VIEW, ACTION_RAW_JSON); export const trackSlimHeaderToggle = (isOpen: boolean) => trackEvent(CATEGORY_SLIM_HEADER, getToggleValue(isOpen)); diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/__snapshots__/AltViewOptions.test.js.snap b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/__snapshots__/AltViewOptions.test.js.snap new file mode 100644 index 0000000000..d3b104a195 --- /dev/null +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/__snapshots__/AltViewOptions.test.js.snap @@ -0,0 +1,64 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AltViewOptions renders correctly 1`] = ` + + + + Trace Timeline + + + + + Trace JSON + + + + + Trace JSON (unadjusted) + + + + } + placement="bottomLeft" + prefixCls="ant-dropdown" +> + + +`;