[Fleet] [Cloud Security] Add Testing Library ESLint for handling waitFor (#198735)

## Summary

This PR aims to fix Flaky tests related to agentless detected by
https://github.com/elastic/kibana/issues/189038 and
https://github.com/elastic/kibana/issues/192126 by adding proper
handling of the `waitFor` methods.

It was also detected with
https://github.com/elastic/security-team/issues/10979 that some other
methods were not proper handled by `waitFor`, leading to the assertions
inside those unhandled `waitFor` being skipped by Jest.

This PR also introduces ESLint to enforce proper handling of waitFor
methods in tests files for Fleet and Cloud Security plugins.

Additional note: These changes should also unblock the failing tests on
the [React18 use waitFor with assertion callbacks in place of
waitForNextUpdate](https://github.com/elastic/kibana/pull/195087) PR


**Fleet changes**

- ESLint rule added to enforce handling `waitFor` on React Testing
Library.
- `useSetupTechnology` hook tests reviewed and updated to handle the
waitFor. Fixed issue identified when reviewing the tests.
- step_define_package_policy.test.tsx: Added package policy vars to the
mock to proper handle the use cases
- step_select_hosts.test.tsx: Handled waitFor, identified outdated test
- step_edit_hosts.test.tsx: Handled waitFor, identified outdated test
With the introduction of the ESLint rule other tests were triggering
ESLint errors, I attempted to fix them while retaining the same
intention, let me know if more changes are needed.

**Cloud Security changes**

- ESLint rule added to enforce handling `waitFor` on React Testing
Library.
- Updated cloud security posture version to include agentless global
tags on End to End tests

**@elastic/kibana-operations changes**

- Added
[eslint-plugin-testing-library](https://testing-library.com/docs/ecosystem-eslint-plugin-testing-library/)
an ESLint plugin for Testing Library that helps users to follow best
practices and anticipate common mistakes when writing tests.
- The adoption and enablement of the rules are opt-in.
This commit is contained in:
Paulo Silva 2024-11-05 14:34:18 -08:00 committed by GitHub
parent 8ebe78857b
commit 5ab59fba40
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 303 additions and 165 deletions

View file

@ -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,16 @@ module.exports = {
},
},
/**
* Cloud Security Team 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.
*/

View file

@ -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",

View file

@ -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';

View file

@ -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,20 +76,23 @@ describe('StepDefinePackagePolicy', () => {
description: null,
namespace: null,
inputs: {},
vars: {},
vars: {
'Required var': ['Required var is required'],
},
};
let testRenderer: TestRenderer;
let renderResult: ReturnType<typeof testRenderer.render>;
const render = () =>
const render = (namespacePlaceholder = getInheritedNamespace(agentPolicies)) =>
(renderResult = testRenderer.render(
<StepDefinePackagePolicy
namespacePlaceholder={getInheritedNamespace(agentPolicies)}
namespacePlaceholder={namespacePlaceholder}
packageInfo={packageInfo}
packagePolicy={packagePolicy}
updatePackagePolicy={mockUpdatePackagePolicy}
validationResults={validationResults}
submitAttempted={false}
submitAttempted={true}
/>
));
@ -107,57 +100,100 @@ describe('StepDefinePackagePolicy', () => {
packagePolicy = {
name: '',
description: 'desc',
namespace: 'default',
namespace: 'package-policy-ns',
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();
expect(renderResult.getByText('Required var is required')).toHaveClass('euiFormErrorText');
await userEvent.click(renderResult.getByText('Advanced options').closest('button')!);
await waitFor(() => {
expect(renderResult.getByRole('switch', { name: 'Advanced var' })).toBeInTheDocument();
expect(renderResult.getByTestId('packagePolicyNamespaceInput')).toHaveTextContent(
'package-policy-ns'
);
});
});
await act(async () => {
fireEvent.click(renderResult.getByText('Advanced options').closest('button')!);
it(`should display namespace from agent policy when there's no package policy namespace`, async () => {
packagePolicy.namespace = '';
act(() => {
render();
});
waitFor(() => {
expect(renderResult.getByRole('switch')).toHaveAttribute('aria-label', 'Advanced var');
expect(renderResult.getByTestId('packagePolicyNamespaceInput')).toHaveAttribute(
await userEvent.click(renderResult.getByText('Advanced options').closest('button')!);
await waitFor(() => {
expect(renderResult.getByTestId('comboBoxSearchInput')).toHaveAttribute(
'placeholder',
'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(getInheritedNamespace([]));
});
await userEvent.click(renderResult.getByText('Advanced options').closest('button')!);
await waitFor(() => {
expect(renderResult.getByTestId('comboBoxSearchInput')).toHaveAttribute(
'placeholder',
'default'
);
});
});
});
describe('update', () => {
describe('when package vars are introduced in a new package version', () => {
it('should display new package vars', () => {
render();
it('should display new package vars', async () => {
act(() => {
render();
});
expect(renderResult.getByDisplayValue('showUserVarVal')).toBeInTheDocument();
expect(renderResult.getByText('Required var')).toBeInTheDocument();
waitFor(async () => {
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();
});
});

View file

@ -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<any>).mockReturnValue({
data: {
items: [],
},
});
(useAllNonManagedAgentPolicies as jest.MockedFunction<any>).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<any>).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();
expect(
renderResult.container.querySelector('[data-test-subj="agentPolicySelect"]')?.textContent
).toContain('Agent policy 1');
await userEvent.click(renderResult.getByText('Existing hosts').closest('button')!);
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 act(async () => {
await userEvent.click(renderResult.getByText('Existing hosts').closest('button')!);
await waitFor(() => {
const select = renderResult.container.querySelector('[data-test-subj="agentPolicySelect"]');
expect((select as any)?.value).toEqual('');
});

View file

@ -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,22 +393,21 @@ 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,
};
const { result, rerender } = renderHook((props = initialProps) => useSetupTechnology(props), {
initialProps,
});
expect(generateNewAgentPolicyWithDefaults).toHaveBeenCalled();
expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED);
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,24 @@ 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);
});
await waitFor(() => {
expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED);
expect(setNewAgentPolicy).toHaveBeenCalledWith(newAgentPolicyMock);
});
expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED);
expect(setNewAgentPolicy).toHaveBeenCalledWith(newAgentPolicyMock);
});
it('should have global_data_tags with the integration team when updating the agentless policy', async () => {
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,
@ -648,8 +652,6 @@ describe.skip('useSetupTechnology', () => {
setSelectedPolicyTab: setSelectedPolicyTabMock,
packagePolicy: packagePolicyMock,
packageInfo: packageInfoMock,
isEditPage: true,
agentPolicies: [{ id: 'agentless-policy-id', supports_agentless: true } as any],
})
);
@ -657,20 +659,21 @@ describe.skip('useSetupTechnology', () => {
result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS, 'cspm');
});
waitFor(() => {
expect(setNewAgentPolicy).toHaveBeenCalledWith({
...newAgentPolicyMock,
supports_agentless: true,
global_data_tags: [
{ name: 'organization', value: 'org' },
{ name: 'division', value: 'div' },
{ name: 'team', value: 'team' },
],
});
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' },
],
})
);
});
});
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 when it cannot find the policy template', async () => {
(useConfig as MockFn).mockReturnValue({
agentless: {
enabled: true,
@ -692,8 +695,7 @@ describe.skip('useSetupTechnology', () => {
updateAgentPolicies: updateAgentPoliciesMock,
setSelectedPolicyTab: setSelectedPolicyTabMock,
packagePolicy: packagePolicyMock,
isEditPage: true,
agentPolicies: [{ id: 'agentless-policy-id', supports_agentless: true } as any],
packageInfo: packageInfoMock,
})
);
@ -704,15 +706,23 @@ describe.skip('useSetupTechnology', () => {
);
});
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 policy template name', async () => {
(useConfig as MockFn).mockReturnValue({
agentless: {
enabled: true,
@ -735,8 +745,6 @@ describe.skip('useSetupTechnology', () => {
setSelectedPolicyTab: setSelectedPolicyTabMock,
packagePolicy: packagePolicyMock,
packageInfo: packageInfoMock,
isEditPage: true,
agentPolicies: [{ id: 'agentless-policy-id', supports_agentless: true } as any],
})
);
@ -744,15 +752,23 @@ describe.skip('useSetupTechnology', () => {
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 packageInfo', 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,
@ -774,8 +790,6 @@ describe.skip('useSetupTechnology', () => {
updateAgentPolicies: updateAgentPoliciesMock,
setSelectedPolicyTab: setSelectedPolicyTabMock,
packagePolicy: packagePolicyMock,
isEditPage: true,
agentPolicies: [{ id: 'agentless-policy-id', supports_agentless: true } as any],
})
);
@ -783,10 +797,77 @@ describe.skip('useSetupTechnology', () => {
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 have global_data_tags when switching from agentless to agent-based policy', 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(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' },
],
});
});
});

View file

@ -180,6 +180,7 @@ export function useSetupTechnology({
} as NewAgentPolicy;
setNewAgentPolicy(agentlessPolicy);
setNewAgentlessPolicy(agentlessPolicy);
setSelectedPolicyTab(SelectedPolicyTab.NEW);
updateAgentPolicies([agentlessPolicy] as AgentPolicy[]);
}

View file

@ -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';
@ -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<any>).mockReturnValue({
data: {
items: [],
},
});
(useAllNonManagedAgentPolicies as jest.MockedFunction<any>).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<any>).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<any>).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();
});
});
});

View file

@ -11724,7 +11724,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==
@ -17472,6 +17472,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"