From ac0554fea1a8550636351f0fbac918a7c4242b30 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 1 Nov 2024 15:51:31 -0700 Subject: [PATCH 01/10] add testing library eslint --- .eslintrc.js | 15 +++++++++++++++ package.json | 1 + yarn.lock | 9 ++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 0e486a64c9440..9c34013180464 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1026,7 +1026,9 @@ module.exports = { */ { files: ['x-pack/plugins/fleet/**/*.{js,mjs,ts,tsx}'], + plugins: ['testing-library'], rules: { + 'testing-library/await-async-utils': 'error', '@typescript-eslint/consistent-type-imports': 'error', 'import/order': [ 'warn', @@ -1954,6 +1956,19 @@ module.exports = { }, }, + /** + * Cloud Security Team overrides + */ + /** + * Fleet overrides + */ + { + files: ['x-pack/plugins/cloud_security_posture/**/*.{js,mjs,ts,tsx}'], + plugins: ['testing-library'], + rules: { + 'testing-library/await-async-utils': 'error', + }, + }, /** * Code inside .buildkite runs separately from everything else in CI, before bootstrap, with ts-node. It needs a few tweaks because of this. */ diff --git a/package.json b/package.json index 58aedd6473d14..4565d239e651b 100644 --- a/package.json +++ b/package.json @@ -1714,6 +1714,7 @@ "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-perf": "^3.3.1", + "eslint-plugin-testing-library": "^6.4.0", "eslint-traverse": "^1.0.0", "exit-hook": "^2.2.0", "expect": "^29.7.0", diff --git a/yarn.lock b/yarn.lock index e725eedf67084..2705fee21c3c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11707,7 +11707,7 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.10.0", "@typescript-eslint/utils@^6.18.1": +"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.10.0", "@typescript-eslint/utils@^5.62.0", "@typescript-eslint/utils@^6.18.1": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== @@ -17450,6 +17450,13 @@ eslint-plugin-react@^7.32.2: semver "^6.3.0" string.prototype.matchall "^4.0.8" +eslint-plugin-testing-library@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-testing-library/-/eslint-plugin-testing-library-6.4.0.tgz#1ba8a7422e3e31cc315a73ff17c34908f56f9838" + integrity sha512-yeWF+YgCgvNyPNI9UKnG0FjeE2sk93N/3lsKqcmR8dSfeXJwFT5irnWo7NjLf152HkRzfoFjh3LsBUrhvFz4eA== + dependencies: + "@typescript-eslint/utils" "^5.62.0" + eslint-rule-composer@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9" From dff28c374339a67dac8fe072f691f08a886d1e49 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 1 Nov 2024 15:51:53 -0700 Subject: [PATCH 02/10] fix setup technology error --- .../single_page_layout/hooks/setup_technology.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts index 465a7241b3ad3..6bd3288af2e0d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts @@ -180,6 +180,7 @@ export function useSetupTechnology({ } as NewAgentPolicy; setNewAgentPolicy(agentlessPolicy); + setNewAgentlessPolicy(agentlessPolicy); setSelectedPolicyTab(SelectedPolicyTab.NEW); updateAgentPolicies([agentlessPolicy] as AgentPolicy[]); } From 23b8d686dbfae25635ca4728c73bbd17685f640b Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 1 Nov 2024 15:52:07 -0700 Subject: [PATCH 03/10] review setup technology test cases --- .../hooks/setup_technology.test.ts | 241 ++++++++++++------ 1 file changed, 161 insertions(+), 80 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts index 4f6da695f260a..82e4f80c9b271 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { renderHook, act } from '@testing-library/react-hooks'; +import { renderHook, act } from '@testing-library/react-hooks/dom'; import { waitFor } from '@testing-library/react'; @@ -135,9 +135,7 @@ describe('useAgentless', () => { }); }); -// FLAKY: https://github.com/elastic/kibana/issues/189038 -// FLAKY: https://github.com/elastic/kibana/issues/192126 -describe.skip('useSetupTechnology', () => { +describe('useSetupTechnology', () => { const setNewAgentPolicy = jest.fn(); const updateAgentPoliciesMock = jest.fn(); const setSelectedPolicyTabMock = jest.fn(); @@ -298,7 +296,7 @@ describe.skip('useSetupTechnology', () => { }); it('should fetch agentless policy if agentless feature is enabled and isServerless is true', async () => { - const { waitForNextUpdate } = renderHook(() => + renderHook(() => useSetupTechnology({ setNewAgentPolicy, newAgentPolicy: newAgentPolicyMock, @@ -308,9 +306,9 @@ describe.skip('useSetupTechnology', () => { }) ); - await waitForNextUpdate(); - - expect(sendGetOneAgentPolicy).toHaveBeenCalled(); + await waitFor(() => { + expect(sendGetOneAgentPolicy).toHaveBeenCalled(); + }); }); it('should set agentless setup technology if agent policy supports agentless in edit page', async () => { @@ -356,7 +354,7 @@ describe.skip('useSetupTechnology', () => { isCloudEnabled: true, }, }); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, newAgentPolicy: newAgentPolicyMock, @@ -371,14 +369,13 @@ describe.skip('useSetupTechnology', () => { act(() => { result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS); }); - - waitForNextUpdate(); - - expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENTLESS); - expect(setNewAgentPolicy).toHaveBeenCalledWith({ - name: 'Agentless policy for endpoint-1', - supports_agentless: true, - inactivity_timeout: 3600, + await waitFor(() => { + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENTLESS); + expect(setNewAgentPolicy).toHaveBeenCalledWith({ + name: 'Agentless policy for endpoint-1', + supports_agentless: true, + inactivity_timeout: 3600, + }); }); }); @@ -396,21 +393,20 @@ describe.skip('useSetupTechnology', () => { isCloudEnabled: true, }, }); - const { result, rerender } = renderHook(() => - useSetupTechnology({ - setNewAgentPolicy, - newAgentPolicy: newAgentPolicyMock, - updateAgentPolicies: updateAgentPoliciesMock, - setSelectedPolicyTab: setSelectedPolicyTabMock, - packagePolicy: packagePolicyMock, - }) - ); - await rerender(); + const initialProps = { + setNewAgentPolicy, + newAgentPolicy: newAgentPolicyMock, + updateAgentPolicies: updateAgentPoliciesMock, + setSelectedPolicyTab: setSelectedPolicyTabMock, + packagePolicy: packagePolicyMock, + }; - expect(generateNewAgentPolicyWithDefaults).toHaveBeenCalled(); + const { result, rerender } = renderHook((props = initialProps) => useSetupTechnology(props), { + initialProps, + }); - expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); + expect(generateNewAgentPolicyWithDefaults).toHaveBeenCalled(); act(() => { result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS); @@ -434,7 +430,7 @@ describe.skip('useSetupTechnology', () => { }, }); - waitFor(() => { + await waitFor(() => { expect(setNewAgentPolicy).toHaveBeenCalledWith({ name: 'Agentless policy for endpoint-2', inactivity_timeout: 3600, @@ -451,7 +447,7 @@ describe.skip('useSetupTechnology', () => { }, }); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, newAgentPolicy: newAgentPolicyMock, @@ -467,8 +463,7 @@ describe.skip('useSetupTechnology', () => { result.current.handleSetupTechnologyChange(SetupTechnology.AGENT_BASED); }); - waitForNextUpdate(); - expect(setNewAgentPolicy).toHaveBeenCalledTimes(0); + await waitFor(() => expect(setNewAgentPolicy).toHaveBeenCalledTimes(0)); }); it('should not fetch agentless policy if agentless is enabled but serverless is disabled', async () => { @@ -493,7 +488,7 @@ describe.skip('useSetupTechnology', () => { }); it('should update agent policy and selected policy tab when setup technology is agentless', async () => { - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, newAgentPolicy: newAgentPolicyMock, @@ -503,18 +498,24 @@ describe.skip('useSetupTechnology', () => { }) ); - await waitForNextUpdate(); - act(() => { result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS); }); - expect(updateAgentPoliciesMock).toHaveBeenCalledWith([{ id: 'agentless-policy-id' }]); - expect(setSelectedPolicyTabMock).toHaveBeenCalledWith(SelectedPolicyTab.EXISTING); + await waitFor(() => { + expect(updateAgentPoliciesMock).toHaveBeenCalledWith([ + { + inactivity_timeout: 3600, + name: 'Agentless policy for endpoint-1', + supports_agentless: true, + }, + ]); + expect(setSelectedPolicyTabMock).toHaveBeenCalledWith(SelectedPolicyTab.EXISTING); + }); }); it('should update new agent policy and selected policy tab when setup technology is agent-based', async () => { - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, newAgentPolicy: newAgentPolicyMock, @@ -524,8 +525,6 @@ describe.skip('useSetupTechnology', () => { }) ); - await waitForNextUpdate(); - expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); act(() => { @@ -540,8 +539,10 @@ describe.skip('useSetupTechnology', () => { expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); - expect(setNewAgentPolicy).toHaveBeenCalledWith(newAgentPolicyMock); - expect(setSelectedPolicyTabMock).toHaveBeenCalledWith(SelectedPolicyTab.NEW); + await waitFor(() => { + expect(setNewAgentPolicy).toHaveBeenCalledWith(newAgentPolicyMock); + expect(setSelectedPolicyTabMock).toHaveBeenCalledWith(SelectedPolicyTab.NEW); + }); }); it('should not update agent policy and selected policy tab when agentless is disabled', async () => { @@ -569,7 +570,7 @@ describe.skip('useSetupTechnology', () => { }); it('should not update agent policy and selected policy tab when setup technology matches the current one ', async () => { - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, newAgentPolicy: newAgentPolicyMock, @@ -579,7 +580,7 @@ describe.skip('useSetupTechnology', () => { }) ); - await waitForNextUpdate(); + await waitFor(() => new Promise((resolve) => resolve(null))); expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); @@ -594,7 +595,7 @@ describe.skip('useSetupTechnology', () => { }); it('should revert the agent policy name to the original value when switching from agentless back to agent-based', async () => { - const { result, rerender } = renderHook(() => + const { result } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, newAgentPolicy: newAgentPolicyMock, @@ -603,7 +604,6 @@ describe.skip('useSetupTechnology', () => { packagePolicy: packagePolicyMock, }) ); - await rerender(); expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); @@ -612,20 +612,68 @@ describe.skip('useSetupTechnology', () => { }); expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENTLESS); - expect(setNewAgentPolicy).toHaveBeenCalledWith({ - id: 'agentless-policy-id', + + await waitFor(() => { + expect(setNewAgentPolicy).toHaveBeenCalledWith({ + name: 'Agentless policy for endpoint-1', + supports_agentless: true, + inactivity_timeout: 3600, + }); }); act(() => { result.current.handleSetupTechnologyChange(SetupTechnology.AGENT_BASED); }); + + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); + expect(setNewAgentPolicy).toHaveBeenCalledWith(newAgentPolicyMock); + }); + + it('should have global_data_tags with the integration team when creating agentless policy with global_data_tags', async () => { + (useConfig as MockFn).mockReturnValue({ + agentless: { + enabled: true, + api: { + url: 'https://agentless.api.url', + }, + }, + } as any); + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isCloudEnabled: true, + }, + }); + + const { result } = renderHook(() => + useSetupTechnology({ + setNewAgentPolicy, + newAgentPolicy: newAgentPolicyMock, + updateAgentPolicies: updateAgentPoliciesMock, + setSelectedPolicyTab: setSelectedPolicyTabMock, + packagePolicy: packagePolicyMock, + packageInfo: packageInfoMock, + }) + ); + + act(() => { + result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS, 'cspm'); + }); + await waitFor(() => { - expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); - expect(setNewAgentPolicy).toHaveBeenCalledWith(newAgentPolicyMock); + expect(setNewAgentPolicy).toHaveBeenCalledWith( + expect.objectContaining({ + supports_agentless: true, + global_data_tags: [ + { name: 'organization', value: 'org' }, + { name: 'division', value: 'div' }, + { name: 'team', value: 'team' }, + ], + }) + ); }); }); - it('should have global_data_tags with the integration team when updating the agentless policy', async () => { + it('should not fail and not have global_data_tags when creating the agentless policy when it cannot find the policy template', async () => { (useConfig as MockFn).mockReturnValue({ agentless: { enabled: true, @@ -648,19 +696,23 @@ describe.skip('useSetupTechnology', () => { setSelectedPolicyTab: setSelectedPolicyTabMock, packagePolicy: packagePolicyMock, packageInfo: packageInfoMock, - isEditPage: true, - agentPolicies: [{ id: 'agentless-policy-id', supports_agentless: true } as any], }) ); act(() => { - result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS, 'cspm'); + result.current.handleSetupTechnologyChange( + SetupTechnology.AGENTLESS, + 'never-gonna-give-you-up' + ); }); - waitFor(() => { + await waitFor(() => { expect(setNewAgentPolicy).toHaveBeenCalledWith({ - ...newAgentPolicyMock, + name: 'Agentless policy for endpoint-1', supports_agentless: true, + inactivity_timeout: 3600, + }); + expect(setNewAgentPolicy).not.toHaveBeenCalledWith({ global_data_tags: [ { name: 'organization', value: 'org' }, { name: 'division', value: 'div' }, @@ -670,7 +722,7 @@ describe.skip('useSetupTechnology', () => { }); }); - it('should not fail and not have global_data_tags when updating the agentless policy when it cannot find the policy template', async () => { + it('should not fail and not have global_data_tags when creating the agentless policy without the policy template name', async () => { (useConfig as MockFn).mockReturnValue({ agentless: { enabled: true, @@ -692,27 +744,31 @@ describe.skip('useSetupTechnology', () => { updateAgentPolicies: updateAgentPoliciesMock, setSelectedPolicyTab: setSelectedPolicyTabMock, packagePolicy: packagePolicyMock, - isEditPage: true, - agentPolicies: [{ id: 'agentless-policy-id', supports_agentless: true } as any], + packageInfo: packageInfoMock, }) ); act(() => { - result.current.handleSetupTechnologyChange( - SetupTechnology.AGENTLESS, - 'never-gonna-give-you-up' - ); + result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS); }); - waitFor(() => { + await waitFor(() => { expect(setNewAgentPolicy).toHaveBeenCalledWith({ - ...newAgentPolicyMock, + name: 'Agentless policy for endpoint-1', supports_agentless: true, + inactivity_timeout: 3600, + }); + expect(setNewAgentPolicy).not.toHaveBeenCalledWith({ + global_data_tags: [ + { name: 'organization', value: 'org' }, + { name: 'division', value: 'div' }, + { name: 'team', value: 'team' }, + ], }); }); }); - it('should not fail and not have global_data_tags when updating the agentless policy without the policy temaplte name', async () => { + it('should not fail and not have global_data_tags when creating the agentless policy without the packageInfo', async () => { (useConfig as MockFn).mockReturnValue({ agentless: { enabled: true, @@ -734,25 +790,30 @@ describe.skip('useSetupTechnology', () => { updateAgentPolicies: updateAgentPoliciesMock, setSelectedPolicyTab: setSelectedPolicyTabMock, packagePolicy: packagePolicyMock, - packageInfo: packageInfoMock, - isEditPage: true, - agentPolicies: [{ id: 'agentless-policy-id', supports_agentless: true } as any], }) ); act(() => { - result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS); + result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS, 'cspm'); }); - waitFor(() => { + await waitFor(() => { expect(setNewAgentPolicy).toHaveBeenCalledWith({ - ...newAgentPolicyMock, + name: 'Agentless policy for endpoint-1', supports_agentless: true, + inactivity_timeout: 3600, + }); + expect(setNewAgentPolicy).not.toHaveBeenCalledWith({ + global_data_tags: [ + { name: 'organization', value: 'org' }, + { name: 'division', value: 'div' }, + { name: 'team', value: 'team' }, + ], }); }); }); - it('should not fail and not have global_data_tags when updating the agentless policy without the packageInfo', async () => { + it('should not have global_data_tags when switching from agentless to agent-based policy', async () => { (useConfig as MockFn).mockReturnValue({ agentless: { enabled: true, @@ -774,8 +835,7 @@ describe.skip('useSetupTechnology', () => { updateAgentPolicies: updateAgentPoliciesMock, setSelectedPolicyTab: setSelectedPolicyTabMock, packagePolicy: packagePolicyMock, - isEditPage: true, - agentPolicies: [{ id: 'agentless-policy-id', supports_agentless: true } as any], + packageInfo: packageInfoMock, }) ); @@ -783,10 +843,31 @@ describe.skip('useSetupTechnology', () => { result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS, 'cspm'); }); - waitFor(() => { - expect(setNewAgentPolicy).toHaveBeenCalledWith({ - ...newAgentPolicyMock, - supports_agentless: true, + await waitFor(() => { + expect(setNewAgentPolicy).toHaveBeenCalledWith( + expect.objectContaining({ + supports_agentless: true, + global_data_tags: [ + { name: 'organization', value: 'org' }, + { name: 'division', value: 'div' }, + { name: 'team', value: 'team' }, + ], + }) + ); + }); + + act(() => { + result.current.handleSetupTechnologyChange(SetupTechnology.AGENT_BASED); + }); + + await waitFor(() => { + expect(setNewAgentPolicy).toHaveBeenCalledWith(newAgentPolicyMock); + expect(setNewAgentPolicy).not.toHaveBeenCalledWith({ + global_data_tags: [ + { name: 'organization', value: 'org' }, + { name: 'division', value: 'div' }, + { name: 'team', value: 'team' }, + ], }); }); }); From 51936085d4bb2e7f32c02c40b73ab978ed1620da Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 1 Nov 2024 15:52:26 -0700 Subject: [PATCH 04/10] handle unhandled waitFor catched by eslint --- .../steps/step_define_package_policy.test.tsx | 91 ++++++++++--------- .../steps/step_select_hosts.test.tsx | 46 +++++----- .../components/step_edit_hosts.test.tsx | 32 ++++--- 3 files changed, 87 insertions(+), 82 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx index 1ebfe5a897b07..2ef8cab16eb8f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx @@ -6,7 +6,9 @@ */ import React from 'react'; -import { act, fireEvent, waitFor } from '@testing-library/react'; +import { waitFor, act } from '@testing-library/react'; + +import { userEvent } from '@testing-library/user-event'; import { getInheritedNamespace } from '../../../../../../../../common/services'; @@ -60,18 +62,6 @@ describe('StepDefinePackagePolicy', () => { package_policies: [], is_protected: false, }, - { - id: 'agent-policy-2', - namespace: 'default', - name: 'Agent policy 2', - is_managed: false, - status: 'active', - updated_at: '', - updated_by: '', - revision: 1, - package_policies: [], - is_protected: false, - }, ]; let packagePolicy: NewPackagePolicy; const mockUpdatePackagePolicy = jest.fn().mockImplementation((val: any) => { @@ -86,11 +76,14 @@ describe('StepDefinePackagePolicy', () => { description: null, namespace: null, inputs: {}, - vars: {}, + vars: { + 'Required var': ['Required var is required'], + }, }; let testRenderer: TestRenderer; let renderResult: ReturnType; + const render = () => (renderResult = testRenderer.render( { packagePolicy={packagePolicy} updatePackagePolicy={mockUpdatePackagePolicy} validationResults={validationResults} - submitAttempted={false} + submitAttempted={true} /> )); @@ -107,37 +100,50 @@ describe('StepDefinePackagePolicy', () => { packagePolicy = { name: '', description: 'desc', - namespace: 'default', + namespace: undefined, + enabled: true, policy_id: '', policy_ids: [''], - enabled: true, + package: { + name: 'apache', + title: 'Apache', + version: '1.0.0', + }, inputs: [], + vars: { + 'Show user var': { + type: 'string', + value: 'showUserVarVal', + }, + 'Required var': { + type: 'bool', + value: undefined, + }, + 'Advanced var': { + type: 'bool', + value: true, + }, + }, }; testRenderer = createFleetTestRendererMock(); }); describe('default API response', () => { - beforeEach(() => { - render(); - }); - it('should display vars coming from package policy', async () => { - waitFor(() => { - expect(renderResult.getByDisplayValue('showUserVarVal')).toBeInTheDocument(); - expect(renderResult.getByRole('switch')).toHaveAttribute('aria-label', 'Required var'); - expect(renderResult.getByText('Required var is required')).toHaveAttribute( - 'class', - 'euiFormErrorText' - ); + act(() => { + render(); }); + expect(renderResult.getByDisplayValue('showUserVarVal')).toBeInTheDocument(); + expect(renderResult.getByRole('switch', { name: 'Required var' })).toBeInTheDocument(); + expect(renderResult.queryByRole('switch', { name: 'Advanced var' })).not.toBeInTheDocument(); - await act(async () => { - fireEvent.click(renderResult.getByText('Advanced options').closest('button')!); - }); + expect(renderResult.getByText('Required var is required')).toHaveClass('euiFormErrorText'); + + await userEvent.click(renderResult.getByText('Advanced options').closest('button')!); - waitFor(() => { - expect(renderResult.getByRole('switch')).toHaveAttribute('aria-label', 'Advanced var'); - expect(renderResult.getByTestId('packagePolicyNamespaceInput')).toHaveAttribute( + await waitFor(() => { + expect(renderResult.getByRole('switch', { name: 'Advanced var' })).toBeInTheDocument(); + expect(renderResult.getByTestId('comboBoxSearchInput')).toHaveAttribute( 'placeholder', 'ns' ); @@ -147,17 +153,16 @@ describe('StepDefinePackagePolicy', () => { describe('update', () => { describe('when package vars are introduced in a new package version', () => { - it('should display new package vars', () => { - render(); - - waitFor(async () => { - expect(renderResult.getByDisplayValue('showUserVarVal')).toBeInTheDocument(); - expect(renderResult.getByText('Required var')).toBeInTheDocument(); + it('should display new package vars', async () => { + act(() => { + render(); + }); + expect(renderResult.getByDisplayValue('showUserVarVal')).toBeInTheDocument(); + expect(renderResult.getByText('Required var')).toBeInTheDocument(); - await act(async () => { - fireEvent.click(renderResult.getByText('Advanced options').closest('button')!); - }); + await userEvent.click(renderResult.getByText('Advanced options').closest('button')!); + await waitFor(async () => { expect(renderResult.getByText('Advanced var')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.test.tsx index 7d1962939d1fa..583957861bd79 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.test.tsx @@ -6,7 +6,9 @@ */ import React from 'react'; -import { act, fireEvent, waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; + +import { userEvent } from '@testing-library/user-event'; import type { TestRenderer } from '../../../../../../../mock'; import { createFleetTestRendererMock } from '../../../../../../../mock'; @@ -108,22 +110,23 @@ describe('StepSelectHosts', () => { testRenderer = createFleetTestRendererMock(); }); - it('should display create form when no agent policies', () => { + it('should display create form when no agent policies', async () => { (useGetAgentPolicies as jest.MockedFunction).mockReturnValue({ data: { items: [], }, }); + (useAllNonManagedAgentPolicies as jest.MockedFunction).mockReturnValue([]); render(); - waitFor(() => { - expect(renderResult.getByText('Agent policy 1')).toBeInTheDocument(); + await waitFor(() => { + expect(renderResult.getByText('New agent policy name')).toBeInTheDocument(); }); expect(renderResult.queryByRole('tablist')).not.toBeInTheDocument(); }); - it('should display tabs with New hosts selected when agent policies exist', () => { + it('should display tabs with New hosts selected when agent policies exist', async () => { (useGetAgentPolicies as jest.MockedFunction).mockReturnValue({ data: { items: [{ id: '1', name: 'Agent policy 1', namespace: 'default' }], @@ -135,10 +138,7 @@ describe('StepSelectHosts', () => { render(); - waitFor(() => { - expect(renderResult.getByRole('tablist')).toBeInTheDocument(); - expect(renderResult.getByText('Agent policy 3')).toBeInTheDocument(); - }); + expect(renderResult.getByRole('tablist')).toBeInTheDocument(); expect(renderResult.getByText('New hosts').closest('button')).toHaveAttribute( 'aria-selected', 'true' @@ -157,16 +157,15 @@ describe('StepSelectHosts', () => { render(); - waitFor(() => { - expect(renderResult.getByRole('tablist')).toBeInTheDocument(); - }); - act(() => { - fireEvent.click(renderResult.getByText('Existing hosts').closest('button')!); - }); + expect(renderResult.getByRole('tablist')).toBeInTheDocument(); + + await userEvent.click(renderResult.getByText('Existing hosts').closest('button')!); - expect( - renderResult.container.querySelector('[data-test-subj="agentPolicySelect"]')?.textContent - ).toContain('Agent policy 1'); + await waitFor(() => { + expect( + renderResult.container.querySelector('[data-test-subj="agentPolicySelect"]')?.textContent + ).toContain('Agent policy 1'); + }); }); it('should display dropdown without preselected value when Existing hosts selected with mulitple agent policies', async () => { @@ -185,14 +184,11 @@ describe('StepSelectHosts', () => { render(); - waitFor(() => { - expect(renderResult.getByRole('tablist')).toBeInTheDocument(); - }); - act(() => { - fireEvent.click(renderResult.getByText('Existing hosts').closest('button')!); - }); + expect(renderResult.getByRole('tablist')).toBeInTheDocument(); + + await userEvent.click(renderResult.getByText('Existing hosts').closest('button')!); - await act(async () => { + await waitFor(() => { const select = renderResult.container.querySelector('[data-test-subj="agentPolicySelect"]'); expect((select as any)?.value).toEqual(''); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/components/step_edit_hosts.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/components/step_edit_hosts.test.tsx index 064624d364a92..809730e28962b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/components/step_edit_hosts.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/components/step_edit_hosts.test.tsx @@ -6,7 +6,9 @@ */ import React from 'react'; -import { act, fireEvent, waitFor } from '@testing-library/react'; +import { act, waitFor } from '@testing-library/react'; + +import { userEvent } from '@testing-library/user-event'; import type { TestRenderer } from '../../../../../../mock'; import { createFleetTestRendererMock } from '../../../../../../mock'; @@ -111,18 +113,18 @@ describe('StepEditHosts', () => { testRenderer = createFleetTestRendererMock(); }); - it('should display create form when no agent policies', () => { + it('should display create form when no agent policies', async () => { (useGetAgentPolicies as jest.MockedFunction).mockReturnValue({ data: { items: [], }, }); + (useAllNonManagedAgentPolicies as jest.MockedFunction).mockReturnValue([]); + render(); - waitFor(() => { - expect(renderResult.getByText('Agent policy 1')).toBeInTheDocument(); - }); + expect(renderResult.getByText('New agent policy name')).toBeInTheDocument(); expect(renderResult.queryByRole('tablist')).not.toBeInTheDocument(); }); @@ -144,7 +146,7 @@ describe('StepEditHosts', () => { ).toContain('Agent policy 1'); }); - it('should display dropdown without preselected value when mulitple agent policies', () => { + it('should display dropdown without preselected value when multiple agent policies', async () => { (useGetAgentPolicies as jest.MockedFunction).mockReturnValue({ data: { items: [ @@ -156,12 +158,12 @@ describe('StepEditHosts', () => { render(); - waitFor(() => { - expect(renderResult.getByText('At least one agent policy is required.')).toBeInTheDocument(); - }); + expect( + renderResult.getByText('Select an agent policy to add this integration to') + ).toBeInTheDocument(); }); - it('should display delete button when add button clicked', () => { + it('should display delete button when add button clicked', async () => { (useGetAgentPolicies as jest.MockedFunction).mockReturnValue({ data: { items: [{ id: '1', name: 'Agent policy 1', namespace: 'default' }], @@ -173,10 +175,12 @@ describe('StepEditHosts', () => { render(); - act(() => { - fireEvent.click(renderResult.getByTestId('createNewAgentPolicyButton').closest('button')!); - }); + await userEvent.click( + renderResult.getByTestId('createNewAgentPolicyButton').closest('button')! + ); - expect(renderResult.getByTestId('deleteNewAgentPolicyButton')).toBeInTheDocument(); + await waitFor(() => { + expect(renderResult.getByTestId('deleteNewAgentPolicyButton')).toBeInTheDocument(); + }); }); }); From 53e8e578368600e230c490b7fb64a65f89f79e09 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 1 Nov 2024 16:12:44 -0700 Subject: [PATCH 05/10] fix eslint rule --- .../components/step_edit_hosts.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/components/step_edit_hosts.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/components/step_edit_hosts.test.tsx index 809730e28962b..e30fa6c22c5ce 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/components/step_edit_hosts.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/components/step_edit_hosts.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { act, waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; import { userEvent } from '@testing-library/user-event'; From f450ea9136aee427559516fe17c3f1dc2a24cc22 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 4 Nov 2024 11:39:26 -0800 Subject: [PATCH 06/10] setting cloud security agentless tests to latest integration version --- x-pack/plugins/cloud_security_posture/common/constants.ts | 2 +- .../test/cloud_security_posture_functional/config.agentless.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index efc56a0da7995..80a6532c4a094 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -174,4 +174,4 @@ export const SINGLE_ACCOUNT = 'single-account'; export const CLOUD_SECURITY_PLUGIN_VERSION = '1.9.0'; // Cloud Credentials Template url was implemented in 1.10.0-preview01. See PR - https://github.com/elastic/integrations/pull/9828 -export const CLOUD_CREDENTIALS_PACKAGE_VERSION = '1.11.0-preview10'; +export const CLOUD_CREDENTIALS_PACKAGE_VERSION = '1.11.0-preview13'; diff --git a/x-pack/test/cloud_security_posture_functional/config.agentless.ts b/x-pack/test/cloud_security_posture_functional/config.agentless.ts index 498de6d888223..4585aa3ec4523 100644 --- a/x-pack/test/cloud_security_posture_functional/config.agentless.ts +++ b/x-pack/test/cloud_security_posture_functional/config.agentless.ts @@ -7,7 +7,6 @@ import type { FtrConfigProviderContext } from '@kbn/test'; import { CA_CERT_PATH, KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; -import { CLOUD_CREDENTIALS_PACKAGE_VERSION } from '@kbn/cloud-security-posture-plugin/common/constants'; import { pageObjects } from './page_objects'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { @@ -32,7 +31,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { `--xpack.fleet.agentless.api.tls.ca=${CA_CERT_PATH}`, `--xpack.cloud.id=something-anything`, `--xpack.fleet.packages.0.name=cloud_security_posture`, - `--xpack.fleet.packages.0.version=${CLOUD_CREDENTIALS_PACKAGE_VERSION}`, + `--xpack.fleet.packages.0.version=latest`, ], }, // load tests in the index file From 192ccabe0fbed0e9e04ac76336af41fa6757befb Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 4 Nov 2024 11:56:29 -0800 Subject: [PATCH 07/10] upgrading cloud security version for agentless tests --- .../test/cloud_security_posture_functional/config.agentless.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/cloud_security_posture_functional/config.agentless.ts b/x-pack/test/cloud_security_posture_functional/config.agentless.ts index 4585aa3ec4523..498de6d888223 100644 --- a/x-pack/test/cloud_security_posture_functional/config.agentless.ts +++ b/x-pack/test/cloud_security_posture_functional/config.agentless.ts @@ -7,6 +7,7 @@ import type { FtrConfigProviderContext } from '@kbn/test'; import { CA_CERT_PATH, KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; +import { CLOUD_CREDENTIALS_PACKAGE_VERSION } from '@kbn/cloud-security-posture-plugin/common/constants'; import { pageObjects } from './page_objects'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { @@ -31,7 +32,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { `--xpack.fleet.agentless.api.tls.ca=${CA_CERT_PATH}`, `--xpack.cloud.id=something-anything`, `--xpack.fleet.packages.0.name=cloud_security_posture`, - `--xpack.fleet.packages.0.version=latest`, + `--xpack.fleet.packages.0.version=${CLOUD_CREDENTIALS_PACKAGE_VERSION}`, ], }, // load tests in the index file From a96f9afd3142c18545b29994d5b94e65414881e4 Mon Sep 17 00:00:00 2001 From: Paulo Silva Date: Mon, 4 Nov 2024 14:30:39 -0800 Subject: [PATCH 08/10] Updating comment --- .eslintrc.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 9c34013180464..f751c9692c996 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1959,9 +1959,6 @@ module.exports = { /** * Cloud Security Team overrides */ - /** - * Fleet overrides - */ { files: ['x-pack/plugins/cloud_security_posture/**/*.{js,mjs,ts,tsx}'], plugins: ['testing-library'], From b2c245667587b5502a8a409a1aa32bf18e4998fe Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Tue, 5 Nov 2024 10:51:50 -0800 Subject: [PATCH 09/10] adding namespace from package policy test --- .../steps/step_define_package_policy.test.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx index 2ef8cab16eb8f..f7bb1e9382edd 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx @@ -100,7 +100,7 @@ describe('StepDefinePackagePolicy', () => { packagePolicy = { name: '', description: 'desc', - namespace: undefined, + namespace: '', enabled: true, policy_id: '', policy_ids: [''], @@ -149,6 +149,21 @@ describe('StepDefinePackagePolicy', () => { ); }); }); + + it('should display namespace from package policy', async () => { + packagePolicy.namespace = 'package-policy-ns'; + act(() => { + render(); + }); + + await userEvent.click(renderResult.getByText('Advanced options').closest('button')!); + + await waitFor(() => { + expect(renderResult.getByTestId('packagePolicyNamespaceInput')).toHaveTextContent( + 'package-policy-ns' + ); + }); + }); }); describe('update', () => { From 536a4ad714a9cfcbc9faeca385f903bd8295c2f0 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Tue, 5 Nov 2024 11:25:29 -0800 Subject: [PATCH 10/10] adding default namespace test --- .../steps/step_define_package_policy.test.tsx | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx index f7bb1e9382edd..62b39e2e8708a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx @@ -84,10 +84,10 @@ describe('StepDefinePackagePolicy', () => { let testRenderer: TestRenderer; let renderResult: ReturnType; - const render = () => + const render = (namespacePlaceholder = getInheritedNamespace(agentPolicies)) => (renderResult = testRenderer.render( { packagePolicy = { name: '', description: 'desc', - namespace: '', + namespace: 'package-policy-ns', enabled: true, policy_id: '', policy_ids: [''], @@ -143,6 +143,21 @@ describe('StepDefinePackagePolicy', () => { await waitFor(() => { expect(renderResult.getByRole('switch', { name: 'Advanced var' })).toBeInTheDocument(); + expect(renderResult.getByTestId('packagePolicyNamespaceInput')).toHaveTextContent( + 'package-policy-ns' + ); + }); + }); + + it(`should display namespace from agent policy when there's no package policy namespace`, async () => { + packagePolicy.namespace = ''; + act(() => { + render(); + }); + + await userEvent.click(renderResult.getByText('Advanced options').closest('button')!); + + await waitFor(() => { expect(renderResult.getByTestId('comboBoxSearchInput')).toHaveAttribute( 'placeholder', 'ns' @@ -150,17 +165,18 @@ describe('StepDefinePackagePolicy', () => { }); }); - it('should display namespace from package policy', async () => { - packagePolicy.namespace = 'package-policy-ns'; + it(`should fallback to the default namespace when namespace is not set in package policy and there's no agent policy`, async () => { + packagePolicy.namespace = ''; act(() => { - render(); + render(getInheritedNamespace([])); }); await userEvent.click(renderResult.getByText('Advanced options').closest('button')!); await waitFor(() => { - expect(renderResult.getByTestId('packagePolicyNamespaceInput')).toHaveTextContent( - 'package-policy-ns' + expect(renderResult.getByTestId('comboBoxSearchInput')).toHaveAttribute( + 'placeholder', + 'default' ); }); });