diff --git a/.betterer.results b/.betterer.results index ce09992dfd1..63aa123eebb 100644 --- a/.betterer.results +++ b/.betterer.results @@ -7,9 +7,6 @@ exports[`no enzyme tests`] = { value: `{ "packages/grafana-ui/src/components/QueryField/QueryField.test.tsx:2976628669": [ [0, 26, 13, "RegExp match", "2409514259"] - ], - "packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.test.tsx:3891071965": [ - [13, 42, 13, "RegExp match", "2409514259"] ] }` }; diff --git a/packages/grafana-ui/src/components/ToolbarButton/ToolbarButton.tsx b/packages/grafana-ui/src/components/ToolbarButton/ToolbarButton.tsx index c7739881f59..6c286ff2dbe 100644 --- a/packages/grafana-ui/src/components/ToolbarButton/ToolbarButton.tsx +++ b/packages/grafana-ui/src/components/ToolbarButton/ToolbarButton.tsx @@ -8,7 +8,7 @@ import { styleMixins, useStyles2 } from '../../themes'; import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins'; import { getPropertiesForVariant } from '../Button'; import { Icon } from '../Icon/Icon'; -import { Tooltip } from '../Tooltip/Tooltip'; +import { Tooltip } from '../Tooltip'; type CommonProps = { /** Icon name */ diff --git a/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.test.tsx b/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.test.tsx index 7c825111eae..f3dab2e1334 100644 --- a/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.test.tsx +++ b/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.test.tsx @@ -11,86 +11,44 @@ // 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 { shallow, mount, ShallowWrapper } from 'enzyme'; +import { render, screen } from '@testing-library/react'; import React from 'react'; -import { TNil } from 'src/types'; -import { Trace, TraceSpan } from 'src/types/trace'; +import { Trace } from 'src/types/trace'; import traceGenerator from '../demo/trace-generators'; import transformTraceData from '../model/transform-trace-data'; -import ListView from './ListView'; -import SpanBarRow, { SpanBarRowProps } from './SpanBarRow'; -import DetailState from './SpanDetail/DetailState'; -import SpanDetailRow, { SpanDetailRowProps } from './SpanDetailRow'; import SpanTreeOffset from './SpanTreeOffset'; -import VirtualizedTraceView, { - DEFAULT_HEIGHTS, - UnthemedVirtualizedTraceView, - VirtualizedTraceViewProps, -} from './VirtualizedTraceView'; +import VirtualizedTraceView, { VirtualizedTraceViewProps } from './VirtualizedTraceView'; jest.mock('./SpanTreeOffset'); -describe('', () => { - let wrapper: ShallowWrapper; - let instance: UnthemedVirtualizedTraceView; - - const trace = transformTraceData(traceGenerator.trace({ numberOfSpans: 10 }))!; - const topOfExploreViewRef = jest.fn(); - const props = { - childrenHiddenIDs: new Set(), - childrenToggle: jest.fn(), - clearShouldScrollToFirstUiFindMatch: jest.fn(), - currentViewRangeTime: [0.25, 0.75], - detailLogItemToggle: jest.fn(), - detailLogsToggle: jest.fn(), - detailProcessToggle: jest.fn(), - detailStates: new Map(), - detailTagsToggle: jest.fn(), - detailToggle: jest.fn(), - findMatchesIDs: null, - registerAccessors: jest.fn(), - scrollToFirstVisibleSpan: jest.fn(), - setSpanNameColumnWidth: jest.fn(), - setTrace: jest.fn(), - shouldScrollToFirstUiFindMatch: false, - spanNameColumnWidth: 0.5, - trace, - uiFind: 'uiFind', - topOfExploreViewRef, - } as unknown as VirtualizedTraceViewProps; - - function expandRow(rowIndex: number) { - const detailStates = new Map(); - const detailState = new DetailState(); - detailStates.set(trace.spans[rowIndex].spanID, detailState); - wrapper.setProps({ detailStates }); - return detailState; - } - - function addSpansAndCollapseTheirParent(newSpanID = 'some-id') { - const childrenHiddenIDs = new Set([newSpanID]); - const spans = [ - trace.spans[0], - // this span is condidered to have collapsed children - { spanID: newSpanID, depth: 1, traceID: trace.traceID }, - // these two "spans" are children and should be hidden - { depth: 2 }, - { depth: 3 }, - ...trace.spans.slice(1), - ] as TraceSpan[]; - const _trace = { ...trace, spans }; - wrapper.setProps({ childrenHiddenIDs, trace: _trace }); - return spans; - } - - function updateSpan(srcTrace: Trace, spanIndex: number, update: Partial) { - const span = { ...srcTrace.spans[spanIndex], ...update }; - const spans = [...srcTrace.spans.slice(0, spanIndex), span, ...srcTrace.spans.slice(spanIndex + 1)]; - return { ...srcTrace, spans }; - } +const trace = transformTraceData(traceGenerator.trace({ numberOfSpans: 2 }))!; +const topOfExploreViewRef = jest.fn(); +let props = { + childrenHiddenIDs: new Set(), + childrenToggle: jest.fn(), + clearShouldScrollToFirstUiFindMatch: jest.fn(), + currentViewRangeTime: [0.25, 0.75], + detailLogItemToggle: jest.fn(), + detailLogsToggle: jest.fn(), + detailProcessToggle: jest.fn(), + detailStates: new Map(), + detailTagsToggle: jest.fn(), + detailToggle: jest.fn(), + findMatchesIDs: null, + registerAccessors: jest.fn(), + scrollToFirstVisibleSpan: jest.fn(), + setSpanNameColumnWidth: jest.fn(), + setTrace: jest.fn(), + shouldScrollToFirstUiFindMatch: false, + spanNameColumnWidth: 0.5, + trace, + uiFind: 'uiFind', + topOfExploreViewRef, +} as unknown as VirtualizedTraceViewProps; +describe('', () => { beforeEach(() => { jest.mocked(SpanTreeOffset).mockReturnValue(
); Object.keys(props).forEach((key) => { @@ -98,344 +56,71 @@ describe('', () => { (props[key as keyof VirtualizedTraceViewProps] as jest.Mock).mockReset(); } }); - wrapper = shallow() - .dive() - .dive(); - instance = wrapper.instance(); - }); - - it('renders without exploding', () => { - expect(wrapper).toBeDefined(); - }); - - it('renders when a trace is not set', () => { - wrapper.setProps({ trace: null as unknown as Trace }); - expect(wrapper).toBeDefined(); - }); - - it('renders a ListView', () => { - expect(wrapper.find(ListView)).toBeDefined(); - }); - - it('renders scrollToTopButton', () => { - expect(wrapper.find({ title: 'Scroll to top' }).exists()).toBeTruthy(); - }); - - it('sets the trace for global state.traceTimeline', () => { - expect(jest.mocked(props.setTrace).mock.calls).toEqual([[trace, props.uiFind]]); - expect(jest.mocked(props.setTrace).mock.calls).toEqual([[trace, props.uiFind]]); - jest.mocked(props.setTrace).mockReset(); - const traceID = 'some-other-id'; - const _trace = { ...trace, traceID }; - wrapper.setProps({ trace: _trace }); - expect(jest.mocked(props.setTrace).mock.calls).toEqual([[_trace, props.uiFind]]); }); - describe('props.registerAccessors', () => { - let lv: ListView; - let expectedArg: { - getBottomRowIndexVisible: () => void; - getTopRowIndexVisible: () => void; - getViewHeight: () => number; - getRowPosition: (index: number) => { height: number; y: number }; - getViewRange: () => [number, number]; - getSearchedSpanIDs: () => Set | TNil; - getCollapsedChildren: () => Set; - mapRowIndexToSpanIndex: (index: number) => number; - mapSpanIndexToRowIndex: (index: number) => number; - }; - - beforeEach(() => { - const getBottomRowIndexVisible = () => {}; - const getTopRowIndexVisible = () => {}; - lv = { - getViewHeight: () => {}, - getBottomVisibleIndex: getBottomRowIndexVisible, - getTopVisibleIndex: getTopRowIndexVisible, - getRowPosition: () => {}, - } as unknown as ListView; - expectedArg = { - getBottomRowIndexVisible, - getTopRowIndexVisible, - getViewHeight: lv.getViewHeight, - getRowPosition: lv.getRowPosition, - getViewRange: instance.getViewRange, - getSearchedSpanIDs: instance.getSearchedSpanIDs, - getCollapsedChildren: instance.getCollapsedChildren, - mapRowIndexToSpanIndex: instance.mapRowIndexToSpanIndex, - mapSpanIndexToRowIndex: instance.mapSpanIndexToRowIndex, - }; - }); - - it('invokes when the listView is set', () => { - expect(jest.mocked(props.registerAccessors).mock.calls.length).toBe(0); - instance.setListView(lv); - expect(jest.mocked(props.registerAccessors).mock.calls).toEqual([[expectedArg]]); - }); - - it('invokes when registerAccessors changes', () => { - const registerAccessors = jest.fn(); - instance.setListView(lv); - wrapper.setProps({ registerAccessors }); - expect(registerAccessors.mock.calls).toEqual([[expectedArg]]); - }); - }); - - it('returns the current view range via getViewRange()', () => { - expect(instance.getViewRange()).toBe(props.currentViewRangeTime); - }); - - it('returns findMatchesIDs via getSearchedSpanIDs()', () => { - const findMatchesIDs: Set = new Set(); - wrapper.setProps({ findMatchesIDs }); - expect(instance.getSearchedSpanIDs()).toBe(findMatchesIDs); - }); - - it('returns childrenHiddenIDs via getCollapsedChildren()', () => { - const childrenHiddenIDs: Set = new Set(); - wrapper.setProps({ childrenHiddenIDs }); - expect(instance.getCollapsedChildren()).toBe(childrenHiddenIDs); - }); - - describe('mapRowIndexToSpanIndex() maps row index to span index', () => { - it('works when nothing is collapsed or expanded', () => { - const i = trace.spans.length - 1; - expect(instance.mapRowIndexToSpanIndex(i)).toBe(i); - }); - - it('works when a span is expanded', () => { - expandRow(1); - expect(instance.mapRowIndexToSpanIndex(0)).toBe(0); - expect(instance.mapRowIndexToSpanIndex(1)).toBe(1); - expect(instance.mapRowIndexToSpanIndex(2)).toBe(1); - expect(instance.mapRowIndexToSpanIndex(3)).toBe(2); - }); - - it('works when a parent span is collapsed', () => { - addSpansAndCollapseTheirParent(); - expect(instance.mapRowIndexToSpanIndex(0)).toBe(0); - expect(instance.mapRowIndexToSpanIndex(1)).toBe(1); - expect(instance.mapRowIndexToSpanIndex(2)).toBe(4); - expect(instance.mapRowIndexToSpanIndex(3)).toBe(5); - }); - }); - - describe('mapSpanIndexToRowIndex() maps span index to row index', () => { - it('works when nothing is collapsed or expanded', () => { - const i = trace.spans.length - 1; - expect(instance.mapSpanIndexToRowIndex(i)).toBe(i); - }); - - it('works when a span is expanded', () => { - expandRow(1); - expect(instance.mapSpanIndexToRowIndex(0)).toBe(0); - expect(instance.mapSpanIndexToRowIndex(1)).toBe(1); - expect(instance.mapSpanIndexToRowIndex(2)).toBe(3); - expect(instance.mapSpanIndexToRowIndex(3)).toBe(4); - }); - - it('works when a parent span is collapsed', () => { - addSpansAndCollapseTheirParent(); - expect(instance.mapSpanIndexToRowIndex(0)).toBe(0); - expect(instance.mapSpanIndexToRowIndex(1)).toBe(1); - expect(() => instance.mapSpanIndexToRowIndex(2)).toThrow(); - expect(() => instance.mapSpanIndexToRowIndex(3)).toThrow(); - expect(instance.mapSpanIndexToRowIndex(4)).toBe(2); - }); - }); + it('renders service name, operation name and duration for each span', () => { + render(); + expect(screen.getAllByText(trace.services[0].name)).toBeTruthy(); - describe('getKeyFromIndex() generates a "key" from a row index', () => { - function verify(input: number, output: string) { - expect(instance.getKeyFromIndex(input)).toBe(output); + if (trace.services.length > 1) { + expect(screen.getAllByText(trace.services[1].name)).toBeTruthy(); } - it('works when nothing is expanded or collapsed', () => { - verify(0, `${trace.spans[0].traceID}--${trace.spans[0].spanID}--bar`); - }); - - it('works when rows are expanded', () => { - expandRow(1); - verify(1, `${trace.spans[1].traceID}--${trace.spans[1].spanID}--bar`); - verify(2, `${trace.spans[1].traceID}--${trace.spans[1].spanID}--detail`); - verify(3, `${trace.spans[2].traceID}--${trace.spans[2].spanID}--bar`); - }); + expect(screen.getAllByText(trace.spans[0].operationName)).toBeTruthy(); + expect(screen.getAllByText(trace.spans[1].operationName)).toBeTruthy(); - it('works when a parent span is collapsed', () => { - const spans = addSpansAndCollapseTheirParent(); - verify(1, `${spans[1].traceID}--${spans[1].spanID}--bar`); - verify(2, `${spans[4].traceID}--${spans[4].spanID}--bar`); - }); - }); + let durationSpan0 = trace.spans[0].duration; - describe('getIndexFromKey() converts a "key" to the corresponding row index', () => { - function verify(input: string, output: number) { - expect(instance.getIndexFromKey(input)).toBe(output); + if (trace.spans[0].duration >= 1_000_000) { + durationSpan0 = Math.floor(trace.spans[0].duration / 1000000); + } else if (trace.spans[0].duration >= 1000) { + durationSpan0 = Math.floor(trace.spans[0].duration / 1000); } - it('works when nothing is expanded or collapsed', () => { - verify(`${trace.traceID}--${trace.spans[0].spanID}--bar`, 0); - }); + let durationSpan1 = trace.spans[1].duration; - it('works when rows are expanded', () => { - expandRow(1); - verify(`${trace.spans[1].traceID}--${trace.spans[1].spanID}--bar`, 1); - verify(`${trace.spans[1].traceID}--${trace.spans[1].spanID}--detail`, 2); - verify(`${trace.spans[2].traceID}--${trace.spans[2].spanID}--bar`, 3); - }); + if (trace.spans[1].duration >= 1_000_000) { + durationSpan1 = Math.floor(trace.spans[1].duration / 1000000); + } else if (trace.spans[1].duration >= 1000) { + durationSpan1 = Math.floor(trace.spans[1].duration / 1000); + } - it('works when a parent span is collapsed', () => { - const spans = addSpansAndCollapseTheirParent(); - verify(`${spans[1].traceID}--${spans[1].spanID}--bar`, 1); - verify(`${spans[4].traceID}--${spans[4].spanID}--bar`, 2); - }); + expect(screen.getAllByText(durationSpan0, { exact: false })).toBeTruthy(); + expect(screen.getAllByText(durationSpan1, { exact: false })).toBeTruthy(); }); - describe('getRowHeight()', () => { - it('returns the expected height for non-detail rows', () => { - expect(instance.getRowHeight(0)).toBe(DEFAULT_HEIGHTS.bar); - }); - - it('returns the expected height for detail rows that do not have logs', () => { - expandRow(0); - expect(instance.getRowHeight(1)).toBe(DEFAULT_HEIGHTS.detail); - }); - - it('returns the expected height for detail rows that do have logs', () => { - const logs = [ - { - timestamp: Date.now(), - fields: traceGenerator.tags(), - }, - ]; - const altTrace = updateSpan(trace, 0, { logs }); - expandRow(0); - wrapper.setProps({ trace: altTrace }); - expect(instance.getRowHeight(1)).toBe(DEFAULT_HEIGHTS.detailWithLogs); - }); + it('renders without exploding', () => { + render(); + expect(screen.getByTestId('ListView')).toBeInTheDocument(); + expect(screen.getByTitle('Scroll to top')).toBeInTheDocument(); }); - describe('renderRow()', () => { - it('renders a SpanBarRow when it is not a detail', () => { - const span = trace.spans[1]; - const row = instance.renderRow('some-key', {}, 1, {}); - const rowWrapper = shallow(row!); - - expect( - rowWrapper.containsMatchingElement( - - ) - ).toBe(true); - }); - - it('renders a SpanBarRow with a RPC span if the row is collapsed and a client span', () => { - const clientTags = [{ key: 'span.kind', value: 'client' }, ...trace.spans[0].tags]; - const serverTags = [{ key: 'span.kind', value: 'server' }, ...trace.spans[1].tags]; - let altTrace = updateSpan(trace, 0, { tags: clientTags }); - altTrace = updateSpan(altTrace, 1, { tags: serverTags }); - const childrenHiddenIDs = new Set([altTrace.spans[0].spanID]); - wrapper.setProps({ childrenHiddenIDs, trace: altTrace }); - - const rowWrapper = mount(instance.renderRow('some-key', {}, 0, {})!); - const spanBarRow = rowWrapper.find(SpanBarRow); - expect(spanBarRow.length).toBe(1); - expect(spanBarRow.prop('rpc')).toBeDefined(); - }); - - it('renders a SpanDetailRow when it is a detail', () => { - const detailState = expandRow(1); - const span = trace.spans[1]; - const row = instance.renderRow('some-key', {}, 2, {}); - const rowWrapper = shallow(row!); - expect( - rowWrapper.containsMatchingElement( - - ) - ).toBe(true); - }); - - it('renders a SpanBarRow with a client span and no instrumented server span', () => { - const externServiceName = 'externalServiceTest'; - const leafSpan = trace.spans.find((span) => !span.hasChildren); - const leafSpanIndex = trace.spans.indexOf(leafSpan!); - const clientTags = [ - { key: 'span.kind', value: 'client' }, - { key: 'peer.service', value: externServiceName }, - ...leafSpan!.tags, - ]; - const altTrace = updateSpan(trace, leafSpanIndex, { tags: clientTags }); - wrapper.setProps({ trace: altTrace }); - const rowWrapper = mount(instance.renderRow('some-key', {}, leafSpanIndex, {})!); - const spanBarRow = rowWrapper.find(SpanBarRow); - expect(spanBarRow.length).toBe(1); - expect(spanBarRow.prop('noInstrumentedServer')).not.toBeNull(); - }); + it('renders when a trace is not set', () => { + props = { ...props, trace: null as unknown as Trace }; + render(); + expect(screen.getByTestId('ListView')).toBeInTheDocument(); + expect(screen.getByTitle('Scroll to top')).toBeInTheDocument(); }); - describe('shouldScrollToFirstUiFindMatch', () => { - const propsWithTrueShouldScrollToFirstUiFindMatch = { ...props, shouldScrollToFirstUiFindMatch: true }; - - beforeEach(() => { - jest.mocked(props.scrollToFirstVisibleSpan).mockReset(); - jest.mocked(props.clearShouldScrollToFirstUiFindMatch).mockReset(); - }); - - it('calls props.scrollToFirstVisibleSpan if shouldScrollToFirstUiFindMatch is true', () => { - expect(props.scrollToFirstVisibleSpan).not.toHaveBeenCalled(); - expect(props.clearShouldScrollToFirstUiFindMatch).not.toHaveBeenCalled(); - - wrapper.setProps(propsWithTrueShouldScrollToFirstUiFindMatch); - expect(props.scrollToFirstVisibleSpan).toHaveBeenCalledTimes(1); - expect(props.clearShouldScrollToFirstUiFindMatch).toHaveBeenCalledTimes(1); - }); - - describe('shouldComponentUpdate', () => { - it('returns true if props.shouldScrollToFirstUiFindMatch changes to true', () => { - expect(wrapper.instance().shouldComponentUpdate(propsWithTrueShouldScrollToFirstUiFindMatch)).toBe(true); - }); - - it('returns true if props.shouldScrollToFirstUiFindMatch changes to false and another props change', () => { - const propsWithOtherDifferenceAndTrueshouldScrollToFirstUiFindMatch = { - ...propsWithTrueShouldScrollToFirstUiFindMatch, - clearShouldScrollToFirstUiFindMatch: () => {}, - }; - wrapper.setProps(propsWithOtherDifferenceAndTrueshouldScrollToFirstUiFindMatch); - expect(wrapper.instance().shouldComponentUpdate(props)).toBe(true); - }); + it('renders ListView', () => { + render(); + expect(screen.getByTestId('ListView')).toBeInTheDocument(); + }); - it('returns false if props.shouldScrollToFirstUiFindMatch changes to false and no other props change', () => { - wrapper.setProps(propsWithTrueShouldScrollToFirstUiFindMatch); - expect(wrapper.instance().shouldComponentUpdate(props)).toBe(false); - }); + it('renders scrollToTopButton', () => { + render(); + expect( + screen.getByRole('button', { + name: /Scroll to top/i, + }) + ).toBeInTheDocument(); + }); - it('returns false if all props are unchanged', () => { - expect(wrapper.instance().shouldComponentUpdate(props)).toBe(false); - }); - }); + it('sets the trace for global state.traceTimeline', () => { + const traceID = 'some-other-id'; + const _trace = { ...trace, traceID }; + props = { ...props, trace: _trace }; + render(); + expect(jest.mocked(props.setTrace).mock.calls).toEqual([[_trace, props.uiFind]]); }); });