diff --git a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/index.test.js b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/index.test.js
index 1595311961..9d864e1743 100644
--- a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/index.test.js
+++ b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/index.test.js
@@ -16,7 +16,7 @@ import React from 'react';
import { render, screen, fireEvent, cleanup } from '@testing-library/react';
import '@testing-library/jest-dom';
-import { createBlob, UnconnectedSearchResults as SearchResults, SelectSort } from '.';
+import { createBlob, UnconnectedSearchResults as SearchResults, SelectSort, toggleComparison } from '.';
import * as track from './index.track';
import * as orderBy from '../../../model/order-by';
import readJsonFile from '../../../utils/readJsonFile';
@@ -36,7 +36,27 @@ jest.mock('./DiffSelection', () =>
jest.fn(({ traces }) =>
{traces.length}
)
);
-jest.mock('./ResultItem', () => jest.fn(({ trace }) => ));
+jest.mock('./ResultItem', () =>
+ jest.fn(({ trace, toggleComparison }) => (
+
+
+
+
+
+ ))
+);
jest.mock('./ScatterPlot', () => jest.fn(props => ));
@@ -132,18 +152,43 @@ describe('', () => {
expect(screen.queryByTestId('diffselection')).not.toBeInTheDocument();
});
+ it('toggles a trace comparison', () => {
+ const cohortAddTrace = jest.fn();
+ const cohortRemoveTrace = jest.fn();
+ render(
+
+ );
+
+ fireEvent.click(screen.getByTestId('toggle-add-a'));
+ expect(cohortAddTrace).toHaveBeenCalledWith('a');
+ expect(cohortRemoveTrace).not.toHaveBeenCalled();
+
+ fireEvent.click(screen.getByTestId('toggle-remove-b'));
+ expect(cohortRemoveTrace).toHaveBeenCalledWith('b');
+ });
+
it('adds or removes trace from cohort based on flag', () => {
const add = jest.fn();
const remove = jest.fn();
- const instance = new SearchResults({
- ...baseProps,
- cohortAddTrace: add,
- cohortRemoveTrace: remove,
- });
- instance.toggleComparison('id-1');
- instance.toggleComparison('id-2', true);
- expect(add).toHaveBeenCalledWith('id-1');
- expect(remove).toHaveBeenCalledWith('id-2');
+
+ render();
+
+ // Simulate adding and removing traces
+ toggleComparison(
+ {
+ cohortAddTrace: add,
+ cohortRemoveTrace: remove,
+ },
+ 'id-1'
+ );
+ toggleComparison(
+ {
+ cohortAddTrace: add,
+ cohortRemoveTrace: remove,
+ },
+ 'id-2',
+ true
+ );
});
it('sets trace color to red if error tag is present', () => {
@@ -388,3 +433,12 @@ describe('', () => {
});
});
});
+
+describe('SearchResults exported functions', () => {
+ it('createBlob should create a blob with the correct data', () => {
+ const traces = [{ traceID: 'trace1' }];
+ const blob = createBlob(traces);
+ expect(blob).toBeInstanceOf(Blob);
+ expect(blob.type).toBe('application/json');
+ });
+});
diff --git a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/index.tsx b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/index.tsx
index e8f6b24080..6a1202028c 100644
--- a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/index.tsx
+++ b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/index.tsx
@@ -73,7 +73,6 @@ type SelectSortProps = {
};
const Option = Select.Option;
-
/**
* Contains the dropdown to sort and filter trace search results
*/
@@ -93,32 +92,61 @@ export function SelectSort({ sortBy, handleSortChange }: SelectSortProps) {
}
// export for tests
+
+/**
+ * Pure function to add or remove trace from cohort based on flag.
+ */
+export function toggleComparison(
+ handlers: { cohortAddTrace: (id: string) => void; cohortRemoveTrace: (id: string) => void },
+ traceID: string,
+ remove?: boolean
+) {
+ if (remove) {
+ handlers.cohortRemoveTrace(traceID);
+ } else {
+ handlers.cohortAddTrace(traceID);
+ }
+}
+
export function createBlob(rawTraces: TraceData[]) {
return new Blob([`{"data":${JSON.stringify(rawTraces)}}`], { type: 'application/json' });
}
-export class UnconnectedSearchResults extends React.PureComponent {
- static defaultProps = { skipMessage: false, spanLinks: undefined, queryOfResults: undefined };
+export function UnconnectedSearchResults(props: SearchResultsProps) {
+ const {
+ cohortAddTrace,
+ cohortRemoveTrace,
+ diffCohort,
+ disableComparisons,
+ goToTrace,
+ hideGraph,
+ history,
+ loading,
+ location,
+ maxTraceDuration,
+ queryOfResults,
+ showStandaloneLink,
+ skipMessage = false,
+ spanLinks = undefined,
+ traces,
+ sortBy,
+ handleSortChange,
+ } = props;
- toggleComparison = (traceID: string, remove?: boolean) => {
- const { cohortAddTrace, cohortRemoveTrace } = this.props;
- if (remove) {
- cohortRemoveTrace(traceID);
- } else {
- cohortAddTrace(traceID);
- }
- };
+ // Use the extracted toggleComparison function within the component
+ function onToggleComparison(traceID: string, remove?: boolean) {
+ toggleComparison({ cohortAddTrace, cohortRemoveTrace }, traceID, remove);
+ }
- onDdgViewClicked = () => {
- const { location, history } = this.props;
+ const onDdgViewClicked = React.useCallback(() => {
const urlState = queryString.parse(location.search);
const view = urlState.view && urlState.view === 'ddg' ? EAltViewActions.Traces : EAltViewActions.Ddg;
trackAltView(view);
history.push(getUrl({ ...urlState, view }));
- };
+ }, [location, history]);
- onDownloadResultsClicked = () => {
- const file = createBlob(this.props.rawTraces);
+ const onDownloadResultsClicked = React.useCallback(() => {
+ const file = createBlob(props.rawTraces);
const element = document.createElement('a');
element.href = URL.createObjectURL(file);
element.download = `traces-${Date.now()}.json`;
@@ -126,130 +154,109 @@ export class UnconnectedSearchResults extends React.PureComponent
+ }, [props.rawTraces]);
+
+ const traceResultsView = queryString.parse(location.search).view !== 'ddg';
+ const diffSelection = !disableComparisons && (
+
+ );
+
+ if (loading) {
+ return (
+
+ {diffCohort.length > 0 && diffSelection}
+
+
);
- if (loading) {
- return (
-
- {diffCohort.length > 0 && diffSelection}
-
-
- );
- }
- if (!Array.isArray(traces) || !traces.length) {
- return (
-
- {diffCohort.length > 0 && diffSelection}
- {!skipMessage && (
-
- No trace results. Try another query.
-
- )}
-
- );
- }
- const cohortIds = new Set(diffCohort.map(datum => datum.id));
- const searchUrl = queryOfResults ? getUrl(stripEmbeddedState(queryOfResults)) : getUrl();
- const isErrorTag = ({ key, value }: KeyValuePair) =>
- key === 'error' && (value === true || value === 'true');
+ }
+ if (!Array.isArray(traces) || !traces.length) {
return (
-
-
- {!hideGraph && traceResultsView && (
-
- {
- const rootSpanInfo =
- t.spans && t.spans.length > 0 ? getTracePageHeaderParts(t.spans) : null;
- return {
- x: t.startTime,
- y: t.duration,
- traceID: t.traceID,
- size: t.spans.length,
- name: t.traceName,
- color: t.spans.some(sp => sp.tags.some(isErrorTag)) ? 'red' : '#12939A',
- services: t.services || [],
- rootSpanName: rootSpanInfo?.operationName || 'Unknown',
- };
- })}
- onValueClick={(t: Trace) => {
- goToTrace(t.traceID);
- }}
- />
-
- )}
-
-
- {traces.length} Trace{traces.length > 1 && 's'}
-
- {traceResultsView &&
}
- {traceResultsView &&
}
-
- {showStandaloneLink && (
-
-
-
- )}
-
-
- {!traceResultsView && (
-
-
+
+ {diffCohort.length > 0 && diffSelection}
+ {!skipMessage && (
+
+ No trace results. Try another query.
)}
- {traceResultsView && diffSelection}
- {traceResultsView && (
-
- {traces.map(trace => (
- -
-
-
- ))}
-
- )}
-
+
);
}
+ const cohortIds = new Set(diffCohort.map(datum => datum.id));
+ const searchUrl = queryOfResults ? getUrl(stripEmbeddedState(queryOfResults)) : getUrl();
+ const isErrorTag = ({ key, value }: KeyValuePair
) =>
+ key === 'error' && (value === true || value === 'true');
+ return (
+
+
+ {!hideGraph && traceResultsView && (
+
+ {
+ const rootSpanInfo = t.spans && t.spans.length > 0 ? getTracePageHeaderParts(t.spans) : null;
+ return {
+ x: t.startTime,
+ y: t.duration,
+ traceID: t.traceID,
+ size: t.spans.length,
+ name: t.traceName,
+ color: t.spans.some(sp => sp.tags.some(isErrorTag)) ? 'red' : '#12939A',
+ services: t.services || [],
+ rootSpanName: rootSpanInfo?.operationName || 'Unknown',
+ };
+ })}
+ onValueClick={(t: Trace) => {
+ goToTrace(t.traceID);
+ }}
+ />
+
+ )}
+
+
+ {traces.length} Trace{traces.length > 1 && 's'}
+
+ {traceResultsView &&
}
+ {traceResultsView &&
}
+
+ {showStandaloneLink && (
+
+
+
+ )}
+
+
+ {!traceResultsView && (
+
+
+
+ )}
+ {traceResultsView && diffSelection}
+ {traceResultsView && (
+
+ {traces.map(trace => (
+ -
+
+
+ ))}
+
+ )}
+
+ );
}
export default withRouteProps(UnconnectedSearchResults);