From 6396b3303b5c995495ec2309ad18d40fba22ef03 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Wed, 27 Nov 2024 10:47:58 -0800 Subject: [PATCH 01/21] Change service table click to page redirection Signed-off-by: Adam Tackett --- .../__snapshots__/service_view.test.tsx.snap | 2 +- .../__snapshots__/services.test.tsx.snap | 24 +++++++++++++++++++ .../components/services/service_view.tsx | 15 +++++++++++- .../components/services/services_content.tsx | 1 + .../components/services/services_table.tsx | 12 ++++------ 5 files changed, 45 insertions(+), 9 deletions(-) diff --git a/public/components/trace_analytics/components/services/__tests__/__snapshots__/service_view.test.tsx.snap b/public/components/trace_analytics/components/services/__tests__/__snapshots__/service_view.test.tsx.snap index 9bc8845a99..8d2829d6ff 100644 --- a/public/components/trace_analytics/components/services/__tests__/__snapshots__/service_view.test.tsx.snap +++ b/public/components/trace_analytics/components/services/__tests__/__snapshots__/service_view.test.tsx.snap @@ -232,7 +232,7 @@ exports[`Service view component renders service view 1`] = ` { const DSL = filtersToDsl( mode, @@ -533,7 +546,7 @@ export function ServiceView(props: ServiceViewProps) { page="serviceView" filterByCurrService={true} mode={mode} - hideSearchBar={page === 'serviceFlyout'} + hideSearchBar={hideSearchBarCheck} /> ) : ( diff --git a/public/components/trace_analytics/components/services/services_content.tsx b/public/components/trace_analytics/components/services/services_content.tsx index 20bfddfb68..e7b14ab826 100644 --- a/public/components/trace_analytics/components/services/services_content.tsx +++ b/public/components/trace_analytics/components/services/services_content.tsx @@ -230,6 +230,7 @@ export function ServicesContent(props: ServicesProps) { isServiceTrendEnabled={isServiceTrendEnabled} setIsServiceTrendEnabled={setIsServiceTrendEnabled} serviceTrends={serviceTrends} + dataSourceMDSId={props.dataSourceMDSId} /> {mode === 'custom_data_prepper' || diff --git a/public/components/trace_analytics/components/services/services_table.tsx b/public/components/trace_analytics/components/services/services_table.tsx index 1921787c8c..b3b9848394 100644 --- a/public/components/trace_analytics/components/services/services_table.tsx +++ b/public/components/trace_analytics/components/services/services_table.tsx @@ -24,11 +24,13 @@ import React, { useMemo } from 'react'; import { ServiceTrends, TraceAnalyticsMode } from '../../../../../common/types/trace_analytics'; import { FilterType } from '../common/filters/filters'; import { + generateServiceUrl, MissingConfigurationMessage, NoMatchMessage, PanelTitle, } from '../common/helper_functions'; import { ServiceTrendsPlots } from './service_trends_plots'; +import { DataSourceOption } from '../../../../../../../src/plugins/data_source_management/public'; interface ServicesTableProps { items: any[]; @@ -46,6 +48,7 @@ interface ServicesTableProps { isServiceTrendEnabled: boolean; setIsServiceTrendEnabled: React.Dispatch>; serviceTrends: ServiceTrends; + dataSourceMDSId: DataSourceOption[]; } export function ServicesTable(props: ServicesTableProps) { @@ -65,6 +68,7 @@ export function ServicesTable(props: ServicesTableProps) { isServiceTrendEnabled, setIsServiceTrendEnabled, serviceTrends, + dataSourceMDSId, } = props; const selectionValue = { @@ -72,13 +76,7 @@ export function ServicesTable(props: ServicesTableProps) { }; const nameColumnAction = (serviceName: string) => { - addFilter({ - field: mode === 'jaeger' ? 'process.serviceName' : 'serviceName', - operator: 'is', - value: serviceName, - inverted: false, - disabled: false, - }); + window.location.href = generateServiceUrl(serviceName, dataSourceMDSId[0].id); }; const renderTitleBar = (totalItems?: number) => { From 87cdd23b585e688063b50d76425255307e8792f0 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Wed, 27 Nov 2024 16:06:39 -0800 Subject: [PATCH 02/21] remove focus on filtering, bug fixes Signed-off-by: Adam Tackett --- .../components/common/plots/service_map.tsx | 117 +++++++----------- .../__snapshots__/services.test.tsx.snap | 32 +++++ .../components/services/services_content.tsx | 2 + 3 files changed, 82 insertions(+), 69 deletions(-) diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index b764e21f6a..760baef0f5 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -61,6 +61,8 @@ export function ServiceMap({ filterByCurrService, includeMetricsCallback, mode, + filters, + setFilters, hideSearchBar = false, }: { serviceMap: ServiceObject; @@ -81,6 +83,10 @@ export function ServiceMap({ filterByCurrService?: boolean; includeMetricsCallback?: () => void; mode?: string; + filters: FilterType[]; + setFilters: + | React.Dispatch> + | ((filters: FilterType[]) => void); hideSearchBar?: boolean; }) { const [graphKey, setGraphKey] = useState(0); // adding key to allow for re-renders @@ -93,7 +99,6 @@ export function ServiceMap({ const [isLoading, setIsLoading] = useState(true); const [filterChange, setIsFilterChange] = useState(false); const [focusedService, setFocusedService] = useState(null); - const [clearFilterRequest, setClearFilterRequest] = useState(false); const toggleButtons = [ { @@ -141,44 +146,14 @@ export function ServiceMap({ }, ]; - const clearFilter = () => { - setFocusedService(null); - setClearFilterRequest(true); + const removeFilter = (field: string, value: string) => { + if (!setFilters) return; + const updatedFilters = filters.filter( + (filter) => !(filter.field === field && filter.value === value) + ); + setFilters(updatedFilters); }; - useEffect(() => { - if (clearFilterRequest && focusedService === null) { - setClearFilterRequest(false); - - setQuery(''); - currService = ''; - - if (addFilter) { - addFilter({ - field: 'serviceName', - operator: 'is', - value: '', - inverted: false, - disabled: true, // Disable the filter to effectively clear it - }); - } - - // Reset the graph to show the full view - setItems( - getServiceMapGraph( - serviceMap, - idSelected, - ticks, - undefined, - serviceMap[currService!]?.relatedServices, - false // Do not filter by the current service to show the entire graph - ) - ); - - setInvalid(false); - } - }, [focusedService, clearFilterRequest]); - useEffect(() => { if (items?.graph?.nodes) { const visibleNodes = items.graph.nodes.map((node) => node.label); @@ -267,8 +242,6 @@ export function ServiceMap({ }; const addServiceFilter = (selectedServiceName) => { - if (selectedServiceName === focusedService) return; - if (!addFilter) return; if (selectedServiceName) { @@ -328,31 +301,39 @@ export function ServiceMap({ }; const onFocus = (service: string) => { - if (service.length === 0) { - clearFilter(); + if (!service) { + // Clear focus if no service is provided + if (focusedService !== null) { + removeFilter('serviceName', focusedService); + setItems( + getServiceMapGraph( + serviceMap, + idSelected, + ticks, + undefined, + undefined, + false // Show the entire graph without filtering + ) + ); + setFocusedService(null); + setInvalid(false); + } } else if (serviceMap[service]) { - // Focus on the specified service and add a filter - setFocusedService(service); - if (addFilter) { - addFilter({ - field: 'serviceName', - operator: 'is', - value: service, - inverted: false, - disabled: false, - }); + if (focusedService !== service) { + const filteredGraph = getServiceMapGraph( + serviceMap, + idSelected, + ticks, + service, + serviceMap[service]?.relatedServices, + true // Enable filtering to focus on connected nodes + ); + setSelectedNodeDetails(null); + setItems(filteredGraph); + setFocusedService(service); + setInvalid(false); + setGraphKey((prevKey) => prevKey + 1); } - - const filteredGraph = getServiceMapGraph( - serviceMap, - idSelected, - ticks, - service, - serviceMap[service]?.relatedServices, - true // Enable filtering by the current service to show only connected nodes - ); - setItems(filteredGraph); - setInvalid(false); } else { setInvalid(true); } @@ -388,10 +369,6 @@ export function ServiceMap({ }, [items]); useEffect(() => { - if (currService === focusedService) { - return; - } - if (!serviceMap || Object.keys(serviceMap).length === 0) { setItems({}); return; @@ -404,14 +381,16 @@ export function ServiceMap({ const max = Math.max(...values); const calculatedTicks = calculateTicks(min, max); setTicks(calculatedTicks); + // Adjust graph rendering logic to ensure related services are visible + const showRelatedServices = focusedService ? true : filterByCurrService; setItems( getServiceMapGraph( serviceMap, idSelected, calculatedTicks, - currService, + focusedService ?? currService, serviceMap[currService!]?.relatedServices, - filterByCurrService + showRelatedServices ) ); }, [serviceMap, idSelected]); @@ -539,7 +518,7 @@ export function ServiceMap({ getNetwork={(networkInstance: any) => { setNetwork(networkInstance); setZoomLimits(networkInstance); - if (currService) onFocus(currService, networkInstance); + if (currService) onFocus(currService); }} /> )} diff --git a/public/components/trace_analytics/components/services/__tests__/__snapshots__/services.test.tsx.snap b/public/components/trace_analytics/components/services/__tests__/__snapshots__/services.test.tsx.snap index 34550d7e4b..ad13907b86 100644 --- a/public/components/trace_analytics/components/services/__tests__/__snapshots__/services.test.tsx.snap +++ b/public/components/trace_analytics/components/services/__tests__/__snapshots__/services.test.tsx.snap @@ -1728,10 +1728,26 @@ exports[`Services component renders empty services page 1`] = ` @@ -5821,10 +5837,26 @@ exports[`Services component renders services page 1`] = ` diff --git a/public/components/trace_analytics/components/services/services_content.tsx b/public/components/trace_analytics/components/services/services_content.tsx index e7b14ab826..d3382ad3d2 100644 --- a/public/components/trace_analytics/components/services/services_content.tsx +++ b/public/components/trace_analytics/components/services/services_content.tsx @@ -237,6 +237,8 @@ export function ServicesContent(props: ServicesProps) { (mode === 'data_prepper' && dataPrepperIndicesExist) ? ( Date: Wed, 27 Nov 2024 17:04:30 -0800 Subject: [PATCH 03/21] add jest test for url redirection on click Signed-off-by: Adam Tackett --- .../__tests__/services_table.test.tsx | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/public/components/trace_analytics/components/services/__tests__/services_table.test.tsx b/public/components/trace_analytics/components/services/__tests__/services_table.test.tsx index d65bcb6b1b..b35fe81c30 100644 --- a/public/components/trace_analytics/components/services/__tests__/services_table.test.tsx +++ b/public/components/trace_analytics/components/services/__tests__/services_table.test.tsx @@ -7,6 +7,7 @@ import { configure, mount } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import React from 'react'; import { ServicesTable } from '../services_table'; +import { generateServiceUrl } from '../../common/helper_functions'; describe('Services table component', () => { configure({ adapter: new Adapter() }); @@ -114,4 +115,55 @@ describe('Services table component', () => { expect(wrapper).toMatchSnapshot(); }); + + it('redirects to the correct URL when the service link is clicked', () => { + const mockDataSourceId = 'mock-data-source-id'; + const tableItems = [ + { + name: 'checkoutservice', + average_latency: 100, + error_rate: 0.5, + throughput: 200, + traces: 10, + itemId: '1', + }, + ]; + + // Mock window.location before rendering + const originalLocation = window.location; + delete window.location; + window.location = { ...originalLocation }; + + const wrapper = mount( + + ); + + // Find and click the service link + const serviceLink = wrapper.find('[data-test-subj="service-link"]').first(); + expect(serviceLink.exists()).toBeTruthy(); + + serviceLink.simulate('click'); + + const expectedUrl = generateServiceUrl('checkoutservice', mockDataSourceId); + expect(window.location.href).toBe(expectedUrl); + + window.location = originalLocation; + }); }); From 9147f5b3d3ed60291ac65effe92217e6bcff1eb2 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Fri, 29 Nov 2024 16:59:22 -0800 Subject: [PATCH 04/21] remove filter on focus, update focus functionality, add testing Signed-off-by: Adam Tackett --- .../__snapshots__/create.test.tsx.snap | 1762 ++++++------ .../service_config.test.tsx.snap | 728 ++--- .../__snapshots__/service_map.test.tsx.snap | 2365 ++++++++++++++--- .../plots/__tests__/service_map.test.tsx | 248 +- .../components/common/plots/service_map.tsx | 127 +- .../__snapshots__/services.test.tsx.snap | 728 ++--- test/constants.ts | 18 +- 7 files changed, 4000 insertions(+), 1976 deletions(-) diff --git a/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap b/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap index c3b15e282b..2785161b1a 100644 --- a/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap +++ b/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap @@ -660,67 +660,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -1940,67 +1946,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -3159,67 +3171,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -4353,67 +4371,73 @@ Object {
-
-
- +
- - +
+ - - - + + +
-
- -
+ + +
+ @@ -5640,67 +5664,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -6834,67 +6864,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -8048,67 +8084,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -9203,67 +9245,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -10420,67 +10468,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -11580,67 +11634,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -12831,67 +12891,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -14025,67 +14091,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -15244,67 +15316,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -16438,67 +16516,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -17695,67 +17779,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ @@ -18975,67 +19065,73 @@ Object {
-
-
- +
- +
- - + + +
-
- -
+ + +
+ diff --git a/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap b/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap index f8ad6ce5fa..b564b3c452 100644 --- a/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap +++ b/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap @@ -1004,27 +1004,35 @@ exports[`Service Config component renders empty service config 1`] = ` - } - aria-controls="service-select-dropdown" - compressed={true} - fullWidth={false} - incremental={false} - isClearable={true} - isInvalid={false} - isLoading={false} - onChange={[Function]} - onClick={[Function]} - placeholder="Service name" - prepend="Focus on" - value="" - /> + + + } + aria-controls="service-select-dropdown" + compressed={true} + disabled={false} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" + /> + } closePopover={[Function]} display="inlineBlock" @@ -1050,169 +1058,195 @@ exports[`Service Config component renders empty service config 1`] = `
- - } - aria-controls="service-select-dropdown" - compressed={true} - fullWidth={false} - incremental={false} - isClearable={true} - isInvalid={false} - isLoading={false} - onChange={[Function]} - onClick={[Function]} - placeholder="Service name" - prepend="Focus on" - value="" + - - } - compressed={true} - fullWidth={false} - icon="search" - isLoading={false} - prepend="Focus on" + -
+ } + aria-controls="service-select-dropdown" + compressed={true} + disabled={false} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onBlur={[Function]} + onChange={[Function]} + onClick={[Function]} + onFocus={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" > - - - -
- - - - +
+ + +
- - + + +
- - - + + + + + + + + +
+
- -
- - - -
- - + + + + + + + + +
+
+
+ +
@@ -2350,27 +2384,35 @@ exports[`Service Config component renders with one service selected 1`] = ` - } - aria-controls="service-select-dropdown" - compressed={true} - fullWidth={false} - incremental={false} - isClearable={true} - isInvalid={false} - isLoading={false} - onChange={[Function]} - onClick={[Function]} - placeholder="Service name" - prepend="Focus on" - value="" - /> + + + } + aria-controls="service-select-dropdown" + compressed={true} + disabled={false} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" + /> + } closePopover={[Function]} display="inlineBlock" @@ -2396,169 +2438,195 @@ exports[`Service Config component renders with one service selected 1`] = `
- - } - aria-controls="service-select-dropdown" - compressed={true} - fullWidth={false} - incremental={false} - isClearable={true} - isInvalid={false} - isLoading={false} - onChange={[Function]} - onClick={[Function]} - placeholder="Service name" - prepend="Focus on" - value="" + - - } - compressed={true} - fullWidth={false} - icon="search" - isLoading={false} - prepend="Focus on" + -
+ } + aria-controls="service-select-dropdown" + compressed={true} + disabled={false} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onBlur={[Function]} + onChange={[Function]} + onClick={[Function]} + onFocus={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" > - - - -
- - - - +
+ + +
- - + + +
- - - + + + + + + + + +
+
- -
- - - -
- - + + + + + + + + +
+
+
+ +
diff --git a/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap b/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap index 390230fd8c..bd5890b96b 100644 --- a/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap +++ b/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap @@ -1,434 +1,1981 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Service map component renders service map 1`] = ` - - - - - - - - + +
+ - - } - aria-controls="service-select-dropdown" - compressed={true} - fullWidth={false} - incremental={false} - isClearable={true} - isInvalid={false} - isLoading={false} - onChange={[Function]} - onClick={[Function]} - placeholder="Service name" - prepend="Focus on" - value="" - /> - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - id="service-select-dropdown" - isOpen={false} - ownFocus={false} - panelPaddingSize="none" - repositionOnScroll={true} + - - - - - - - - - + + Service map + +
+ +
+
+ + +
+ + + Select metric for service map display + +
- -
-
- - - + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+ + + } + aria-controls="service-select-dropdown" + compressed={true} + disabled={false} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" + /> + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="service-select-dropdown" + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + repositionOnScroll={true} + > + +
+
+ + + + } + aria-controls="service-select-dropdown" + compressed={true} + disabled={false} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onBlur={[Function]} + onChange={[Function]} + onClick={[Function]} + onFocus={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" + > + + } + compressed={true} + fullWidth={false} + icon="search" + isLoading={false} + prepend="Focus on" + > +
+ + + +
+ + + + +
+ + + + + +
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + +
+ +
+
+ +
+ Mock Graph +
+
+
+ + + +
+
+
+
+ +
+ - - + "analytics-service": Object { + "average_latency": 150, + "destServices": Array [ + "order", + "inventory", + "authentication", + "payment", + "recommendation", + ], + "error_rate": 0, + "id": 2, + "latency": 12.99, + "relatedServices": Array [ + "order", + "inventory", + "authentication", + "payment", + "recommendation", + ], + "serviceName": "analytics-service", + "targetServices": Array [], + "throughput": 37, + "traceGroups": Array [ + Object { + "targetResource": Array [ + "/logs", + ], + "traceGroup": "client_cancel_order", + }, + Object { + "targetResource": Array [ + "/logs", + ], + "traceGroup": "client_checkout", + }, + Object { + "targetResource": Array [ + "/logs", + ], + "traceGroup": "client_create_order", + }, + Object { + "targetResource": Array [ + "/logs", + ], + "traceGroup": "client_delivery_status", + }, + Object { + "targetResource": Array [ + "/logs", + ], + "traceGroup": "client_pay_order", + }, + Object { + "targetResource": Array [ + "/logs", + ], + "traceGroup": "load_main_screen", + }, + ], + }, + "authentication": Object { + "average_latency": 350, + "destServices": Array [ + "frontend-client", + ], + "error_rate": 8.33, + "id": 6, + "latency": 139.09, + "relatedServices": Array [ + "analytics-service", + "recommendation", + "frontend-client", + ], + "serviceName": "authentication", + "targetServices": Array [ + "analytics-service", + "recommendation", + ], + "throughput": 12, + "traceGroups": Array [ + Object { + "targetResource": Array [ + "server_request_login", + ], + "traceGroup": "load_main_screen", + }, + ], + }, + "database": Object { + "average_latency": 200, + "destServices": Array [ + "order", + "inventory", + ], + "error_rate": 3.77, + "id": 3, + "latency": 49.54, + "relatedServices": Array [ + "order", + "inventory", + ], + "serviceName": "database", + "targetServices": Array [], + "throughput": 53, + "traceGroups": Array [ + Object { + "targetResource": Array [ + "cartEmpty", + ], + "traceGroup": "client_cancel_order", + }, + Object { + "targetResource": Array [ + "updateItem", + ], + "traceGroup": "client_checkout", + }, + Object { + "targetResource": Array [ + "addItemToCart", + ], + "traceGroup": "client_create_order", + }, + Object { + "targetResource": Array [ + "getCart", + ], + "traceGroup": "client_delivery_status", + }, + Object { + "targetResource": Array [ + "cartSold", + ], + "traceGroup": "client_pay_order", + }, + Object { + "targetResource": Array [ + "getInventory", + ], + "traceGroup": "load_main_screen", + }, + ], + }, + "frontend-client": Object { + "average_latency": 250, + "destServices": Array [], + "error_rate": 7.41, + "id": 4, + "latency": 207.71, + "relatedServices": Array [ + "order", + "payment", + "authentication", + ], + "serviceName": "frontend-client", + "targetServices": Array [ + "order", + "payment", + "authentication", + ], + "throughput": 27, + "traceGroups": Array [ + Object { + "targetResource": Array [], + "traceGroup": "client_cancel_order", + }, + Object { + "targetResource": Array [], + "traceGroup": "client_checkout", + }, + Object { + "targetResource": Array [], + "traceGroup": "client_create_order", + }, + Object { + "targetResource": Array [], + "traceGroup": "client_delivery_status", + }, + Object { + "targetResource": Array [], + "traceGroup": "client_pay_order", + }, + Object { + "targetResource": Array [], + "traceGroup": "load_main_screen", + }, + ], + }, + "inventory": Object { + "average_latency": 300, + "destServices": Array [ + "payment", + "recommendation", + ], + "error_rate": 3.23, + "id": 5, + "latency": 183.52, + "relatedServices": Array [ + "analytics-service", + "database", + "payment", + "recommendation", + ], + "serviceName": "inventory", + "targetServices": Array [ + "analytics-service", + "database", + ], + "throughput": 31, + "traceGroups": Array [ + Object { + "targetResource": Array [ + "update_inventory", + ], + "traceGroup": "client_checkout", + }, + Object { + "targetResource": Array [ + "read_inventory", + ], + "traceGroup": "load_main_screen", + }, + ], + }, + "order": Object { + "average_latency": 100, + "destServices": Array [ + "frontend-client", + ], + "error_rate": 4.17, + "id": 1, + "latency": 90.1, + "relatedServices": Array [ + "analytics-service", + "database", + "frontend-client", + ], + "serviceName": "order", + "targetServices": Array [ + "analytics-service", + "database", + ], + "throughput": 48, + "traceGroups": Array [ + Object { + "targetResource": Array [ + "clear_order", + ], + "traceGroup": "client_cancel_order", + }, + Object { + "targetResource": Array [ + "update_order", + ], + "traceGroup": "client_create_order", + }, + Object { + "targetResource": Array [ + "get_order", + ], + "traceGroup": "client_delivery_status", + }, + Object { + "targetResource": Array [ + "pay_order", + ], + "traceGroup": "client_pay_order", + }, + ], + }, + "payment": Object { + "average_latency": 400, + "destServices": Array [ + "frontend-client", + ], + "error_rate": 9.09, + "id": 7, + "latency": 134.36, + "relatedServices": Array [ + "analytics-service", + "inventory", + "frontend-client", + ], + "serviceName": "payment", + "targetServices": Array [ + "analytics-service", + "inventory", + ], + "throughput": 11, + "traceGroups": Array [ + Object { + "targetResource": Array [ + "payment", + ], + "traceGroup": "client_checkout", + }, + ], + }, + "recommendation": Object { + "average_latency": 450, + "destServices": Array [ + "authentication", + ], + "error_rate": 6.25, + "id": 8, + "latency": 176.97, + "relatedServices": Array [ + "analytics-service", + "inventory", + "authentication", + ], + "serviceName": "recommendation", + "targetServices": Array [ + "analytics-service", + "inventory", + ], + "throughput": 16, + "traceGroups": Array [ + Object { + "targetResource": Array [ + "recommend", + ], + "traceGroup": "load_main_screen", + }, + ], + }, + } + } + ticks={ + Array [ + 0, + 50, + 100, + 150, + 200, + 250, + ] + } + > +
+ + +
+ + + +
+ + +
+ +
+ unmatched-node-legend +
+
+ +
+ +
+ No match +
+
+
+
+
+
+
+ +
+ +
+ +
- + > +
+ + `; diff --git a/public/components/trace_analytics/components/common/plots/__tests__/service_map.test.tsx b/public/components/trace_analytics/components/common/plots/__tests__/service_map.test.tsx index b7d505fb8f..8e2fe78ede 100644 --- a/public/components/trace_analytics/components/common/plots/__tests__/service_map.test.tsx +++ b/public/components/trace_analytics/components/common/plots/__tests__/service_map.test.tsx @@ -3,25 +3,243 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { configure, shallow } from 'enzyme'; +import { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; +import { mount } from 'enzyme'; import React from 'react'; -import { TEST_SERVICE_MAP } from '../../../../../../../test/constants'; +import { act } from '@testing-library/react'; import { ServiceMap } from '../service_map'; +import { EuiFieldSearch, EuiSelectable } from '@elastic/eui'; +import { TEST_SERVICE_MAP } from '../../../../../../../test/constants'; +import Graph from 'react-graph-vis'; + +configure({ adapter: new Adapter() }); + +// Mock uuid +jest.mock('uuid', () => ({ + v4: jest.fn(() => 'static-uuid'), +})); + +// Normalize dynamic values in snapshots +expect.addSnapshotSerializer({ + test: (val) => typeof val === 'string' && /^[a-f0-9-]{36}$/.test(val), + print: () => '""', +}); + +// Mock crypto.getRandomValues +const crypto = { + getRandomValues: jest.fn((arr) => arr.fill(0)), // Fill with consistent values +}; +Object.defineProperty(global, 'crypto', { value: crypto }); + +const mockContext = ({ + canvas: document.createElement('canvas'), + fillRect: jest.fn(), + clearRect: jest.fn(), + getImageData: jest.fn(() => ({ data: new Uint8ClampedArray() })), + putImageData: jest.fn(), + createImageData: jest.fn(), + setTransform: jest.fn(), + drawImage: jest.fn(), + save: jest.fn(), + fillText: jest.fn(), + restore: jest.fn(), + beginPath: jest.fn(), + moveTo: jest.fn(), + lineTo: jest.fn(), + closePath: jest.fn(), + stroke: jest.fn(), + translate: jest.fn(), + scale: jest.fn(), + rotate: jest.fn(), + arc: jest.fn(), + fill: jest.fn(), + measureText: jest.fn(() => ({ width: 0 })), + transform: jest.fn(), + rect: jest.fn(), + globalAlpha: 1, + globalCompositeOperation: 'source-over', + filter: 'none', + imageSmoothingEnabled: true, + imageSmoothingQuality: 'low', + strokeStyle: '#000', + fillStyle: '#000', + shadowOffsetX: 0, + shadowOffsetY: 0, + shadowBlur: 0, + shadowColor: 'rgba(0,0,0,0)', + lineWidth: 1, + lineCap: 'butt', + lineJoin: 'miter', + miterLimit: 10, + lineDashOffset: 0, + font: '10px sans-serif', + textAlign: 'start', + textBaseline: 'alphabetic', + direction: 'ltr', + getContextAttributes: jest.fn(() => ({ + alpha: true, + desynchronized: false, + colorSpace: 'srgb', + willReadFrequently: false, + })), +} as unknown) as CanvasRenderingContext2D; + +jest + .spyOn(HTMLCanvasElement.prototype, 'getContext') + .mockImplementation((contextId) => (contextId === '2d' ? mockContext : null)); + +jest.mock('react-graph-vis', () => { + const GraphMock = () =>
Mock Graph
; + return GraphMock; +}); -describe('Service map component', () => { - configure({ adapter: new Adapter() }); - - it('renders service map', async () => { - const setServiceMapIdSelected = jest.fn((e) => {}); - const wrapper = shallow( - - ); +async function setFocusOnService(wrapper: ReturnType, serviceName: string) { + const searchField = wrapper.find(EuiFieldSearch).first(); + expect(searchField.exists()).toBeTruthy(); + + await act(async () => { + const mockEvent = { + preventDefault: jest.fn(), + stopPropagation: jest.fn(), + target: { value: '' }, + }; + searchField.prop('onClick')?.((mockEvent as unknown) as React.MouseEvent); + }); + wrapper.update(); + + const selectable = wrapper.find(EuiSelectable); + const onChange = selectable.prop('onChange'); + + if (onChange) { + await act(async () => { + onChange([{ label: serviceName, checked: 'on' }]); + }); + wrapper.update(); + } else { + throw new Error('onChange handler is undefined on EuiSelectable'); + } + expect(wrapper.find(EuiFieldSearch).prop('placeholder')).toBe(serviceName); +} + +describe('ServiceMap Component', () => { + const defaultProps = { + serviceMap: TEST_SERVICE_MAP, + idSelected: 'latency' as 'latency' | 'error_rate' | 'throughput', + setIdSelected: jest.fn(), + page: 'dashboard' as + | 'app' + | 'appCreate' + | 'dashboard' + | 'traces' + | 'services' + | 'serviceView' + | 'detailFlyout' + | 'traceView', + mode: 'jaeger', + currService: '', + filters: [], + setFilters: jest.fn(), + addFilter: jest.fn(), + removeFilter: jest.fn(), + isLoading: false, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders service map component', () => { + const wrapper = mount(); expect(wrapper).toMatchSnapshot(); }); + + it('renders application composition map title when page is app', () => { + const wrapper = mount(); + expect(wrapper.find('PanelTitle').prop('title')).toBe('Application Composition Map'); + }); + + it('renders service map title for other pages', () => { + const wrapper = mount(); + expect(wrapper.find('PanelTitle').prop('title')).toBe('Service map'); + }); + + describe('Service search and selection', () => { + it('updates placeholder when service is focused', async () => { + const wrapper = mount(); + await setFocusOnService(wrapper, 'order'); + }); + + it('clears focus with refresh button', async () => { + const wrapper = mount(); + await setFocusOnService(wrapper, 'order'); + + // Verify focus is set + expect(wrapper.find(EuiFieldSearch).prop('placeholder')).toBe('order'); + + // Find and click the refresh button + const refreshButton = wrapper.find('button[data-test-subj="serviceMapRefreshButton"]'); + expect(refreshButton.exists()).toBeTruthy(); + + await act(async () => { + refreshButton.simulate('click'); + }); + wrapper.update(); + + // Verify the search field is cleared + const updatedSearchField = wrapper.find(EuiFieldSearch).first(); + expect(updatedSearchField.prop('value')).toBe(''); + expect(updatedSearchField.prop('placeholder')).not.toBe('order'); + }); + }); + + describe('Metric selection', () => { + it('changes selected metric', async () => { + const setIdSelected = jest.fn(); + const wrapper = mount(); + + const buttonGroup = wrapper.find('EuiButtonGroup'); + const onChange = buttonGroup.prop('onChange'); + + if (onChange) { + await act(async () => { + onChange('error_rate' as any); + }); + wrapper.update(); + } else { + throw new Error('onChange handler is undefined on EuiButtonGroup'); + } + + expect(setIdSelected).toHaveBeenCalledWith('error_rate'); + }); + }); + + describe('Service dependencies', () => { + it('shows related services when focusing on a service', async () => { + const wrapper = mount(); + await setFocusOnService(wrapper, 'order'); + + // Verify that the graph exists and has nodes + const graph = wrapper.find(Graph); + expect(graph.exists()).toBeTruthy(); + + const graphProps = graph.props() as { graph: { nodes: any[] } }; + expect(graphProps.graph).toBeDefined(); + expect(graphProps.graph.nodes.length).toBeGreaterThan(0); + }); + }); + + describe('Loading state', () => { + it('shows loading indicator when isLoading is true', () => { + const wrapper = mount(); + expect(wrapper.find('.euiLoadingSpinner').exists()).toBeTruthy(); + }); + }); + + describe('Empty state', () => { + it('handles empty service map', () => { + const wrapper = mount(); + expect(wrapper.find('Graph').exists()).toBeFalsy(); + }); + }); }); diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index 760baef0f5..3b0d338c7d 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -14,12 +14,12 @@ import { EuiSuperSelect, EuiSuperSelectOption, EuiSelectable, - EuiSelectableOption, EuiPopover, EuiFieldSearch, EuiLoadingSpinner, + EuiToolTip, } from '@elastic/eui'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; // @ts-ignore import Graph from 'react-graph-vis'; import { ServiceNodeDetails } from '../../../../../../common/types/trace_analytics'; @@ -61,7 +61,7 @@ export function ServiceMap({ filterByCurrService, includeMetricsCallback, mode, - filters, + filters = [], setFilters, hideSearchBar = false, }: { @@ -95,7 +95,6 @@ export function ServiceMap({ const [ticks, setTicks] = useState([]); const [items, setItems] = useState({}); const [query, setQuery] = useState(''); - const [selectableOptions, setSelectableOptions] = useState([]); const [isLoading, setIsLoading] = useState(true); const [filterChange, setIsFilterChange] = useState(false); const [focusedService, setFocusedService] = useState(null); @@ -116,10 +115,16 @@ export function ServiceMap({ ]; const [selectedNodeDetails, setSelectedNodeDetails] = useState(null); - const [selectableValue, setSelectableValue] = useState>>([]); const [isPopoverOpen, setPopoverOpen] = useState(false); + // Memoize a boolean to determine if the focus bar should be disabled + const isFocusBarDisabled = useMemo(() => { + return filters.some( + (filter) => filter.field === 'serviceName' && focusedService === filter.value + ); + }, [filters, focusedService]); + const onChangeSelectable = (value: React.SetStateAction>>) => { // if the change is changing for the first time then callback servicemap with metrics if (selectableValue.length === 0 && value.length !== 0) { @@ -154,21 +159,6 @@ export function ServiceMap({ setFilters(updatedFilters); }; - useEffect(() => { - if (items?.graph?.nodes) { - const visibleNodes = items.graph.nodes.map((node) => node.label); - const options = Object.keys(serviceMap) - .filter((key) => visibleNodes.includes(serviceMap[key].serviceName)) - .map((key) => ({ - label: serviceMap[key].serviceName, - value: serviceMap[key].serviceName, - })); - setSelectableOptions(options); - } else { - setSelectableOptions([]); // Ensure options are empty if items.graph.nodes doesn't exist - } - }, [items.graph, serviceMap]); - const options = { layout: { randomSeed: 10, @@ -328,14 +318,9 @@ export function ServiceMap({ serviceMap[service]?.relatedServices, true // Enable filtering to focus on connected nodes ); - setSelectedNodeDetails(null); setItems(filteredGraph); setFocusedService(service); - setInvalid(false); - setGraphKey((prevKey) => prevKey + 1); } - } else { - setInvalid(true); } }; @@ -422,35 +407,57 @@ export function ServiceMap({ setPopoverOpen(!isPopoverOpen)} - onChange={(e) => { - const newValue = e.target.value; - setQuery(newValue); - if (newValue === '') { - setGraphKey((prevKey) => prevKey + 1); - setQuery(''); - onFocus(''); - } - }} - isInvalid={query.length > 0 && invalid} - append={ - { - setGraphKey((prevKey) => prevKey + 1); - setQuery(''); - onFocus(''); - }} - /> + + > + { + if (!isFocusBarDisabled) setPopoverOpen(!isPopoverOpen); + }} + onChange={(e) => { + if (!isFocusBarDisabled) { + const newValue = e.target.value; + setQuery(newValue); + if (newValue === '') { + if (focusedService) { + setGraphKey((prevKey) => prevKey + 1); + setQuery(''); + onFocus(focusedService); + } else { + setGraphKey((prevKey) => prevKey + 1); + setQuery(''); + onFocus(''); + } + } + } + }} + isInvalid={query.length > 0 && invalid} + append={ + { + setGraphKey((prevKey) => prevKey + 1); + setQuery(''); + onFocus(''); + }} + /> + } + aria-controls="service-select-dropdown" + disabled={isFocusBarDisabled} + /> + } isOpen={isPopoverOpen} closePopover={() => setPopoverOpen(false)} @@ -468,9 +475,12 @@ export function ServiceMap({ isClearable: true, autoFocus: true, }} - options={selectableOptions.filter((option) => - option.label.toLowerCase().includes(query.toLowerCase()) - )} + options={ + items?.graph?.nodes?.map((node) => ({ + label: node.label, + checked: focusedService === node.label ? 'on' : undefined, + })) || [] + } singleSelection={true} onChange={(newOptions) => { const selectedOption = newOptions.find((option) => option.checked === 'on'); @@ -479,9 +489,10 @@ export function ServiceMap({ setPopoverOpen(false); return; } - setQuery(selectedOption.label); + setQuery(''); onFocus(selectedOption.label); setPopoverOpen(false); + setGraphKey((prevKey) => prevKey + 1); } }} listProps={{ bordered: true, style: { width: '300px' } }} @@ -537,7 +548,7 @@ export function ServiceMap({ zIndex: 1000, }} > - +
)} {selectedNodeDetails && ( diff --git a/public/components/trace_analytics/components/services/__tests__/__snapshots__/services.test.tsx.snap b/public/components/trace_analytics/components/services/__tests__/__snapshots__/services.test.tsx.snap index ad13907b86..c49ee9d309 100644 --- a/public/components/trace_analytics/components/services/__tests__/__snapshots__/services.test.tsx.snap +++ b/public/components/trace_analytics/components/services/__tests__/__snapshots__/services.test.tsx.snap @@ -2081,27 +2081,35 @@ exports[`Services component renders empty services page 1`] = ` - } - aria-controls="service-select-dropdown" - compressed={true} - fullWidth={false} - incremental={false} - isClearable={true} - isInvalid={false} - isLoading={false} - onChange={[Function]} - onClick={[Function]} - placeholder="Service name" - prepend="Focus on" - value="" - /> + + + } + aria-controls="service-select-dropdown" + compressed={true} + disabled={false} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" + /> + } closePopover={[Function]} display="inlineBlock" @@ -2127,169 +2135,195 @@ exports[`Services component renders empty services page 1`] = `
- - } - aria-controls="service-select-dropdown" - compressed={true} - fullWidth={false} - incremental={false} - isClearable={true} - isInvalid={false} - isLoading={false} - onChange={[Function]} - onClick={[Function]} - placeholder="Service name" - prepend="Focus on" - value="" + - - } - compressed={true} - fullWidth={false} - icon="search" - isLoading={false} - prepend="Focus on" + -
+ } + aria-controls="service-select-dropdown" + compressed={true} + disabled={false} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onBlur={[Function]} + onChange={[Function]} + onClick={[Function]} + onFocus={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" > - - - -
- - - - +
+ + +
- + + + - - - - + + + + + + + + +
+
-
-
- - - -
-
-
+ + + + + + + + +
+ + + +
@@ -6190,27 +6224,35 @@ exports[`Services component renders services page 1`] = ` - } - aria-controls="service-select-dropdown" - compressed={true} - fullWidth={false} - incremental={false} - isClearable={true} - isInvalid={false} - isLoading={false} - onChange={[Function]} - onClick={[Function]} - placeholder="Service name" - prepend="Focus on" - value="" - /> + + + } + aria-controls="service-select-dropdown" + compressed={true} + disabled={false} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" + /> + } closePopover={[Function]} display="inlineBlock" @@ -6236,169 +6278,195 @@ exports[`Services component renders services page 1`] = `
- - } - aria-controls="service-select-dropdown" - compressed={true} - fullWidth={false} - incremental={false} - isClearable={true} - isInvalid={false} - isLoading={false} - onChange={[Function]} - onClick={[Function]} - placeholder="Service name" - prepend="Focus on" - value="" + - - } - compressed={true} - fullWidth={false} - icon="search" - isLoading={false} - prepend="Focus on" + -
+ } + aria-controls="service-select-dropdown" + compressed={true} + disabled={false} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onBlur={[Function]} + onChange={[Function]} + onClick={[Function]} + onFocus={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" > - - - -
- - - - +
+ + +
- - + + +
- - - + + + + + + + + +
+
- -
- - - -
- - + + + + + + + + +
+
+
+ +
diff --git a/test/constants.ts b/test/constants.ts index fe399e08b1..c7ef31a754 100644 --- a/test/constants.ts +++ b/test/constants.ts @@ -419,6 +419,7 @@ export const TEST_SERVICE_MAP = { order: { serviceName: 'order', id: 1, + average_latency: 100, traceGroups: [ { traceGroup: 'client_cancel_order', @@ -442,10 +443,12 @@ export const TEST_SERVICE_MAP = { latency: 90.1, error_rate: 4.17, throughput: 48, + relatedServices: ['analytics-service', 'database', 'frontend-client'], }, 'analytics-service': { serviceName: 'analytics-service', id: 2, + average_latency: 150, traceGroups: [ { traceGroup: 'client_cancel_order', @@ -477,10 +480,12 @@ export const TEST_SERVICE_MAP = { latency: 12.99, error_rate: 0, throughput: 37, + relatedServices: ['order', 'inventory', 'authentication', 'payment', 'recommendation'], }, database: { serviceName: 'database', id: 3, + average_latency: 200, traceGroups: [ { traceGroup: 'client_cancel_order', @@ -504,7 +509,7 @@ export const TEST_SERVICE_MAP = { }, { traceGroup: 'load_main_screen', - targetResource: ['getIntentory'], + targetResource: ['getInventory'], }, ], targetServices: [], @@ -512,10 +517,12 @@ export const TEST_SERVICE_MAP = { latency: 49.54, error_rate: 3.77, throughput: 53, + relatedServices: ['order', 'inventory'], }, 'frontend-client': { serviceName: 'frontend-client', id: 4, + average_latency: 250, traceGroups: [ { traceGroup: 'client_cancel_order', @@ -547,10 +554,12 @@ export const TEST_SERVICE_MAP = { latency: 207.71, error_rate: 7.41, throughput: 27, + relatedServices: ['order', 'payment', 'authentication'], }, inventory: { serviceName: 'inventory', id: 5, + average_latency: 300, traceGroups: [ { traceGroup: 'client_checkout', @@ -566,10 +575,12 @@ export const TEST_SERVICE_MAP = { latency: 183.52, error_rate: 3.23, throughput: 31, + relatedServices: ['analytics-service', 'database', 'payment', 'recommendation'], }, authentication: { serviceName: 'authentication', id: 6, + average_latency: 350, traceGroups: [ { traceGroup: 'load_main_screen', @@ -581,10 +592,12 @@ export const TEST_SERVICE_MAP = { latency: 139.09, error_rate: 8.33, throughput: 12, + relatedServices: ['analytics-service', 'recommendation', 'frontend-client'], }, payment: { serviceName: 'payment', id: 7, + average_latency: 400, traceGroups: [ { traceGroup: 'client_checkout', @@ -596,10 +609,12 @@ export const TEST_SERVICE_MAP = { latency: 134.36, error_rate: 9.09, throughput: 11, + relatedServices: ['analytics-service', 'inventory', 'frontend-client'], }, recommendation: { serviceName: 'recommendation', id: 8, + average_latency: 450, traceGroups: [ { traceGroup: 'load_main_screen', @@ -611,6 +626,7 @@ export const TEST_SERVICE_MAP = { latency: 176.97, error_rate: 6.25, throughput: 16, + relatedServices: ['analytics-service', 'inventory', 'authentication'], }, }; From f50422e11c324e1da45fb7bc2465b71ea75203b2 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Mon, 2 Dec 2024 09:45:10 -0800 Subject: [PATCH 05/21] add query filtering back Signed-off-by: Adam Tackett --- .../components/common/plots/service_map.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index 3b0d338c7d..a5bb0b7e14 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -476,10 +476,12 @@ export function ServiceMap({ autoFocus: true, }} options={ - items?.graph?.nodes?.map((node) => ({ - label: node.label, - checked: focusedService === node.label ? 'on' : undefined, - })) || [] + items?.graph?.nodes + ?.filter((node) => node.label.toLowerCase().includes(query.toLowerCase())) + .map((node) => ({ + label: node.label, + checked: focusedService === node.label ? 'on' : undefined, + })) || [] } singleSelection={true} onChange={(newOptions) => { From 10ef09335e03b23c069f78965696017341a4f2a3 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Mon, 2 Dec 2024 16:37:14 -0800 Subject: [PATCH 06/21] change url check to use hook state Signed-off-by: Adam Tackett --- .../services/__tests__/service_view.test.tsx | 68 +++++++++++-------- .../components/services/service_view.tsx | 20 +++--- 2 files changed, 51 insertions(+), 37 deletions(-) diff --git a/public/components/trace_analytics/components/services/__tests__/service_view.test.tsx b/public/components/trace_analytics/components/services/__tests__/service_view.test.tsx index 0299fe2555..a269be81e2 100644 --- a/public/components/trace_analytics/components/services/__tests__/service_view.test.tsx +++ b/public/components/trace_analytics/components/services/__tests__/service_view.test.tsx @@ -7,39 +7,51 @@ import { configure, shallow } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import React from 'react'; import { ServiceView } from '..'; -import { coreStartMock } from '../../../../../../test/__mocks__/coreMocks'; + +jest.mock('../../../../../../test/__mocks__/coreMocks', () => ({ + coreStartMock: { + chrome: { setBreadcrumbs: jest.fn() }, + http: { post: jest.fn() }, + }, +})); + +jest.mock('react-router-dom', () => ({ + useLocation: jest.fn().mockReturnValue({ + pathname: '/services', + search: '?serviceId=test-id', + hash: '', + state: null, + key: '', + }), + useHistory: jest.fn(), +})); describe('Service view component', () => { configure({ adapter: new Adapter() }); - it('renders service view', () => { - const core = coreStartMock; - const setQuery = jest.fn(); - const setFilters = jest.fn(); - const setStartTime = jest.fn(); - const setEndTime = jest.fn(); - const addFilter = jest.fn(); - const wrapper = shallow( - - ); + const { coreStartMock } = jest.requireMock('../../../../../../test/__mocks__/coreMocks'); + const defaultProps = { + serviceName: 'order', + chrome: coreStartMock.chrome, + appConfigs: [], + parentBreadcrumbs: [{ text: 'test', href: 'test#/' }], + http: coreStartMock.http, + query: '', + setQuery: jest.fn(), + filters: [], + setFilters: jest.fn(), + startTime: 'now-5m', + setStartTime: jest.fn(), + endTime: 'now', + setEndTime: jest.fn(), + addFilter: jest.fn(), + mode: 'data_prepper', + dataSourceMDSId: [{ id: '', label: '' }], + }; + + it('renders service view', () => { + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/public/components/trace_analytics/components/services/service_view.tsx b/public/components/trace_analytics/components/services/service_view.tsx index a95b023b10..e3328c5b81 100644 --- a/public/components/trace_analytics/components/services/service_view.tsx +++ b/public/components/trace_analytics/components/services/service_view.tsx @@ -76,16 +76,18 @@ export function ServiceView(props: ServiceViewProps) { >('latency'); const [redirect, setRedirect] = useState(false); const [actionsMenuPopover, setActionsMenuPopover] = useState(false); + const [serviceId, setServiceId] = useState(null); + const location = useLocation(); - let serviceId: string | null = null; - - try { - const location = useLocation(); - const params = new URLSearchParams(location?.search || ''); - serviceId = params.get('serviceId'); - } catch (error) { - serviceId = null; - } + useEffect(() => { + try { + const params = new URLSearchParams(location.search); + const id = params.get('serviceId'); + setServiceId(id); + } catch (error) { + setServiceId(null); + } + }, [location]); const hideSearchBarCheck = page === 'serviceFlyout' || serviceId !== ''; From 8bc552366325addd4cfa763a8ecd705bb4fa80c6 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Tue, 3 Dec 2024 10:31:36 -0800 Subject: [PATCH 07/21] fix location error Signed-off-by: Adam Tackett --- .../components/services/service_view.tsx | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/public/components/trace_analytics/components/services/service_view.tsx b/public/components/trace_analytics/components/services/service_view.tsx index e3328c5b81..a95b023b10 100644 --- a/public/components/trace_analytics/components/services/service_view.tsx +++ b/public/components/trace_analytics/components/services/service_view.tsx @@ -76,18 +76,16 @@ export function ServiceView(props: ServiceViewProps) { >('latency'); const [redirect, setRedirect] = useState(false); const [actionsMenuPopover, setActionsMenuPopover] = useState(false); - const [serviceId, setServiceId] = useState(null); - const location = useLocation(); - useEffect(() => { - try { - const params = new URLSearchParams(location.search); - const id = params.get('serviceId'); - setServiceId(id); - } catch (error) { - setServiceId(null); - } - }, [location]); + let serviceId: string | null = null; + + try { + const location = useLocation(); + const params = new URLSearchParams(location?.search || ''); + serviceId = params.get('serviceId'); + } catch (error) { + serviceId = null; + } const hideSearchBarCheck = page === 'serviceFlyout' || serviceId !== ''; From c1ac1c251e56ae84114cae2d1765794c37097fb6 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Wed, 4 Dec 2024 13:13:05 -0800 Subject: [PATCH 08/21] move flyout into hashrouter, remove extra filter Signed-off-by: Adam Tackett --- .../components/common/plots/service_map.tsx | 4 +--- .../components/services/service_view.tsx | 20 ++++++++++--------- public/components/trace_analytics/home.tsx | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index a5bb0b7e14..e33b9bb09b 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -84,9 +84,7 @@ export function ServiceMap({ includeMetricsCallback?: () => void; mode?: string; filters: FilterType[]; - setFilters: - | React.Dispatch> - | ((filters: FilterType[]) => void); + setFilters: (filters: FilterType[]) => void; hideSearchBar?: boolean; }) { const [graphKey, setGraphKey] = useState(0); // adding key to allow for re-renders diff --git a/public/components/trace_analytics/components/services/service_view.tsx b/public/components/trace_analytics/components/services/service_view.tsx index a95b023b10..2ca6d2333b 100644 --- a/public/components/trace_analytics/components/services/service_view.tsx +++ b/public/components/trace_analytics/components/services/service_view.tsx @@ -76,16 +76,18 @@ export function ServiceView(props: ServiceViewProps) { >('latency'); const [redirect, setRedirect] = useState(false); const [actionsMenuPopover, setActionsMenuPopover] = useState(false); + const [serviceId, setServiceId] = useState(null); + const location = useLocation(); - let serviceId: string | null = null; - - try { - const location = useLocation(); - const params = new URLSearchParams(location?.search || ''); - serviceId = params.get('serviceId'); - } catch (error) { - serviceId = null; - } + useEffect(() => { + try { + const params = new URLSearchParams(location?.search || ''); + const id = params.get('serviceId'); + setServiceId(id); + } catch (error) { + setServiceId(null); + } + }, [location]); const hideSearchBarCheck = page === 'serviceFlyout' || serviceId !== ''; diff --git a/public/components/trace_analytics/home.tsx b/public/components/trace_analytics/home.tsx index d107bfe2d6..ccdb45c391 100644 --- a/public/components/trace_analytics/home.tsx +++ b/public/components/trace_analytics/home.tsx @@ -490,9 +490,9 @@ export const Home = (props: HomeProps) => { }} /> } /> + {flyout} + {spanFlyoutComponent} - {flyout} - {spanFlyoutComponent} ); }; From 187754013f8c55e79f1fcc9d5026472c204b912b Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Thu, 5 Dec 2024 15:51:25 -0800 Subject: [PATCH 09/21] fix applications, address comments Signed-off-by: Adam Tackett --- .../app_analytics_test/app_analytics.spec.js | 2 +- .../__snapshots__/flyout.test.tsx.snap | 8 +- .../plots/__tests__/service_map.test.tsx | 59 +--- .../components/common/plots/service_map.tsx | 12 +- .../__snapshots__/services.test.tsx.snap | 3 + .../services_table.test.tsx.snap | 4 + .../components/services/services_content.tsx | 1 + .../components/services/services_table.tsx | 286 +++++++++--------- .../components/traces/span_detail_panel.tsx | 4 +- test/constants.ts | 53 ++++ 10 files changed, 224 insertions(+), 208 deletions(-) diff --git a/.cypress/integration/app_analytics_test/app_analytics.spec.js b/.cypress/integration/app_analytics_test/app_analytics.spec.js index ce0ff7820f..de8862d82d 100644 --- a/.cypress/integration/app_analytics_test/app_analytics.spec.js +++ b/.cypress/integration/app_analytics_test/app_analytics.spec.js @@ -274,7 +274,7 @@ describe('Viewing application', () => { it('Opens service detail flyout when Service Name is clicked', () => { cy.get('[data-test-subj="app-analytics-serviceTab"]').click(); - cy.get('*[data-test-subj^="service-flyout-action-btntrace"]').eq(0).click(); + cy.get('*[data-test-subj^="service-link"]').eq(0).click(); cy.get('[data-test-subj="serviceDetailFlyoutTitle"]').should('be.visible'); cy.get('[data-test-subj="Number of connected servicesDescriptionList"]').should('contain', '3'); cy.get('[data-text="Errors"]').eq(1).click(); // Selecting errors tab within flyout diff --git a/public/components/application_analytics/__tests__/__snapshots__/flyout.test.tsx.snap b/public/components/application_analytics/__tests__/__snapshots__/flyout.test.tsx.snap index 7f5a964e89..8647fe383e 100644 --- a/public/components/application_analytics/__tests__/__snapshots__/flyout.test.tsx.snap +++ b/public/components/application_analytics/__tests__/__snapshots__/flyout.test.tsx.snap @@ -1221,7 +1221,7 @@ exports[`Trace Detail Render Flyout component render trace detail 1`] = ` "yref": "paper", }, ], - "width": undefined, + "width": 412, "xaxis": Object { "color": "#91989c", "range": Array [ @@ -1295,7 +1295,7 @@ exports[`Trace Detail Render Flyout component render trace detail 1`] = ` }, ], "showlegend": false, - "width": undefined, + "width": 412, "xaxis": Object { "color": "#91989c", "range": Array [ @@ -1365,7 +1365,7 @@ exports[`Trace Detail Render Flyout component render trace detail 1`] = ` }, "paper_bgcolor": "rgba(0, 0, 0, 0)", "plot_bgcolor": "rgba(0, 0, 0, 0)", - "width": undefined, + "width": 412, "xaxis": Object { "color": "#91989c", "range": Array [ @@ -1425,7 +1425,7 @@ exports[`Trace Detail Render Flyout component render trace detail 1`] = ` "paper_bgcolor": "rgba(0, 0, 0, 0)", "plot_bgcolor": "rgba(0, 0, 0, 0)", "showlegend": false, - "width": undefined, + "width": 412, "xaxis": Object { "color": "#91989c", "range": Array [ diff --git a/public/components/trace_analytics/components/common/plots/__tests__/service_map.test.tsx b/public/components/trace_analytics/components/common/plots/__tests__/service_map.test.tsx index 8e2fe78ede..60fce85488 100644 --- a/public/components/trace_analytics/components/common/plots/__tests__/service_map.test.tsx +++ b/public/components/trace_analytics/components/common/plots/__tests__/service_map.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { act } from '@testing-library/react'; import { ServiceMap } from '../service_map'; import { EuiFieldSearch, EuiSelectable } from '@elastic/eui'; -import { TEST_SERVICE_MAP } from '../../../../../../../test/constants'; +import { TEST_SERVICE_MAP, MOCK_CANVAS_CONTEXT } from '../../../../../../../test/constants'; import Graph from 'react-graph-vis'; configure({ adapter: new Adapter() }); @@ -32,62 +32,11 @@ const crypto = { }; Object.defineProperty(global, 'crypto', { value: crypto }); -const mockContext = ({ - canvas: document.createElement('canvas'), - fillRect: jest.fn(), - clearRect: jest.fn(), - getImageData: jest.fn(() => ({ data: new Uint8ClampedArray() })), - putImageData: jest.fn(), - createImageData: jest.fn(), - setTransform: jest.fn(), - drawImage: jest.fn(), - save: jest.fn(), - fillText: jest.fn(), - restore: jest.fn(), - beginPath: jest.fn(), - moveTo: jest.fn(), - lineTo: jest.fn(), - closePath: jest.fn(), - stroke: jest.fn(), - translate: jest.fn(), - scale: jest.fn(), - rotate: jest.fn(), - arc: jest.fn(), - fill: jest.fn(), - measureText: jest.fn(() => ({ width: 0 })), - transform: jest.fn(), - rect: jest.fn(), - globalAlpha: 1, - globalCompositeOperation: 'source-over', - filter: 'none', - imageSmoothingEnabled: true, - imageSmoothingQuality: 'low', - strokeStyle: '#000', - fillStyle: '#000', - shadowOffsetX: 0, - shadowOffsetY: 0, - shadowBlur: 0, - shadowColor: 'rgba(0,0,0,0)', - lineWidth: 1, - lineCap: 'butt', - lineJoin: 'miter', - miterLimit: 10, - lineDashOffset: 0, - font: '10px sans-serif', - textAlign: 'start', - textBaseline: 'alphabetic', - direction: 'ltr', - getContextAttributes: jest.fn(() => ({ - alpha: true, - desynchronized: false, - colorSpace: 'srgb', - willReadFrequently: false, - })), -} as unknown) as CanvasRenderingContext2D; - jest .spyOn(HTMLCanvasElement.prototype, 'getContext') - .mockImplementation((contextId) => (contextId === '2d' ? mockContext : null)); + .mockImplementation((contextId) => + contextId === '2d' ? ((MOCK_CANVAS_CONTEXT as unknown) as CanvasRenderingContext2D) : null + ); jest.mock('react-graph-vis', () => { const GraphMock = () =>
Mock Graph
; diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index e33b9bb09b..6832af4b8d 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -426,15 +426,9 @@ export function ServiceMap({ const newValue = e.target.value; setQuery(newValue); if (newValue === '') { - if (focusedService) { - setGraphKey((prevKey) => prevKey + 1); - setQuery(''); - onFocus(focusedService); - } else { - setGraphKey((prevKey) => prevKey + 1); - setQuery(''); - onFocus(''); - } + setGraphKey((prevKey) => prevKey + 1); + setQuery(''); + onFocus(focusedService || ''); } } }} diff --git a/public/components/trace_analytics/components/services/__tests__/__snapshots__/services.test.tsx.snap b/public/components/trace_analytics/components/services/__tests__/__snapshots__/services.test.tsx.snap index c49ee9d309..d6c8a0dbaf 100644 --- a/public/components/trace_analytics/components/services/__tests__/__snapshots__/services.test.tsx.snap +++ b/public/components/trace_analytics/components/services/__tests__/__snapshots__/services.test.tsx.snap @@ -1465,6 +1465,7 @@ exports[`Services component renders empty services page 1`] = ` items={Array []} loading={true} mode="data_prepper" + page="services" selectedItems={Array []} serviceTrends={Object {}} setIsServiceTrendEnabled={[Function]} @@ -3910,6 +3911,7 @@ exports[`Services component renders jaeger services page 1`] = ` jaegerIndicesExist={true} loading={true} mode="jaeger" + page="services" selectedItems={Array []} serviceTrends={Object {}} setIsServiceTrendEnabled={[Function]} @@ -5608,6 +5610,7 @@ exports[`Services component renders services page 1`] = ` items={Array []} loading={true} mode="data_prepper" + page="services" selectedItems={Array []} serviceTrends={Object {}} setIsServiceTrendEnabled={[Function]} diff --git a/public/components/trace_analytics/components/services/__tests__/__snapshots__/services_table.test.tsx.snap b/public/components/trace_analytics/components/services/__tests__/__snapshots__/services_table.test.tsx.snap index 91c056f4f3..af114a4cf2 100644 --- a/public/components/trace_analytics/components/services/__tests__/__snapshots__/services_table.test.tsx.snap +++ b/public/components/trace_analytics/components/services/__tests__/__snapshots__/services_table.test.tsx.snap @@ -670,6 +670,7 @@ exports[`Services table component renders jaeger services table 1`] = ` "field": "actions", "name": "Actions", "render": [Function], + "sortable": false, }, ] } @@ -758,6 +759,7 @@ exports[`Services table component renders jaeger services table 1`] = ` "field": "actions", "name": "Actions", "render": [Function], + "sortable": false, }, ] } @@ -2672,6 +2674,7 @@ exports[`Services table component renders services table 1`] = ` "field": "actions", "name": "Actions", "render": [Function], + "sortable": false, }, ] } @@ -2782,6 +2785,7 @@ exports[`Services table component renders services table 1`] = ` "field": "actions", "name": "Actions", "render": [Function], + "sortable": false, }, ] } diff --git a/public/components/trace_analytics/components/services/services_content.tsx b/public/components/trace_analytics/components/services/services_content.tsx index 269efc001b..d78aa57473 100644 --- a/public/components/trace_analytics/components/services/services_content.tsx +++ b/public/components/trace_analytics/components/services/services_content.tsx @@ -234,6 +234,7 @@ export function ServicesContent(props: ServicesProps) { setIsServiceTrendEnabled={setIsServiceTrendEnabled} serviceTrends={serviceTrends} dataSourceMDSId={props.dataSourceMDSId} + page={page} /> {mode === 'custom_data_prepper' || diff --git a/public/components/trace_analytics/components/services/services_table.tsx b/public/components/trace_analytics/components/services/services_table.tsx index b3b9848394..71da21de57 100644 --- a/public/components/trace_analytics/components/services/services_table.tsx +++ b/public/components/trace_analytics/components/services/services_table.tsx @@ -49,6 +49,7 @@ interface ServicesTableProps { setIsServiceTrendEnabled: React.Dispatch>; serviceTrends: ServiceTrends; dataSourceMDSId: DataSourceOption[]; + page: 'app' | 'services'; } export function ServicesTable(props: ServicesTableProps) { @@ -69,6 +70,7 @@ export function ServicesTable(props: ServicesTableProps) { setIsServiceTrendEnabled, serviceTrends, dataSourceMDSId, + page, } = props; const selectionValue = { @@ -76,7 +78,11 @@ export function ServicesTable(props: ServicesTableProps) { }; const nameColumnAction = (serviceName: string) => { - window.location.href = generateServiceUrl(serviceName, dataSourceMDSId[0].id); + if (page === 'app') { + setCurrentSelectedService(serviceName); + } else { + window.location.href = generateServiceUrl(serviceName, dataSourceMDSId[0].id); + } }; const renderTitleBar = (totalItems?: number) => { @@ -114,144 +120,148 @@ export function ServicesTable(props: ServicesTableProps) { ); }; - const columns = useMemo( - () => - [ - { - field: 'name', - name: 'Name', - align: 'left', - sortable: true, - render: (item: any) => ( - nameColumnAction(item)}> - {item.length < 24 ? item :
{truncate(item, { length: 24 })}
} -
- ), - }, - { - field: 'average_latency', - name: 'Average duration (ms)', - align: 'right', - sortable: true, - render: (item: any, row: any) => ( - - ), - }, - { - field: 'error_rate', - name: 'Error rate', - align: 'right', - sortable: true, - render: (item: any, row: any) => ( - - ), - }, - { - field: 'throughput', - name: 'Request rate', - align: 'right', - sortable: true, - truncateText: true, - render: (item: any, row: any) => ( - - ), - }, - ...(mode === 'data_prepper' || mode === 'custom_data_prepper' - ? [ - { - field: 'number_of_connected_services', - name: 'No. of connected services', - align: 'right', - sortable: true, - truncateText: true, - width: '80px', - render: (item: any) => (item === 0 || item ? item : '-'), - }, - ] - : []), - ...(mode === 'data_prepper' || mode === 'custom_data_prepper' - ? [ - { - field: 'connected_services', - name: 'Connected services', - align: 'left', - sortable: true, - truncateText: true, - render: (item: any) => - item ? ( - {truncate(item.join(', '), { length: 50 })} - ) : ( - '-' - ), - }, - ] - : []), + const columns = useMemo(() => { + const baseColumns = [ + { + field: 'name', + name: 'Name', + align: 'left', + sortable: true, + render: (item: any) => ( + nameColumnAction(item)}> + {item.length < 24 ? item :
{truncate(item, { length: 24 })}
} +
+ ), + }, + { + field: 'average_latency', + name: 'Average duration (ms)', + align: 'right', + sortable: true, + render: (item: any, row: any) => ( + + ), + }, + { + field: 'error_rate', + name: 'Error rate', + align: 'right', + sortable: true, + render: (item: any, row: any) => ( + + ), + }, + { + field: 'throughput', + name: 'Request rate', + align: 'right', + sortable: true, + truncateText: true, + render: (item: any, row: any) => ( + + ), + }, + ...(mode === 'data_prepper' || mode === 'custom_data_prepper' + ? [ + { + field: 'number_of_connected_services', + name: 'No. of connected services', + align: 'right', + sortable: true, + truncateText: true, + width: '80px', + render: (item: any) => (item === 0 || item ? item : '-'), + }, + ] + : []), + ...(mode === 'data_prepper' || mode === 'custom_data_prepper' + ? [ + { + field: 'connected_services', + name: 'Connected services', + align: 'left', + sortable: true, + truncateText: true, + render: (item: any) => + item ? ( + {truncate(item.join(', '), { length: 50 })} + ) : ( + '-' + ), + }, + ] + : []), - { - field: 'traces', - name: 'Traces', - align: 'right', - sortable: true, - truncateText: true, - render: (item: any, row: any) => ( - <> - {item === 0 || item ? ( - { - setRedirect(true); - addFilter({ - field: mode === 'jaeger' ? 'process.serviceName' : 'serviceName', - operator: 'is', - value: row.name, - inverted: false, - disabled: false, - }); - traceColumnAction(); - }} - > - - - ) : ( - '-' - )} - - ), - }, - { - field: 'actions', - name: 'Actions', - align: 'center', - render: (_item: any, row: any) => ( - - setCurrentSelectedService(row.name)}> - - - - - - ), - }, - ] as Array>, - [items] - ); + { + field: 'traces', + name: 'Traces', + align: 'right', + sortable: true, + truncateText: true, + render: (item: any, row: any) => ( + <> + {item === 0 || item ? ( + { + setRedirect(true); + addFilter({ + field: mode === 'jaeger' ? 'process.serviceName' : 'serviceName', + operator: 'is', + value: row.name, + inverted: false, + disabled: false, + }); + traceColumnAction(); + }} + > + + + ) : ( + '-' + )} + + ), + }, + ]; + + if (page !== 'app') { + baseColumns.push({ + field: 'actions', + name: 'Actions', + align: 'center', + render: (_item: any, row: any) => ( + + setCurrentSelectedService(row.name)}> + + + + + + ), + sortable: false, + }); + } + + return baseColumns as Array>; + }, [items, page]); const titleBar = useMemo(() => renderTitleBar(items?.length), [ items, diff --git a/public/components/trace_analytics/components/traces/span_detail_panel.tsx b/public/components/trace_analytics/components/traces/span_detail_panel.tsx index 09ad0b480a..12ae943593 100644 --- a/public/components/trace_analytics/components/traces/span_detail_panel.tsx +++ b/public/components/trace_analytics/components/traces/span_detail_panel.tsx @@ -226,7 +226,9 @@ export function SpanDetailPanel(props: { plot_bgcolor: 'rgba(0, 0, 0, 0)', paper_bgcolor: 'rgba(0, 0, 0, 0)', height: 25 * plotTraces.length + 60, - width: props.isApplicationFlyout ? undefined : availableWidth - dynamicWidthAdjustment, // Allow plotly to render the gantt chart full screen with padding + width: props.isApplicationFlyout + ? availableWidth / 2 - 100 // Allow gantt chart to fit in flyout + : availableWidth - dynamicWidthAdjustment, // Allow gantt chart to render full screen margin: { l: dynamicLeftMargin, r: 5, diff --git a/test/constants.ts b/test/constants.ts index c7ef31a754..98ad23ca09 100644 --- a/test/constants.ts +++ b/test/constants.ts @@ -785,3 +785,56 @@ export const fieldCapQueryResponse2 = { }, }, }; + +export const MOCK_CANVAS_CONTEXT = { + canvas: document.createElement('canvas'), + fillRect: jest.fn(), + clearRect: jest.fn(), + getImageData: jest.fn(() => ({ data: new Uint8ClampedArray() })), + putImageData: jest.fn(), + createImageData: jest.fn(), + setTransform: jest.fn(), + drawImage: jest.fn(), + save: jest.fn(), + fillText: jest.fn(), + restore: jest.fn(), + beginPath: jest.fn(), + moveTo: jest.fn(), + lineTo: jest.fn(), + closePath: jest.fn(), + stroke: jest.fn(), + translate: jest.fn(), + scale: jest.fn(), + rotate: jest.fn(), + arc: jest.fn(), + fill: jest.fn(), + measureText: jest.fn(() => ({ width: 0 })), + transform: jest.fn(), + rect: jest.fn(), + globalAlpha: 1, + globalCompositeOperation: 'source-over', + filter: 'none', + imageSmoothingEnabled: true, + imageSmoothingQuality: 'low', + strokeStyle: '#000', + fillStyle: '#000', + shadowOffsetX: 0, + shadowOffsetY: 0, + shadowBlur: 0, + shadowColor: 'rgba(0,0,0,0)', + lineWidth: 1, + lineCap: 'butt', + lineJoin: 'miter', + miterLimit: 10, + lineDashOffset: 0, + font: '10px sans-serif', + textAlign: 'start', + textBaseline: 'alphabetic', + direction: 'ltr', + getContextAttributes: jest.fn(() => ({ + alpha: true, + desynchronized: false, + colorSpace: 'srgb', + willReadFrequently: false, + })), +}; From 04e30f9b8523ce27b8294b62f612895dadc39133 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Thu, 5 Dec 2024 17:52:19 -0800 Subject: [PATCH 10/21] fix flaky application test Signed-off-by: Adam Tackett --- .cypress/integration/app_analytics_test/app_analytics.spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.cypress/integration/app_analytics_test/app_analytics.spec.js b/.cypress/integration/app_analytics_test/app_analytics.spec.js index de8862d82d..bee7aba52c 100644 --- a/.cypress/integration/app_analytics_test/app_analytics.spec.js +++ b/.cypress/integration/app_analytics_test/app_analytics.spec.js @@ -297,6 +297,7 @@ describe('Viewing application', () => { }); cy.get('[data-test-subj="euiFlyoutCloseButton"]').click(); cy.get('[data-test-subj="traceDetailFlyout"]').should('not.exist'); + cy.get('[data-test-subj="superDatePickerShowDatesButton"]').click();//added to replace wait cy.get('[title="03f9c770db5ee2f1caac0afc36db49ba"]').click(); cy.get('[data-text="Span list"]').click(); cy.get('[data-test-subj="dataGridRowCell"]').contains('d67c5bb617ba9203').click(); From b5e435108e8a7741fe541dc2b0da76d619b177e2 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Fri, 6 Dec 2024 09:53:46 -0800 Subject: [PATCH 11/21] make service map snapshop deep Signed-off-by: Adam Tackett --- .../__snapshots__/service_map.test.tsx.snap | 2214 +++-------------- .../plots/__tests__/service_map.test.tsx | 14 +- 2 files changed, 305 insertions(+), 1923 deletions(-) diff --git a/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap b/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap index bd5890b96b..57ee5253fd 100644 --- a/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap +++ b/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap @@ -1,1981 +1,353 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`ServiceMap Component renders service map component 1`] = ` - - +Array [ +
+
+ + Service map + +
+
- + Select metric for service map display + +
- -
- Service map + + Duration -
-
- - -
- - + +
+
+
+
+
+
- - - - - - -
+
- - - +
+
+
+
+
-
- -
- -
- - - } - aria-controls="service-select-dropdown" - compressed={true} - disabled={false} - fullWidth={false} - incremental={false} - isClearable={true} - isInvalid={false} - isLoading={false} - onChange={[Function]} - onClick={[Function]} - placeholder="Service name" - prepend="Focus on" - value="" - /> - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - id="service-select-dropdown" - isOpen={false} - ownFocus={false} - panelPaddingSize="none" - repositionOnScroll={true} - > - -
-
- - - - } - aria-controls="service-select-dropdown" - compressed={true} - disabled={false} - fullWidth={false} - incremental={false} - isClearable={true} - isInvalid={false} - isLoading={false} - onBlur={[Function]} - onChange={[Function]} - onClick={[Function]} - onFocus={[Function]} - placeholder="Service name" - prepend="Focus on" - value="" - > - - } - compressed={true} - fullWidth={false} - icon="search" - isLoading={false} - prepend="Focus on" - > -
- - - -
- - - - -
- - - - - -
-
-
- - - -
-
-
-
-
-
-
-
-
-
-
+ Mock Graph +
+
+ +
- - -
- - +
- +
+
+
-
- -
- Mock Graph -
-
-
- - - -
-
+ />
- -
- -
- - -
- - - -
- - -
- -
- unmatched-node-legend -
-
- -
- -
- No match -
-
-
-
-
-
-
- + No match +
- +
- +
- - -
- - +
, +
, +] `; diff --git a/public/components/trace_analytics/components/common/plots/__tests__/service_map.test.tsx b/public/components/trace_analytics/components/common/plots/__tests__/service_map.test.tsx index 60fce85488..9b77009b29 100644 --- a/public/components/trace_analytics/components/common/plots/__tests__/service_map.test.tsx +++ b/public/components/trace_analytics/components/common/plots/__tests__/service_map.test.tsx @@ -12,6 +12,7 @@ import { ServiceMap } from '../service_map'; import { EuiFieldSearch, EuiSelectable } from '@elastic/eui'; import { TEST_SERVICE_MAP, MOCK_CANVAS_CONTEXT } from '../../../../../../../test/constants'; import Graph from 'react-graph-vis'; +import toJson from 'enzyme-to-json'; configure({ adapter: new Adapter() }); @@ -98,9 +99,18 @@ describe('ServiceMap Component', () => { jest.clearAllMocks(); }); - it('renders service map component', () => { + it('renders service map component', async () => { const wrapper = mount(); - expect(wrapper).toMatchSnapshot(); + wrapper.update(); + + await act(async () => { + expect( + toJson(wrapper, { + noKey: false, + mode: 'deep', + }) + ).toMatchSnapshot(); + }); }); it('renders application composition map title when page is app', () => { From 4781348fdb2a18152ba7958a998b180a5c79087c Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Fri, 6 Dec 2024 10:39:34 -0800 Subject: [PATCH 12/21] add service map focus cypress testing Signed-off-by: Adam Tackett --- .../trace_analytics_services.spec.js | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js index 95ef28e57b..35663f35f5 100644 --- a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js +++ b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js @@ -171,7 +171,35 @@ describe('Testing Service map', () => { cy.get('[data-text = "Duration"]').click(); cy.contains('100'); cy.get('.euiFormLabel.euiFormControlLayout__prepend').contains('Focus on').should('exist'); - cy.get('[placeholder="Service name"]').focus().type('database{enter}'); + }); + + it('Tests focus functionality in Service map', () => { + cy.get('.euiText.euiText--medium .panel-title').contains('Service map'); + cy.get('[data-test-subj="latency"]').should('exist'); + cy.get('.ytitle').contains('Average duration (ms)'); + + // Test metric selection functionality + cy.get('[data-text="Errors"]').click(); + cy.contains('60%'); + cy.get('[data-text="Duration"]').click(); + cy.contains('100'); + + // Focus on "order" by selecting the first option + cy.get('.euiFormLabel.euiFormControlLayout__prepend').contains('Focus on').should('exist'); + cy.get('[placeholder="Service name"]').click(); + cy.get('.euiSelectableList__list li').eq(0).click(); + + // Verify the service map updates and focus is applied + cy.get('.euiFormLabel.euiFormControlLayout__prepend').contains('Focus on').should('exist'); + cy.get('[placeholder="order"]').click(); + cy.get('.euiSelectableList__list li').should('have.length', 4); // Focused view with 4 options + + // Refresh to reset the focus + cy.get('[data-test-subj="serviceMapRefreshButton"]').click(); + + // Verify the service map is reset to the original state + cy.get('[placeholder="Service name"]').should('have.value', ''); + cy.get('.euiSelectableList__list li').should('have.length', 8); // Original 8 options }); }); From 9ffec7991e1254d882fea29435b140c6a3192b93 Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Fri, 6 Dec 2024 17:40:15 -0800 Subject: [PATCH 13/21] add more tests to cover service map load and click Signed-off-by: Shenoy Pratik --- .../trace_analytics_services.spec.js | 28 ++++++++++++++++++- .../components/common/plots/service_map.tsx | 10 +++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js index 35663f35f5..c973637805 100644 --- a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js +++ b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js @@ -150,7 +150,7 @@ describe('Testing service view', () => { }); }); -describe('Testing Service map', () => { +describe.only('Testing Service map', () => { beforeEach(() => { cy.visit('app/observability-traces#/services', { onBeforeLoad: (win) => { @@ -173,6 +173,32 @@ describe('Testing Service map', () => { cy.get('.euiFormLabel.euiFormControlLayout__prepend').contains('Focus on').should('exist'); }); + it('Render the vis-network div and canvas', () => { + // Visit the page where your ServiceMap component is rendered + cy.get('.euiText.euiText--medium .panel-title').contains('Service map'); + cy.get('.vis-network').should('exist'); + cy.get('.vis-network canvas').should('exist'); + + cy.get('.vis-network canvas') + .should('have.attr', 'style') + .and('include', 'position: relative') + .and('include', 'touch-action: none') + .and('include', 'user-select: none') + .and('include', 'width: 100%') + .and('include', 'height: 100%'); + + cy.get('.vis-network canvas').should('have.attr', 'width').and('not.eq', '0'); + cy.get('.vis-network canvas').should('have.attr', 'height').and('not.eq', '0'); + }); + + it.only('Click on a node to see the details', () => { + cy.get('.euiText.euiText--medium .panel-title').contains('Service map'); + cy.get('.vis-network canvas').should('exist'); + + cy.get('.vis-network canvas').click(707, 388); // clicks on payment node + cy.get('.euiText.euiText--small').contains('Average duration: 216.43ms').should('exist'); + }); + it('Tests focus functionality in Service map', () => { cy.get('.euiText.euiText--medium .panel-title').contains('Service map'); cy.get('[data-test-subj="latency"]').should('exist'); diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index 6832af4b8d..929e78fce8 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -6,17 +6,17 @@ import { EuiButtonGroup, EuiButtonIcon, + EuiFieldSearch, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, + EuiLoadingSpinner, EuiPanel, + EuiPopover, + EuiSelectable, EuiSpacer, EuiSuperSelect, EuiSuperSelectOption, - EuiSelectable, - EuiPopover, - EuiFieldSearch, - EuiLoadingSpinner, EuiToolTip, } from '@elastic/eui'; import React, { useEffect, useMemo, useState } from 'react'; @@ -538,7 +538,7 @@ export function ServiceMap({ display: 'flex', alignItems: 'center', justifyContent: 'center', - backgroundColor: 'rgba(255, 255, 255, 0.8)', + backgroundColor: 'transparent', // supports both dark and light themes zIndex: 1000, }} > From 5faeeb63ce34c24fc085ddf2d5b89e34b5fa6ee8 Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Fri, 6 Dec 2024 17:43:19 -0800 Subject: [PATCH 14/21] add comments to cypress Signed-off-by: Shenoy Pratik --- .../trace_analytics_test/trace_analytics_services.spec.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js index c973637805..ecfaf047e1 100644 --- a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js +++ b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js @@ -174,11 +174,12 @@ describe.only('Testing Service map', () => { }); it('Render the vis-network div and canvas', () => { - // Visit the page where your ServiceMap component is rendered + // Check the view where ServiceMap component is rendered cy.get('.euiText.euiText--medium .panel-title').contains('Service map'); cy.get('.vis-network').should('exist'); cy.get('.vis-network canvas').should('exist'); + // Check the canvas is not empty cy.get('.vis-network canvas') .should('have.attr', 'style') .and('include', 'position: relative') @@ -195,7 +196,9 @@ describe.only('Testing Service map', () => { cy.get('.euiText.euiText--medium .panel-title').contains('Service map'); cy.get('.vis-network canvas').should('exist'); - cy.get('.vis-network canvas').click(707, 388); // clicks on payment node + // clicks on payment node + cy.get('.vis-network canvas').click(707, 388); + // checks the duration in node details popover cy.get('.euiText.euiText--small').contains('Average duration: 216.43ms').should('exist'); }); From 747b1b6d4d997949ade5cdd1e9fc4511f72b33ed Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Fri, 6 Dec 2024 17:45:21 -0800 Subject: [PATCH 15/21] remove only from cypress tests Signed-off-by: Shenoy Pratik --- .../trace_analytics_test/trace_analytics_services.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js index ecfaf047e1..f601cae9e0 100644 --- a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js +++ b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js @@ -150,7 +150,7 @@ describe('Testing service view', () => { }); }); -describe.only('Testing Service map', () => { +describe('Testing Service map', () => { beforeEach(() => { cy.visit('app/observability-traces#/services', { onBeforeLoad: (win) => { @@ -192,7 +192,7 @@ describe.only('Testing Service map', () => { cy.get('.vis-network canvas').should('have.attr', 'height').and('not.eq', '0'); }); - it.only('Click on a node to see the details', () => { + it('Click on a node to see the details', () => { cy.get('.euiText.euiText--medium .panel-title').contains('Service map'); cy.get('.vis-network canvas').should('exist'); From 8b2af4f5b964eb6141d743d0a08e8c9c28c42316 Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Fri, 6 Dec 2024 18:40:59 -0800 Subject: [PATCH 16/21] update snapshots Signed-off-by: Shenoy Pratik --- .../plots/__tests__/__snapshots__/service_map.test.tsx.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap b/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap index 57ee5253fd..77d18ceeaf 100644 --- a/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap +++ b/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap @@ -260,7 +260,7 @@ Array [ style={ Object { "alignItems": "center", - "backgroundColor": "rgba(255, 255, 255, 0.8)", + "backgroundColor": "transparent", "bottom": 0, "display": "flex", "justifyContent": "center", From b2a2f1c9a7343914c051ead7294800c886cda7dd Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Sat, 7 Dec 2024 12:33:49 -0800 Subject: [PATCH 17/21] adjust cypress test Signed-off-by: Adam Tackett --- .../trace_analytics_services.spec.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js index f601cae9e0..3697880686 100644 --- a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js +++ b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js @@ -149,7 +149,7 @@ describe('Testing service view', () => { cy.contains('Spans (1)').should('exist'); }); }); - +//adam describe('Testing Service map', () => { beforeEach(() => { cy.visit('app/observability-traces#/services', { @@ -160,6 +160,7 @@ describe('Testing Service map', () => { cy.get("[data-test-subj='indexPattern-switch-link']").click(); cy.get("[data-test-subj='data_prepper-mode']").click(); setTimeFilter(); + cy.get('.euiTableRow').should('have.length.greaterThan', 7); //Replaces wait }); it('Render Service map', () => { @@ -196,6 +197,12 @@ describe('Testing Service map', () => { cy.get('.euiText.euiText--medium .panel-title').contains('Service map'); cy.get('.vis-network canvas').should('exist'); + // ensure rendering is complete before node click, replace wait + cy.get('[data-text="Errors"]').click(); + cy.contains('60%'); + cy.get('[data-text="Duration"]').click(); + cy.contains('100'); + // clicks on payment node cy.get('.vis-network canvas').click(707, 388); // checks the duration in node details popover From 77bc6f4eb6bd19cc36d0154f6a4d2d9ade5926f3 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Sat, 7 Dec 2024 14:20:01 -0800 Subject: [PATCH 18/21] adjust cypress testw Signed-off-by: Adam Tackett --- .../trace_analytics_services.spec.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js index 3697880686..613bc5b422 100644 --- a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js +++ b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js @@ -193,20 +193,25 @@ describe('Testing Service map', () => { cy.get('.vis-network canvas').should('have.attr', 'height').and('not.eq', '0'); }); - it('Click on a node to see the details', () => { + it.only('Click on a node to see the details', () => { cy.get('.euiText.euiText--medium .panel-title').contains('Service map'); cy.get('.vis-network canvas').should('exist'); + // Focus on "order" by selecting the first option + cy.get('.euiFormLabel.euiFormControlLayout__prepend').contains('Focus on').should('exist'); + cy.get('[placeholder="Service name"]').click(); + cy.get('.euiSelectableList__list li').eq(0).click(); + // ensure rendering is complete before node click, replace wait cy.get('[data-text="Errors"]').click(); cy.contains('60%'); cy.get('[data-text="Duration"]').click(); cy.contains('100'); - // clicks on payment node - cy.get('.vis-network canvas').click(707, 388); + // clicks on database node + cy.get('.vis-network canvas').click(727, 388); // checks the duration in node details popover - cy.get('.euiText.euiText--small').contains('Average duration: 216.43ms').should('exist'); + cy.get('.euiText.euiText--small').contains('Average duration: 45.84ms').should('exist'); }); it('Tests focus functionality in Service map', () => { From 3bb65ecd08c1c1fd4e6ac8ee1d0bf07426b451bf Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Sat, 7 Dec 2024 16:20:17 -0800 Subject: [PATCH 19/21] remove only and comment Signed-off-by: Adam Tackett --- .../trace_analytics_test/trace_analytics_services.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js index 613bc5b422..a14844ec24 100644 --- a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js +++ b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js @@ -149,7 +149,7 @@ describe('Testing service view', () => { cy.contains('Spans (1)').should('exist'); }); }); -//adam + describe('Testing Service map', () => { beforeEach(() => { cy.visit('app/observability-traces#/services', { @@ -193,7 +193,7 @@ describe('Testing Service map', () => { cy.get('.vis-network canvas').should('have.attr', 'height').and('not.eq', '0'); }); - it.only('Click on a node to see the details', () => { + it('Click on a node to see the details', () => { cy.get('.euiText.euiText--medium .panel-title').contains('Service map'); cy.get('.vis-network canvas').should('exist'); From 87017b1dcda3ce12c71011b87e61743bab5f35fc Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Mon, 9 Dec 2024 09:39:21 -0800 Subject: [PATCH 20/21] switch test back Signed-off-by: Adam Tackett --- .../trace_analytics_services.spec.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js index a14844ec24..8f3121f8ae 100644 --- a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js +++ b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js @@ -197,21 +197,16 @@ describe('Testing Service map', () => { cy.get('.euiText.euiText--medium .panel-title').contains('Service map'); cy.get('.vis-network canvas').should('exist'); - // Focus on "order" by selecting the first option - cy.get('.euiFormLabel.euiFormControlLayout__prepend').contains('Focus on').should('exist'); - cy.get('[placeholder="Service name"]').click(); - cy.get('.euiSelectableList__list li').eq(0).click(); - // ensure rendering is complete before node click, replace wait cy.get('[data-text="Errors"]').click(); cy.contains('60%'); cy.get('[data-text="Duration"]').click(); cy.contains('100'); - // clicks on database node - cy.get('.vis-network canvas').click(727, 388); + // clicks on payment node + cy.get('.vis-network canvas').click(707, 388); // checks the duration in node details popover - cy.get('.euiText.euiText--small').contains('Average duration: 45.84ms').should('exist'); + cy.get('.euiText.euiText--small').contains('Average duration: 216.43ms').should('exist'); }); it('Tests focus functionality in Service map', () => { From c430f5cfdd176b0b3190a0be40d7c68cfa516dce Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Tue, 10 Dec 2024 09:48:27 -0800 Subject: [PATCH 21/21] add dependancy to use effect Signed-off-by: Adam Tackett --- .../trace_analytics/components/common/plots/service_map.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index 929e78fce8..ae53fb5f85 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -376,7 +376,7 @@ export function ServiceMap({ showRelatedServices ) ); - }, [serviceMap, idSelected]); + }, [serviceMap, idSelected, focusedService, filterByCurrService]); return ( <> @@ -440,7 +440,9 @@ export function ServiceMap({ aria-label="Clear focus and refresh the service map" size="s" onClick={() => { - setGraphKey((prevKey) => prevKey + 1); + if (!isFocusBarDisabled) { + setGraphKey((prevKey) => prevKey + 1); + } setQuery(''); onFocus(''); }}