1313// limitations under the License.
1414
1515import * as React from 'react' ;
16- import { shallow } from 'enzyme' ;
17- import { Dropdown } from 'antd' ;
16+ import { render , screen , fireEvent , cleanup } from '@testing-library/react' ;
17+ import '@testing-library/jest-dom' ;
18+ import { BrowserRouter } from 'react-router-dom' ;
1819import AltViewOptions from './AltViewOptions' ;
1920import * as track from './TracePageHeader.track' ;
2021import { ETraceViewType } from '../types' ;
2122
23+ jest . mock ( 'antd' , ( ) => {
24+ const originalModule = jest . requireActual ( 'antd' ) ;
25+ return {
26+ ...originalModule ,
27+ Dropdown : ( { children, menu } ) => (
28+ < div data-testid = "dropdown" >
29+ { children }
30+ < div data-testid = "dropdown-menu" >
31+ { menu . items . map ( item => (
32+ < div
33+ key = { item . key }
34+ data-testid = { `menu-item-${ item . key } ` }
35+ onClick = { ( ) => {
36+ // Simulate clicking the link/button inside
37+ if ( item . label ?. props ?. onClick ) {
38+ item . label . props . onClick ( ) ;
39+ }
40+ } }
41+ >
42+ { item . label }
43+ </ div >
44+ ) ) }
45+ </ div >
46+ </ div >
47+ ) ,
48+ Button : ( { children, className } ) => (
49+ < button type = "button" className = { className } data-testid = "dropdown-button" >
50+ { children }
51+ </ button >
52+ ) ,
53+ } ;
54+ } ) ;
55+
2256describe ( 'AltViewOptions' , ( ) => {
2357 let trackGanttView ;
2458 let trackGraphView ;
@@ -27,20 +61,11 @@ describe('AltViewOptions', () => {
2761 let trackStatisticsView ;
2862 let trackTraceSpansView ;
2963
30- let wrapper ;
31- const getLink = text => {
32- const links = wrapper . find ( Dropdown ) . prop ( 'menu' ) . items ;
33- for ( let i = 0 ; i < links . length ; i ++ ) {
34- const link = links [ i ] ;
35- if ( link . label . props . children === text ) return link . label . props ;
36- }
37- throw new Error ( `Could not find "${ text } "` ) ;
38- } ;
39-
4064 const props = {
4165 viewType : ETraceViewType . TraceTimelineViewer ,
4266 traceID : 'test trace ID' ,
4367 onTraceViewChange : jest . fn ( ) ,
68+ disableJsonView : false ,
4469 } ;
4570
4671 beforeAll ( ( ) => {
@@ -52,78 +77,153 @@ describe('AltViewOptions', () => {
5277 trackTraceSpansView = jest . spyOn ( track , 'trackTraceSpansView' ) ;
5378 } ) ;
5479
55- beforeEach ( ( ) => {
80+ afterEach ( ( ) => {
5681 jest . clearAllMocks ( ) ;
57- wrapper = shallow ( < AltViewOptions { ...props } /> ) ;
82+ cleanup ( ) ;
83+ } ) ;
84+
85+ const renderComponent = ( customProps = { } ) => {
86+ const mergedProps = { ...props , ...customProps } ;
87+ return render (
88+ < BrowserRouter >
89+ < AltViewOptions { ...mergedProps } />
90+ </ BrowserRouter >
91+ ) ;
92+ } ;
93+
94+ it ( 'renders dropdown button displaying the current view type' , ( ) => {
95+ renderComponent ( ) ;
96+ const button = screen . getByTestId ( 'dropdown-button' ) ;
97+ expect ( button ) . toHaveTextContent ( 'Trace Timeline' ) ;
98+ } ) ;
99+
100+ it ( 'shows "Alternate Views" when viewType is not in MENU_ITEMS' , ( ) => {
101+ renderComponent ( { viewType : 'UnknownViewType' } ) ;
102+ const button = screen . getByTestId ( 'dropdown-button' ) ;
103+ expect ( button ) . toHaveTextContent ( 'Alternate Views' ) ;
104+ } ) ;
105+
106+ it ( 'shows all alternate view options except current view' , ( ) => {
107+ renderComponent ( ) ;
108+
109+ expect ( screen . queryByTestId ( 'menu-item-TraceTimelineViewer' ) ) . not . toBeInTheDocument ( ) ;
110+
111+ expect ( screen . getByTestId ( 'menu-item-TraceGraph' ) ) . toBeInTheDocument ( ) ;
112+ expect ( screen . getByTestId ( 'menu-item-TraceStatistics' ) ) . toBeInTheDocument ( ) ;
113+ expect ( screen . getByTestId ( 'menu-item-TraceSpansView' ) ) . toBeInTheDocument ( ) ;
114+ expect ( screen . getByTestId ( 'menu-item-TraceFlamegraph' ) ) . toBeInTheDocument ( ) ;
115+ expect ( screen . getByTestId ( 'menu-item-trace-json' ) ) . toBeInTheDocument ( ) ;
116+ expect ( screen . getByTestId ( 'menu-item-trace-json-unadjusted' ) ) . toBeInTheDocument ( ) ;
58117 } ) ;
59118
60- it ( 'renders correctly' , ( ) => {
61- expect ( wrapper ) . toMatchSnapshot ( ) ;
119+ it ( 'hides json links when disableJsonView is true' , ( ) => {
120+ renderComponent ( { disableJsonView : true } ) ;
121+
122+ expect ( screen . queryByTestId ( 'menu-item-trace-json' ) ) . not . toBeInTheDocument ( ) ;
123+ expect ( screen . queryByTestId ( 'menu-item-trace-json-unadjusted' ) ) . not . toBeInTheDocument ( ) ;
124+
125+ expect ( screen . getByTestId ( 'menu-item-TraceGraph' ) ) . toBeInTheDocument ( ) ;
126+ expect ( screen . getByTestId ( 'menu-item-TraceStatistics' ) ) . toBeInTheDocument ( ) ;
62127 } ) ;
63128
64- it ( 'disables json links because disableJsonView is true' , ( ) => {
65- wrapper . setProps ( { disableJsonView : true } ) ;
129+ it ( 'tracks and changes view for Trace Graph' , ( ) => {
130+ renderComponent ( { viewType : ETraceViewType . TraceTimelineViewer } ) ;
131+ const menuItem = screen . getByTestId ( 'menu-item-TraceGraph' ) ;
132+
133+ fireEvent . click ( menuItem ) ;
66134
67- expect ( ( ) => getLink ( 'Trace JSON' ) ) . toThrow ( 'Could not find "Trace JSON"' ) ;
68- expect ( ( ) => getLink ( 'Trace JSON (unadjusted)' ) ) . toThrow ( 'Could not find "Trace JSON (unadjusted)"' ) ;
135+ expect ( props . onTraceViewChange ) . toHaveBeenCalledWith ( ETraceViewType . TraceGraph ) ;
136+ expect ( trackGraphView ) . toHaveBeenCalledTimes ( 1 ) ;
69137 } ) ;
70138
71- it ( 'tracks viewing JSONs' , ( ) => {
72- expect ( trackJsonView ) . not . toHaveBeenCalled ( ) ;
73- getLink ( 'Trace JSON' ) . onClick ( ) ;
74- expect ( trackJsonView ) . toHaveBeenCalledTimes ( 1 ) ;
139+ it ( 'tracks and changes view for Trace Statistics' , ( ) => {
140+ renderComponent ( { viewType : ETraceViewType . TraceGraph } ) ;
141+ const menuItem = screen . getByTestId ( 'menu-item-TraceStatistics' ) ;
75142
76- expect ( trackRawJsonView ) . not . toHaveBeenCalled ( ) ;
77- getLink ( 'Trace JSON (unadjusted)' ) . onClick ( ) ;
78- expect ( trackRawJsonView ) . toHaveBeenCalledTimes ( 1 ) ;
143+ fireEvent . click ( menuItem ) ;
144+
145+ expect ( props . onTraceViewChange ) . toHaveBeenCalledWith ( ETraceViewType . TraceStatistics ) ;
146+ expect ( trackStatisticsView ) . toHaveBeenCalledTimes ( 1 ) ;
147+ } ) ;
148+
149+ it ( 'tracks and changes view for Trace Timeline' , ( ) => {
150+ renderComponent ( { viewType : ETraceViewType . TraceStatistics } ) ;
151+ const menuItem = screen . getByTestId ( 'menu-item-TraceTimelineViewer' ) ;
152+
153+ fireEvent . click ( menuItem ) ;
154+
155+ expect ( props . onTraceViewChange ) . toHaveBeenCalledWith ( ETraceViewType . TraceTimelineViewer ) ;
156+ expect ( trackGanttView ) . toHaveBeenCalledTimes ( 1 ) ;
157+ } ) ;
158+
159+ it ( 'tracks and changes view for Trace Spans Table' , ( ) => {
160+ renderComponent ( { viewType : ETraceViewType . TraceTimelineViewer } ) ;
161+ const menuItem = screen . getByTestId ( 'menu-item-TraceSpansView' ) ;
162+
163+ fireEvent . click ( menuItem ) ;
164+
165+ expect ( props . onTraceViewChange ) . toHaveBeenCalledWith ( ETraceViewType . TraceSpansView ) ;
166+ expect ( trackTraceSpansView ) . toHaveBeenCalledTimes ( 1 ) ;
167+ } ) ;
168+
169+ it ( 'does not track or change view for Trace Flamegraph' , ( ) => {
170+ renderComponent ( { viewType : ETraceViewType . TraceTimelineViewer } ) ;
171+ const menuItem = screen . getByTestId ( 'menu-item-TraceFlamegraph' ) ;
172+
173+ fireEvent . click ( menuItem ) ;
174+
175+ expect ( props . onTraceViewChange ) . toHaveBeenCalledWith ( ETraceViewType . TraceFlamegraph ) ;
79176
80- expect ( trackJsonView ) . toHaveBeenCalledTimes ( 1 ) ;
81177 expect ( trackGanttView ) . not . toHaveBeenCalled ( ) ;
82178 expect ( trackGraphView ) . not . toHaveBeenCalled ( ) ;
179+ expect ( trackStatisticsView ) . not . toHaveBeenCalled ( ) ;
83180 expect ( trackTraceSpansView ) . not . toHaveBeenCalled ( ) ;
84181 } ) ;
85182
86- it ( 'track dropdown menu' , ( ) => {
87- const viewInteractions = [
88- {
89- link : 'Trace Graph' ,
90- trackFn : trackGraphView ,
91- onTraceViewChangeArg : ETraceViewType . TraceGraph ,
92- } ,
93- {
94- link : 'Trace Statistics' ,
95- trackFn : trackStatisticsView ,
96- onTraceViewChangeArg : ETraceViewType . TraceStatisticsView ,
97- propViewType : ETraceViewType . TraceGraph ,
98- } ,
99- {
100- link : 'Trace Timeline' ,
101- trackFn : trackGanttView ,
102- onTraceViewChangeArg : ETraceViewType . TraceTimelineViewer ,
103- propViewType : ETraceViewType . TraceStatisticsView ,
104- } ,
105- {
106- link : 'Trace Spans Table' ,
107- trackFn : trackTraceSpansView ,
108- onTraceViewChangeArg : ETraceViewType . TraceSpansView ,
109- propViewType : ETraceViewType . TraceTimelineViewer ,
110- } ,
183+ it ( 'renders JSON links with correct URLs' , ( ) => {
184+ renderComponent ( ) ;
185+
186+ const jsonMenuItem = screen . getByTestId ( 'menu-item-trace-json' ) ;
187+ const jsonLink = jsonMenuItem . querySelector ( 'a' ) ;
188+ expect ( jsonLink ) . toHaveAttribute ( 'href' , '/api/traces/test trace ID?prettyPrint=true' ) ;
189+
190+ const rawJsonMenuItem = screen . getByTestId ( 'menu-item-trace-json-unadjusted' ) ;
191+ const rawJsonLink = rawJsonMenuItem . querySelector ( 'a' ) ;
192+ expect ( rawJsonLink ) . toHaveAttribute ( 'href' , '/api/traces/test trace ID?raw=true&prettyPrint=true' ) ;
193+ } ) ;
194+
195+ it ( 'updates dropdown text when view type changes' , ( ) => {
196+ const { rerender } = renderComponent ( { viewType : ETraceViewType . TraceTimelineViewer } ) ;
197+ expect ( screen . getByTestId ( 'dropdown-button' ) ) . toHaveTextContent ( 'Trace Timeline' ) ;
198+
199+ const rerenderWithViewType = ( viewType , expectedText ) => {
200+ rerender (
201+ < BrowserRouter >
202+ < AltViewOptions { ...props } viewType = { viewType } />
203+ </ BrowserRouter >
204+ ) ;
205+ expect ( screen . getByTestId ( 'dropdown-button' ) ) . toHaveTextContent ( expectedText ) ;
206+ } ;
207+
208+ rerenderWithViewType ( ETraceViewType . TraceGraph , 'Trace Graph' ) ;
209+ rerenderWithViewType ( ETraceViewType . TraceStatistics , 'Trace Statistics' ) ;
210+ rerenderWithViewType ( ETraceViewType . TraceSpansView , 'Trace Spans Table' ) ;
211+ rerenderWithViewType ( ETraceViewType . TraceFlamegraph , 'Trace Flamegraph' ) ;
212+ } ) ;
213+
214+ it ( 'excludes current view from dropdown options for all view types' , ( ) => {
215+ const viewTypes = [
216+ { type : ETraceViewType . TraceTimelineViewer , testId : 'menu-item-TraceTimelineViewer' } ,
217+ { type : ETraceViewType . TraceGraph , testId : 'menu-item-TraceGraph' } ,
218+ { type : ETraceViewType . TraceStatistics , testId : 'menu-item-TraceStatistics' } ,
219+ { type : ETraceViewType . TraceSpansView , testId : 'menu-item-TraceSpansView' } ,
220+ { type : ETraceViewType . TraceFlamegraph , testId : 'menu-item-TraceFlamegraph' } ,
111221 ] ;
112222
113- viewInteractions . forEach ( ( { link, trackFn, propViewType } , i ) => {
114- if ( propViewType ) {
115- wrapper . setProps ( {
116- viewType : propViewType ,
117- } ) ;
118- }
119- expect ( props . onTraceViewChange ) . toHaveBeenCalledTimes ( i ) ;
120- expect ( trackFn ) . not . toHaveBeenCalled ( ) ;
121-
122- getLink ( link ) . onClick ( ) ;
123- expect ( props . onTraceViewChange ) . toHaveBeenCalledTimes ( i + 1 ) ;
124- viewInteractions . forEach ( ( { trackFn : fn } , j ) => {
125- expect ( fn ) . toHaveBeenCalledTimes ( j <= i ? 1 : 0 ) ;
126- } ) ;
223+ viewTypes . forEach ( ( { type, testId } ) => {
224+ cleanup ( ) ;
225+ renderComponent ( { viewType : type } ) ;
226+ expect ( screen . queryByTestId ( testId ) ) . not . toBeInTheDocument ( ) ;
127227 } ) ;
128228 } ) ;
129229} ) ;
0 commit comments