TraceView.test.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. import { TopOfViewRefType } from '@jaegertracing/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView';
  2. import { TraceData, TraceSpanData } from '@jaegertracing/jaeger-ui-components/src/types/trace';
  3. import { render, prettyDOM, screen } from '@testing-library/react';
  4. import userEvent from '@testing-library/user-event';
  5. import React, { createRef } from 'react';
  6. import { Provider } from 'react-redux';
  7. import { DataFrame, MutableDataFrame, getDefaultTimeRange, LoadingState } from '@grafana/data';
  8. import { setDataSourceSrv } from '@grafana/runtime';
  9. import { ExploreId } from 'app/types';
  10. import { configureStore } from '../../../store/configureStore';
  11. import { TraceView } from './TraceView';
  12. import { transformDataFrames } from './utils/transform';
  13. function getTraceView(frames: DataFrame[]) {
  14. const store = configureStore();
  15. const mockPanelData = {
  16. state: LoadingState.Done,
  17. series: [],
  18. timeRange: getDefaultTimeRange(),
  19. };
  20. const topOfViewRef = createRef<HTMLDivElement>();
  21. const traceView = (
  22. <Provider store={store}>
  23. <TraceView
  24. exploreId={ExploreId.left}
  25. dataFrames={frames}
  26. splitOpenFn={() => {}}
  27. traceProp={transformDataFrames(frames[0])!}
  28. search=""
  29. focusedSpanIdForSearch=""
  30. queryResponse={mockPanelData}
  31. datasource={undefined}
  32. topOfViewRef={topOfViewRef}
  33. topOfViewRefType={TopOfViewRefType.Explore}
  34. />
  35. </Provider>
  36. );
  37. return traceView;
  38. }
  39. function renderTraceView(frames = [frameOld]) {
  40. const { container, baseElement } = render(getTraceView(frames));
  41. return {
  42. header: container.children[0],
  43. timeline: container.children[1],
  44. container,
  45. baseElement,
  46. };
  47. }
  48. function renderTraceViewNew() {
  49. return renderTraceView([frameNew]);
  50. }
  51. describe('TraceView', () => {
  52. beforeAll(() => {
  53. setDataSourceSrv({
  54. getInstanceSettings() {
  55. return undefined;
  56. },
  57. } as any);
  58. });
  59. it('renders TraceTimelineViewer', () => {
  60. const { timeline, header } = renderTraceView();
  61. expect(timeline).toBeDefined();
  62. expect(header).toBeDefined();
  63. });
  64. it('renders TraceTimelineViewer with new format', () => {
  65. const { timeline, header } = renderTraceViewNew();
  66. expect(timeline).toBeDefined();
  67. expect(header).toBeDefined();
  68. });
  69. it('renders renders the same for old and new format', () => {
  70. const { baseElement } = renderTraceViewNew();
  71. const { baseElement: baseElementOld } = renderTraceView();
  72. expect(prettyDOM(baseElement)).toEqual(prettyDOM(baseElementOld));
  73. });
  74. it('only renders noDataMsg on missing trace', () => {
  75. // Simulating Explore's access to empty response data
  76. const { container } = renderTraceView([]);
  77. expect(container.childNodes.length === 1).toBeTruthy();
  78. });
  79. it('toggles detailState', async () => {
  80. renderTraceViewNew();
  81. expect(screen.queryByText(/Tags/)).toBeFalsy();
  82. const spanView = screen.getAllByText('', { selector: 'div[data-testid="span-view"]' })[0];
  83. await userEvent.click(spanView);
  84. expect(screen.queryByText(/Tags/)).toBeTruthy();
  85. await userEvent.click(spanView);
  86. screen.debug(screen.queryAllByText(/Tags/));
  87. expect(screen.queryByText(/Tags/)).toBeFalsy();
  88. });
  89. it('shows timeline ticks', () => {
  90. renderTraceViewNew();
  91. function ticks() {
  92. return screen.getByText('', { selector: 'div[data-test-id="TimelineHeaderRow"]' }).children[1].children[1]
  93. .textContent;
  94. }
  95. expect(ticks()).toBe('0μs274.5μs549μs823.5μs1.1ms');
  96. });
  97. it('correctly shows processes for each span', async () => {
  98. renderTraceView();
  99. let table: HTMLElement;
  100. expect(screen.queryAllByText('', { selector: 'div[data-testid="span-view"]' }).length).toBe(3);
  101. const firstSpan = screen.getAllByText('', { selector: 'div[data-testid="span-view"]' })[0];
  102. await userEvent.click(firstSpan);
  103. await userEvent.click(screen.getByText(/Process/));
  104. table = screen.getByText('', { selector: 'div[data-test-id="KeyValueTable"]' });
  105. expect(table.innerHTML).toContain('client-uuid-1');
  106. await userEvent.click(firstSpan);
  107. const secondSpan = screen.getAllByText('', { selector: 'div[data-testid="span-view"]' })[1];
  108. await userEvent.click(secondSpan);
  109. await userEvent.click(screen.getByText(/Process/));
  110. table = screen.getByText('', { selector: 'div[data-test-id="KeyValueTable"]' });
  111. expect(table.innerHTML).toContain('client-uuid-2');
  112. await userEvent.click(secondSpan);
  113. const thirdSpan = screen.getAllByText('', { selector: 'div[data-testid="span-view"]' })[2];
  114. await userEvent.click(thirdSpan);
  115. await userEvent.click(screen.getByText(/Process/));
  116. table = screen.getByText('', { selector: 'div[data-test-id="KeyValueTable"]' });
  117. expect(table.innerHTML).toContain('client-uuid-3');
  118. });
  119. it('resets detail view for new trace with the identical spanID', async () => {
  120. const { rerender } = render(getTraceView([frameOld]));
  121. const span = screen.getAllByText('', { selector: 'div[data-testid="span-view"]' })[2];
  122. await userEvent.click(span);
  123. //Process is in detail view
  124. expect(screen.getByText(/Process/)).toBeInTheDocument();
  125. rerender(getTraceView([frameNew]));
  126. expect(screen.queryByText(/Process/)).not.toBeInTheDocument();
  127. });
  128. });
  129. const response: TraceData & { spans: TraceSpanData[] } = {
  130. traceID: '1ed38015486087ca',
  131. spans: [
  132. {
  133. traceID: '1ed38015486087ca',
  134. spanID: '1ed38015486087ca',
  135. flags: 1,
  136. operationName: 'HTTP POST - api_prom_push',
  137. references: [] as any,
  138. startTime: 1585244579835187,
  139. duration: 1098,
  140. tags: [
  141. { key: 'sampler.type', type: 'string', value: 'const' },
  142. { key: 'sampler.param', type: 'bool', value: true },
  143. { key: 'span.kind', type: 'string', value: 'server' },
  144. { key: 'http.method', type: 'string', value: 'POST' },
  145. { key: 'http.url', type: 'string', value: '/api/prom/push' },
  146. { key: 'component', type: 'string', value: 'net/http' },
  147. { key: 'http.status_code', type: 'int64', value: 204 },
  148. { key: 'internal.span.format', type: 'string', value: 'proto' },
  149. ],
  150. logs: [
  151. {
  152. timestamp: 1585244579835229,
  153. fields: [{ key: 'event', type: 'string', value: 'util.ParseProtoRequest[start reading]' }],
  154. },
  155. {
  156. timestamp: 1585244579835241,
  157. fields: [
  158. { key: 'event', type: 'string', value: 'util.ParseProtoRequest[decompress]' },
  159. { key: 'size', type: 'int64', value: 315 },
  160. ],
  161. },
  162. {
  163. timestamp: 1585244579835245,
  164. fields: [
  165. { key: 'event', type: 'string', value: 'util.ParseProtoRequest[unmarshal]' },
  166. { key: 'size', type: 'int64', value: 446 },
  167. ],
  168. },
  169. ],
  170. processID: '1ed38015486087ca',
  171. warnings: null as any,
  172. },
  173. {
  174. traceID: '1ed38015486087ca',
  175. spanID: '3fb050342773d333',
  176. flags: 1,
  177. operationName: '/logproto.Pusher/Push',
  178. references: [{ refType: 'CHILD_OF', traceID: '1ed38015486087ca', spanID: '1ed38015486087ca' }],
  179. startTime: 1585244579835341,
  180. duration: 921,
  181. tags: [
  182. { key: 'span.kind', type: 'string', value: 'client' },
  183. { key: 'component', type: 'string', value: 'gRPC' },
  184. { key: 'internal.span.format', type: 'string', value: 'proto' },
  185. ],
  186. logs: [],
  187. processID: '3fb050342773d333',
  188. warnings: null,
  189. },
  190. {
  191. traceID: '1ed38015486087ca',
  192. spanID: '35118c298fc91f68',
  193. flags: 1,
  194. operationName: '/logproto.Pusher/Push',
  195. references: [{ refType: 'CHILD_OF', traceID: '1ed38015486087ca', spanID: '3fb050342773d333' }],
  196. startTime: 1585244579836040,
  197. duration: 36,
  198. tags: [
  199. { key: 'span.kind', type: 'string', value: 'server' },
  200. { key: 'component', type: 'string', value: 'gRPC' },
  201. { key: 'internal.span.format', type: 'string', value: 'proto' },
  202. ],
  203. logs: [] as any,
  204. processID: '35118c298fc91f68',
  205. warnings: null as any,
  206. },
  207. ],
  208. processes: {
  209. '1ed38015486087ca': {
  210. serviceName: 'loki-all',
  211. tags: [
  212. { key: 'client-uuid', type: 'string', value: 'client-uuid-1' },
  213. { key: 'hostname', type: 'string', value: '0080b530fae3' },
  214. { key: 'ip', type: 'string', value: '172.18.0.6' },
  215. { key: 'jaeger.version', type: 'string', value: 'Go-2.20.1' },
  216. ],
  217. },
  218. '3fb050342773d333': {
  219. serviceName: 'loki-all',
  220. tags: [
  221. { key: 'client-uuid', type: 'string', value: 'client-uuid-2' },
  222. { key: 'hostname', type: 'string', value: '0080b530fae3' },
  223. { key: 'ip', type: 'string', value: '172.18.0.6' },
  224. { key: 'jaeger.version', type: 'string', value: 'Go-2.20.1' },
  225. ],
  226. },
  227. '35118c298fc91f68': {
  228. serviceName: 'loki-all',
  229. tags: [
  230. { key: 'client-uuid', type: 'string', value: 'client-uuid-3' },
  231. { key: 'hostname', type: 'string', value: '0080b530fae3' },
  232. { key: 'ip', type: 'string', value: '172.18.0.6' },
  233. { key: 'jaeger.version', type: 'string', value: 'Go-2.20.1' },
  234. ],
  235. },
  236. },
  237. warnings: null as any,
  238. };
  239. export const frameOld = new MutableDataFrame({
  240. fields: [
  241. {
  242. name: 'trace',
  243. values: [response],
  244. },
  245. ],
  246. meta: {
  247. preferredVisualisationType: 'trace',
  248. },
  249. });
  250. const frameNew = new MutableDataFrame({
  251. fields: [
  252. { name: 'traceID', values: ['1ed38015486087ca', '1ed38015486087ca', '1ed38015486087ca'] },
  253. { name: 'spanID', values: ['1ed38015486087ca', '3fb050342773d333', '35118c298fc91f68'] },
  254. { name: 'parentSpanID', values: [undefined, '1ed38015486087ca', '3fb050342773d333'] },
  255. { name: 'operationName', values: ['HTTP POST - api_prom_push', '/logproto.Pusher/Push', '/logproto.Pusher/Push'] },
  256. { name: 'serviceName', values: ['loki-all', 'loki-all', 'loki-all'] },
  257. {
  258. name: 'serviceTags',
  259. values: [
  260. [
  261. { key: 'client-uuid', value: '2a59d08899ef6a8a' },
  262. { key: 'hostname', value: '0080b530fae3' },
  263. { key: 'ip', value: '172.18.0.6' },
  264. { key: 'jaeger.version', value: 'Go-2.20.1' },
  265. ],
  266. [
  267. { key: 'client-uuid', value: '2a59d08899ef6a8a' },
  268. { key: 'hostname', value: '0080b530fae3' },
  269. { key: 'ip', value: '172.18.0.6' },
  270. { key: 'jaeger.version', value: 'Go-2.20.1' },
  271. ],
  272. [
  273. { key: 'client-uuid', value: '2a59d08899ef6a8a' },
  274. { key: 'hostname', value: '0080b530fae3' },
  275. { key: 'ip', value: '172.18.0.6' },
  276. { key: 'jaeger.version', value: 'Go-2.20.1' },
  277. ],
  278. ],
  279. },
  280. { name: 'startTime', values: [1585244579835.187, 1585244579835.341, 1585244579836.04] },
  281. { name: 'duration', values: [1.098, 0.921, 0.036] },
  282. {
  283. name: 'logs',
  284. values: [
  285. [
  286. {
  287. timestamp: 1585244579835.229,
  288. fields: [{ key: 'event', value: 'util.ParseProtoRequest[start reading]' }],
  289. },
  290. {
  291. timestamp: 1585244579835.241,
  292. fields: [
  293. { key: 'event', value: 'util.ParseProtoRequest[decompress]' },
  294. { key: 'size', value: 315 },
  295. ],
  296. },
  297. {
  298. timestamp: 1585244579835.245,
  299. fields: [
  300. { key: 'event', value: 'util.ParseProtoRequest[unmarshal]' },
  301. { key: 'size', value: 446 },
  302. ],
  303. },
  304. ],
  305. [],
  306. [],
  307. ],
  308. },
  309. {
  310. name: 'tags',
  311. values: [
  312. [
  313. { key: 'sampler.type', value: 'const' },
  314. { key: 'sampler.param', value: true },
  315. { key: 'span.kind', value: 'server' },
  316. { key: 'http.method', value: 'POST' },
  317. { key: 'http.url', value: '/api/prom/push' },
  318. { key: 'component', value: 'net/http' },
  319. { key: 'http.status_code', value: 204 },
  320. { key: 'internal.span.format', value: 'proto' },
  321. ],
  322. [
  323. { key: 'span.kind', value: 'client' },
  324. { key: 'component', value: 'gRPC' },
  325. { key: 'internal.span.format', value: 'proto' },
  326. ],
  327. [
  328. { key: 'span.kind', value: 'server' },
  329. { key: 'component', value: 'gRPC' },
  330. { key: 'internal.span.format', value: 'proto' },
  331. ],
  332. ],
  333. },
  334. { name: 'warnings', values: [undefined, undefined] },
  335. { name: 'stackTraces', values: [undefined, undefined] },
  336. ],
  337. meta: {
  338. preferredVisualisationType: 'trace',
  339. },
  340. });