This is an automated email from the ASF dual-hosted git repository.

andytaylor pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/artemis-console.git


The following commit(s) were added to refs/heads/main by this push:
     new 7245f8e  ARTEMIS-5853 - Fix Operations dialog auto-collapse on refresh
7245f8e is described below

commit 7245f8e205f1fb90f886982eb23001c4d476997c
Author: Domenico Francesco Bruscino <[email protected]>
AuthorDate: Mon Mar 23 19:13:41 2026 +0100

    ARTEMIS-5853 - Fix Operations dialog auto-collapse on refresh
    
    Pause auto-refresh when Operations dialog is open to prevent expanded
    operations from collapsing every 5 seconds during the default refresh cycle.
    
    Generated-by: Claude Sonnet 4.5
---
 .../src/status/Status.test.tsx                     | 250 +++++++++++++++++++++
 .../artemis-console-plugin/src/status/Status.tsx   |  13 +-
 2 files changed, 258 insertions(+), 5 deletions(-)

diff --git 
a/artemis-console-extension/artemis-extension/packages/artemis-console-plugin/src/status/Status.test.tsx
 
b/artemis-console-extension/artemis-extension/packages/artemis-console-plugin/src/status/Status.test.tsx
new file mode 100644
index 0000000..135b99b
--- /dev/null
+++ 
b/artemis-console-extension/artemis-extension/packages/artemis-console-plugin/src/status/Status.test.tsx
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { render, screen, waitFor, fireEvent, within } from 
'@testing-library/react'
+import { Status } from './Status'
+import { artemisService, BrokerInfo, BrokerState, BrokerNetworkTopology } from 
'../artemis-service'
+import { jolokiaService, MBeanTree } from '@hawtio/react'
+import { ArtemisContext } from '../context'
+import { artemisPluginName } from '../globals'
+
+jest.mock('../artemis-service')
+jest.mock('@hawtio/react', () => ({
+  ...jest.requireActual('@hawtio/react'),
+  jolokiaService: {
+    loadUpdateRate: jest.fn(),
+    errorMessage: jest.fn((error) => error.message || 'Error')
+  },
+  eventService: {
+    notify: jest.fn()
+  }
+}))
+
+const mockBrokerInfo: BrokerInfo = {
+  name: '127.0.0.1',
+  nodeID: '0',
+  objectName: 'org.apache.activemq.artemis:broker=127.0.0.1',
+  version: '2.50.0',
+  uptime: '1 day',
+  started: 'true',
+  haPolicy: 'Primary',
+  globalMaxSizeMB: 1024,
+  addressMemoryUsed: 25.5,
+  addressMemoryUsage: 256,
+  networkTopology: new BrokerNetworkTopology([])
+}
+
+const mockBrokerState: BrokerState = {
+  loaded: true,
+  accessible: true,
+  message: 'Success'
+}
+
+const renderWithContext = () => {
+  const contextValue = {
+    tree: MBeanTree.createEmpty(artemisPluginName),
+    selectedNode: null,
+    brokerNode: undefined,
+    setSelectedNode: jest.fn(),
+    findAndSelectNode: jest.fn()
+  }
+
+  return render(
+    <ArtemisContext.Provider value={contextValue}>
+      <Status />
+    </ArtemisContext.Provider>
+  )
+}
+
+describe('Status', () => {
+  beforeEach(() => {
+    jest.clearAllMocks()
+    jest.useFakeTimers()
+
+    ;(jolokiaService.loadUpdateRate as jest.Mock).mockReturnValue(5000)
+    ;(artemisService.getBrokerInfo as jest.Mock).mockResolvedValue({
+      info: mockBrokerInfo,
+      ...mockBrokerState
+    })
+    ;(artemisService.createAcceptors as jest.Mock).mockResolvedValue({ 
acceptors: [] })
+    ;(artemisService.createClusterConnections as 
jest.Mock).mockResolvedValue({ clusterConnections: [] })
+    ;(artemisService.getBrokerObjectName as 
jest.Mock).mockResolvedValue('org.apache.activemq.artemis:broker=127.0.0.1')
+  })
+
+  afterEach(() => {
+    jest.runOnlyPendingTimers()
+    jest.useRealTimers()
+  })
+
+  test('sets up auto-refresh on initial render', async () => {
+    renderWithContext()
+
+    await waitFor(() => {
+      expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(1)
+    })
+
+    jest.advanceTimersByTime(5000)
+
+    await waitFor(() => {
+      expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(2)
+    })
+
+    jest.advanceTimersByTime(5000)
+
+    await waitFor(() => {
+      expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(3)
+    })
+  })
+
+  test('pauses auto-refresh when Operations dialog is opened', async () => {
+    renderWithContext()
+
+    await waitFor(() => {
+      expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(1)
+    })
+
+    await waitFor(() => {
+      expect(screen.getByText('Broker Info')).toBeInTheDocument()
+    })
+
+    jest.advanceTimersByTime(5000)
+
+    await waitFor(() => {
+      expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(2)
+    })
+
+    const brokerInfoCard = screen.getByText('Broker 
Info').closest('.pf-v5-c-card') as HTMLElement
+    const menuToggle = within(brokerInfoCard).getByRole('button', { expanded: 
false })
+    fireEvent.click(menuToggle)
+
+    const operationsButton = await screen.findByText('Operations')
+    fireEvent.click(operationsButton)
+
+    await waitFor(() => {
+      expect(screen.getByLabelText('operations-modal')).toBeInTheDocument()
+    })
+
+    const callCountBeforePause = (artemisService.getBrokerInfo as 
jest.Mock).mock.calls.length
+
+    jest.advanceTimersByTime(5000)
+    jest.advanceTimersByTime(5000)
+    jest.advanceTimersByTime(5000)
+
+    await waitFor(() => {
+      
expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(callCountBeforePause)
+    })
+  })
+
+  test('resumes auto-refresh when Operations dialog is closed', async () => {
+    renderWithContext()
+
+    await waitFor(() => {
+      expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(1)
+    })
+
+    await waitFor(() => {
+      expect(screen.getByText('Broker Info')).toBeInTheDocument()
+    })
+
+    const brokerInfoCard = screen.getByText('Broker 
Info').closest('.pf-v5-c-card') as HTMLElement
+    const menuToggle = within(brokerInfoCard).getByRole('button', { expanded: 
false })
+    fireEvent.click(menuToggle)
+
+    const operationsButton = await screen.findByText('Operations')
+    fireEvent.click(operationsButton)
+
+    await waitFor(() => {
+      expect(screen.getByLabelText('operations-modal')).toBeInTheDocument()
+    })
+
+    const callCountWhenDialogOpened = (artemisService.getBrokerInfo as 
jest.Mock).mock.calls.length
+
+    jest.advanceTimersByTime(10000)
+
+    await waitFor(() => {
+      
expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(callCountWhenDialogOpened)
+    })
+
+    const closeButtons = screen.getAllByRole('button', { name: 'Close' })
+    const primaryCloseButton = closeButtons.find(btn => 
btn.classList.contains('pf-m-primary'))
+    fireEvent.click(primaryCloseButton!)
+
+    await waitFor(() => {
+      
expect(screen.queryByLabelText('operations-modal')).not.toBeInTheDocument()
+    })
+
+    const callCountAfterClose = (artemisService.getBrokerInfo as 
jest.Mock).mock.calls.length
+
+    jest.advanceTimersByTime(5000)
+
+    await waitFor(() => {
+      
expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(callCountAfterClose 
+ 1)
+    })
+  })
+
+  test('allows Attributes dialog to receive updates during auto-refresh', 
async () => {
+    renderWithContext()
+
+    await waitFor(() => {
+      expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(1)
+    })
+
+    await waitFor(() => {
+      expect(screen.getByText('Broker Info')).toBeInTheDocument()
+    })
+
+    const brokerInfoCard = screen.getByText('Broker 
Info').closest('.pf-v5-c-card') as HTMLElement
+    const menuToggle = within(brokerInfoCard).getByRole('button', { expanded: 
false })
+    fireEvent.click(menuToggle)
+
+    const attributesButton = await screen.findByText('Attributes')
+    fireEvent.click(attributesButton)
+
+    await waitFor(() => {
+      expect(screen.getByLabelText('attributes-modal')).toBeInTheDocument()
+    })
+
+    const callCountBeforeInterval = (artemisService.getBrokerInfo as 
jest.Mock).mock.calls.length
+
+    jest.advanceTimersByTime(5000)
+
+    await waitFor(() => {
+      
expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(callCountBeforeInterval
 + 1)
+    })
+  })
+
+  test('cleans up interval on unmount', async () => {
+    const { unmount } = render(<Status />)
+
+    await waitFor(() => {
+      expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(1)
+    })
+
+    jest.advanceTimersByTime(5000)
+
+    await waitFor(() => {
+      expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(2)
+    })
+
+    const callCountBeforeUnmount = (artemisService.getBrokerInfo as 
jest.Mock).mock.calls.length
+
+    unmount()
+
+    jest.advanceTimersByTime(10000)
+
+    
expect(artemisService.getBrokerInfo).toHaveBeenCalledTimes(callCountBeforeUnmount)
+  })
+})
diff --git 
a/artemis-console-extension/artemis-extension/packages/artemis-console-plugin/src/status/Status.tsx
 
b/artemis-console-extension/artemis-extension/packages/artemis-console-plugin/src/status/Status.tsx
index f4d8965..7d77844 100644
--- 
a/artemis-console-extension/artemis-extension/packages/artemis-console-plugin/src/status/Status.tsx
+++ 
b/artemis-console-extension/artemis-extension/packages/artemis-console-plugin/src/status/Status.tsx
@@ -99,16 +99,19 @@ export const Status: React.FunctionComponent = () => {
     useEffect(() => {
         // run only once at the beginning
         getBrokerInfo();
- 
+
         getAcceptors();
 
         getClusterConnections();
 
-        const refreshRate = jolokiaService.loadUpdateRate()
-        const timer = setInterval(getBrokerInfo, refreshRate ?? 5000)
-        return () => clearInterval(timer)
+        // Pause auto-refresh when Operations dialog is open to prevent 
collapsing expanded operations
+        if (!showOperationsDialog) {
+            const refreshRate = jolokiaService.loadUpdateRate()
+            const timer = setInterval(getBrokerInfo, refreshRate ?? 5000)
+            return () => clearInterval(timer)
+        }
 
-    }, [])
+    }, [showOperationsDialog])
 
     useEffect(() => {
     // update frontend when brokerInfo, acceptors, or clusterConnections change


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to