From 6fb253c3b3bdd1dc3388db535e5a0ef840cd834b Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Fri, 29 Nov 2024 08:49:09 +0100 Subject: [PATCH 1/2] PMM-13548 Use FinalForm to store selected node & preselect pmm-server --- .../NodesAgents/NodesAgents.constants.tsx | 1 + .../NodesAgents/NodesAgents.test.tsx | 10 ++++++++ .../FormParts/NodesAgents/NodesAgents.tsx | 24 +++++++++++++++---- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.constants.tsx b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.constants.tsx index 262ceefc161b7..bc62f0170f771 100644 --- a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.constants.tsx +++ b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.constants.tsx @@ -1 +1,2 @@ +export const PMM_SERVER_NODE_ID = 'pmm-server'; export const PMM_SERVER_NODE_AGENT_ID = 'pmm-server'; diff --git a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.test.tsx b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.test.tsx index 6e0ace17322a8..05345b66a8084 100644 --- a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.test.tsx +++ b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.test.tsx @@ -46,6 +46,16 @@ describe('Nodes Agents:: ', () => { submitMock.mockClear(); }); + it('should pick pmm-server node by default when available', async () => { + jest.spyOn(InventoryService, 'getNodes').mockReturnValue(Promise.resolve({ nodes: nodesMock })); + + setup(); + + await waitFor(() => expect(fetchNodesActionActionSpy).toHaveBeenCalled()); + + await waitFor(() => expect(screen.getByTestId('node')).toHaveTextContent(nodesMock[0].node_id)); + }); + it('should not pick any agent when the selected node is not pmm-server', async () => { jest .spyOn(InventoryService, 'getNodes') diff --git a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.tsx b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.tsx index e63447f677744..02bec8011d9c9 100644 --- a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.tsx +++ b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.tsx @@ -1,9 +1,13 @@ -import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { FC, useCallback, useEffect, useMemo } from 'react'; +import { useField } from 'react-final-form'; import { useStyles2 } from '@grafana/ui'; import { Messages } from 'app/percona/add-instance/components/AddRemoteInstance/FormParts/FormParts.messages'; import { getStyles } from 'app/percona/add-instance/components/AddRemoteInstance/FormParts/FormParts.styles'; -import { PMM_SERVER_NODE_AGENT_ID } from 'app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.constants'; +import { + PMM_SERVER_NODE_AGENT_ID, + PMM_SERVER_NODE_ID, +} from 'app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.constants'; import { NodesAgentsProps } from 'app/percona/add-instance/components/AddRemoteInstance/FormParts/NodesAgents/NodesAgents.types'; import { GET_NODES_CANCEL_TOKEN } from 'app/percona/inventory/Inventory.constants'; import { AgentsOption, NodesOption } from 'app/percona/inventory/Inventory.types'; @@ -14,6 +18,7 @@ import { fetchNodesAction } from 'app/percona/shared/core/reducers/nodes/nodes'; import { getNodes } from 'app/percona/shared/core/selectors'; import { isApiCancelError } from 'app/percona/shared/helpers/api'; import { logger } from 'app/percona/shared/helpers/logger'; +import { validators } from 'app/percona/shared/helpers/validatorsForm'; import { useAppDispatch } from 'app/store/store'; import { useSelector } from 'app/types'; @@ -21,8 +26,10 @@ export const NodesAgents: FC = ({ form }) => { const styles = useStyles2(getStyles); const dispatch = useAppDispatch(); const [generateToken] = useCancelToken(); - const [selectedNode, setSelectedNode] = useState(); const { nodes } = useSelector(getNodes); + const { + input: { value: selectedNode }, + } = useField('node'); const nodesOptions = useMemo(() => nodesOptionsMapper(nodes), [nodes]); @@ -49,7 +56,7 @@ export const NodesAgents: FC = ({ form }) => { }; const setNodeAndAgent = (value: NodesOption) => { - setSelectedNode(value); + form?.change('node', value); let selectedAgent: AgentsOption | undefined; if (value.agents && value.agents?.length > 1) { @@ -68,6 +75,12 @@ export const NodesAgents: FC = ({ form }) => { useEffect(() => { if (nodesOptions.length === 0) { loadData(); + } else if (!selectedNode) { + // preselect pmm-server node + const pmmServerNode = nodesOptions.find((node) => node.value === PMM_SERVER_NODE_ID); + if (pmmServerNode) { + setNodeAndAgent(pmmServerNode); + } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [nodesOptions]); @@ -84,8 +97,8 @@ export const NodesAgents: FC = ({ form }) => { data-testid="nodes-selectbox" onChange={(event) => setNodeAndAgent(event as NodesOption)} className={styles.selectField} - value={selectedNode} aria-label={Messages.form.labels.nodesAgents.nodes} + validators={[validators.required]} />
@@ -99,6 +112,7 @@ export const NodesAgents: FC = ({ form }) => { onChange={(event) => changeAgentValue(event as AgentsOption)} className={styles.selectField} aria-label={Messages.form.labels.nodesAgents.agents} + validators={selectedNode ? [validators.required] : undefined} />
From f751990b99f66095a27e9d16d59528e879924597 Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Fri, 29 Nov 2024 08:49:35 +0100 Subject: [PATCH 2/2] PMM-13548 Fix warnings in unit tests related to nodes fetching --- .../AddRemoteInstance/FormParts/FormParts.test.tsx | 8 +++++--- .../MongoDBConnectionDetails.test.tsx | 8 +++++--- .../MySQLConnectionDetails.test.tsx | 8 +++++--- .../PostgreSQLConnectionDetails.test.tsx | 12 +++++++----- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/FormParts.test.tsx b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/FormParts.test.tsx index f7050cf756c07..129effdf898a1 100644 --- a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/FormParts.test.tsx +++ b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/FormParts.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import { FormApi, FormState } from 'final-form'; import React from 'react'; import { Form } from 'react-final-form'; @@ -13,6 +13,8 @@ import { trackingOptions, rdsTrackingOptions } from './FormParts.constants'; import { LabelsFormPart } from './Labels/Labels'; import { MainDetailsFormPart } from './MainDetails/MainDetails'; +jest.mock('app/percona/inventory/Inventory.service'); + const form: Partial = { change: jest.fn(), /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -31,7 +33,7 @@ describe('MainDetailsFormPart ::', () => { ); const fields = container.querySelectorAll('input'); - expect(fields.length).toBe(8); + await waitFor(() => expect(fields.length).toBe(8)); expect(screen.getByTestId('address-text-input')).toBeDisabled(); expect(screen.getByTestId('serviceName-text-input')).not.toBeDisabled(); @@ -51,7 +53,7 @@ describe('MainDetailsFormPart ::', () => { ); const fields = container.querySelectorAll('input'); - expect(fields.length).toBe(8); + await waitFor(() => expect(fields.length).toBe(8)); expect(screen.getByTestId('address-text-input')).not.toBeDisabled(); expect(screen.getByTestId('serviceName-text-input')).not.toBeDisabled(); diff --git a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/MongoDBConnectionDetails/MongoDBConnectionDetails.test.tsx b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/MongoDBConnectionDetails/MongoDBConnectionDetails.test.tsx index 8057d857b0813..6839e2b38ac5b 100644 --- a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/MongoDBConnectionDetails/MongoDBConnectionDetails.test.tsx +++ b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/MongoDBConnectionDetails/MongoDBConnectionDetails.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, fireEvent } from '@testing-library/react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import React from 'react'; import { Form } from 'react-final-form'; import { Provider } from 'react-redux'; @@ -7,8 +7,10 @@ import { configureStore } from 'app/store/configureStore'; import { MongoDBConnectionDetails } from './MongoDBConnectionDetails'; +jest.mock('app/percona/inventory/Inventory.service'); + describe('MongoDB connection details:: ', () => { - it('should have max query length attribute', () => { + it('should have max query length attribute', async () => { render(
} /> @@ -18,6 +20,6 @@ describe('MongoDB connection details:: ', () => { const textInput = screen.getByTestId('maxQueryLength-text-input'); fireEvent.change(textInput, { target: { value: '1000' } }); - expect(screen.getByTestId('maxQueryLength-text-input')).toHaveValue('1000'); + await waitFor(() => expect(screen.getByTestId('maxQueryLength-text-input')).toHaveValue('1000')); }); }); diff --git a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/MySQLConnectionDetails/MySQLConnectionDetails.test.tsx b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/MySQLConnectionDetails/MySQLConnectionDetails.test.tsx index 902f9960fe5e6..23d57c20ef92e 100644 --- a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/MySQLConnectionDetails/MySQLConnectionDetails.test.tsx +++ b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/MySQLConnectionDetails/MySQLConnectionDetails.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, fireEvent } from '@testing-library/react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import React from 'react'; import { Form } from 'react-final-form'; import { Provider } from 'react-redux'; @@ -7,8 +7,10 @@ import { configureStore } from 'app/store/configureStore'; import { MySQLConnectionDetails } from './MySQLConnectionDetails'; +jest.mock('app/percona/inventory/Inventory.service'); + describe('MySQL connection details:: ', () => { - it('should have max query length attribute', () => { + it('should have max query length attribute', async () => { render( } /> @@ -18,6 +20,6 @@ describe('MySQL connection details:: ', () => { const textInput = screen.getByTestId('maxQueryLength-text-input'); fireEvent.change(textInput, { target: { value: '1000' } }); - expect(screen.getByTestId('maxQueryLength-text-input')).toHaveValue('1000'); + await waitFor(() => expect(screen.getByTestId('maxQueryLength-text-input')).toHaveValue('1000')); }); }); diff --git a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/PostgreSQLConnectionDetails/PostgreSQLConnectionDetails.test.tsx b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/PostgreSQLConnectionDetails/PostgreSQLConnectionDetails.test.tsx index 4b817753588a9..1b0115c062af3 100644 --- a/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/PostgreSQLConnectionDetails/PostgreSQLConnectionDetails.test.tsx +++ b/public/app/percona/add-instance/components/AddRemoteInstance/FormParts/PostgreSQLConnectionDetails/PostgreSQLConnectionDetails.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, fireEvent } from '@testing-library/react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import React from 'react'; import { Form } from 'react-final-form'; import { Provider } from 'react-redux'; @@ -7,8 +7,10 @@ import { configureStore } from 'app/store/configureStore'; import { PostgreSQLConnectionDetails } from './PostgreSQLConnectionDetails'; +jest.mock('app/percona/inventory/Inventory.service'); + describe('PostgreSQL connection details:: ', () => { - it('should have database attribute', () => { + it('should have database attribute', async () => { render( } /> @@ -18,10 +20,10 @@ describe('PostgreSQL connection details:: ', () => { const textInput = screen.getByTestId('database-text-input'); fireEvent.change(textInput, { target: { value: 'db1' } }); - expect(screen.getByTestId('database-text-input')).toHaveValue('db1'); + await waitFor(() => expect(screen.getByTestId('database-text-input')).toHaveValue('db1')); }); - it('should have max query length attribute', () => { + it('should have max query length attribute', async () => { render( } /> @@ -31,6 +33,6 @@ describe('PostgreSQL connection details:: ', () => { const textInput = screen.getByTestId('maxQueryLength-text-input'); fireEvent.change(textInput, { target: { value: '1000' } }); - expect(screen.getByTestId('maxQueryLength-text-input')).toHaveValue('1000'); + await waitFor(() => expect(screen.getByTestId('maxQueryLength-text-input')).toHaveValue('1000')); }); });