Repository: provectus/kafka-ui
Issue: Improve test coverage for ConsumerGroups folder
Labels: scope/frontend
status/accepted
type/chore

**Describe the bug**
<!--(A clear and concise description of what the bug is.)-->

Improve test coverage for [src/components/ConsumerGroups](https://sonarcloud.io/component_measures?metric=new_coverage&selected=com.provectus%3Akafka-ui_frontend%3Asrc%2Fcomponents%2FConsumerGroups&view=list&id=com.provectus%3Akafka-ui_frontend)

**Expected**
Coverage is close to 100%


Issue comments:

Pull request review comments:
why are you using async here?
delete 'async' please, I don't see reason of having it 
Why we have async here?
I guess you can delete it
I think if we expect something to be in the DOM definitely like this case , a better approach is to use `getBy` syntax to pinpoint exact point of failure on that function if something goes wrong.
Same here as well
Replacing queryBy with getBy since it exist in Dom to pin point the point of failure.

Patch that resolved the issue:
diff --git a/kafka-ui-react-app/src/components/ConsumerGroups/Details/ListItem.tsx b/kafka-ui-react-app/src/components/ConsumerGroups/Details/ListItem.tsx
index 7fab2e8b736..4d440d46a56 100644
--- a/kafka-ui-react-app/src/components/ConsumerGroups/Details/ListItem.tsx
+++ b/kafka-ui-react-app/src/components/ConsumerGroups/Details/ListItem.tsx
@@ -22,7 +22,11 @@ const ListItem: React.FC<Props> = ({ clusterName, name, consumers }) => {
     <>
       <tr>
         <ToggleButton>
-          <IconButtonWrapper onClick={() => setIsOpen(!isOpen)} aria-hidden>
+          <IconButtonWrapper
+            onClick={() => setIsOpen(!isOpen)}
+            aria-hidden
+            data-testid="consumer-group-list-item-toggle"
+          >
             <MessageToggleIcon isOpen={isOpen} />
           </IconButtonWrapper>
         </ToggleButton>
diff --git a/kafka-ui-react-app/src/components/ConsumerGroups/Details/TopicContents/__test__/TopicContents.spec.tsx b/kafka-ui-react-app/src/components/ConsumerGroups/Details/TopicContents/__test__/TopicContents.spec.tsx
new file mode 100644
index 00000000000..1c36aceda5f
--- /dev/null
+++ b/kafka-ui-react-app/src/components/ConsumerGroups/Details/TopicContents/__test__/TopicContents.spec.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import { clusterConsumerGroupDetailsPath } from 'lib/paths';
+import { screen } from '@testing-library/react';
+import TopicContents from 'components/ConsumerGroups/Details/TopicContents/TopicContents';
+import { consumerGroupPayload } from 'redux/reducers/consumerGroups/__test__/fixtures';
+import { render } from 'lib/testHelpers';
+import { Route } from 'react-router';
+import { ConsumerGroupTopicPartition } from 'generated-sources';
+
+const clusterName = 'cluster1';
+
+const renderComponent = (consumers: ConsumerGroupTopicPartition[] = []) =>
+  render(
+    <Route
+      path={clusterConsumerGroupDetailsPath(':clusterName', ':consumerGroupID')}
+    >
+      <table>
+        <tbody>
+          <TopicContents consumers={consumers} />
+        </tbody>
+      </table>
+    </Route>,
+    {
+      pathname: clusterConsumerGroupDetailsPath(
+        clusterName,
+        consumerGroupPayload.groupId
+      ),
+    }
+  );
+
+describe('TopicContent', () => {
+  it('renders empty table', () => {
+    renderComponent();
+    const table = screen.getAllByRole('table')[1];
+    expect(table.getElementsByTagName('td').length).toBe(0);
+  });
+
+  it('renders table with content', () => {
+    renderComponent(consumerGroupPayload.partitions);
+    const table = screen.getAllByRole('table')[1];
+    expect(table.getElementsByTagName('td').length).toBe(
+      consumerGroupPayload.partitions.length * 6
+    );
+  });
+});
diff --git a/kafka-ui-react-app/src/components/ConsumerGroups/Details/__tests__/ListItem.spec.tsx b/kafka-ui-react-app/src/components/ConsumerGroups/Details/__tests__/ListItem.spec.tsx
new file mode 100644
index 00000000000..7b3f9440661
--- /dev/null
+++ b/kafka-ui-react-app/src/components/ConsumerGroups/Details/__tests__/ListItem.spec.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import { clusterConsumerGroupDetailsPath } from 'lib/paths';
+import { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import ListItem from 'components/ConsumerGroups/Details/ListItem';
+import { consumerGroupPayload } from 'redux/reducers/consumerGroups/__test__/fixtures';
+import { render } from 'lib/testHelpers';
+import { Route } from 'react-router';
+import { ConsumerGroupTopicPartition } from 'generated-sources';
+
+const clusterName = 'cluster1';
+
+const renderComponent = (consumers: ConsumerGroupTopicPartition[] = []) =>
+  render(
+    <Route
+      path={clusterConsumerGroupDetailsPath(':clusterName', ':consumerGroupID')}
+    >
+      <table>
+        <tbody>
+          <ListItem
+            clusterName={clusterName}
+            name={clusterName}
+            consumers={consumers}
+          />
+        </tbody>
+      </table>
+    </Route>,
+    {
+      pathname: clusterConsumerGroupDetailsPath(
+        clusterName,
+        consumerGroupPayload.groupId
+      ),
+    }
+  );
+
+describe('ListItem', () => {
+  beforeEach(() => renderComponent(consumerGroupPayload.partitions));
+
+  it('should renders list item with topic content closed and check if element exists', () => {
+    expect(screen.getByRole('row')).toBeInTheDocument();
+  });
+
+  it('should renders list item with topic content open', () => {
+    userEvent.click(screen.getByTestId('consumer-group-list-item-toggle'));
+
+    expect(screen.getByText('Consumer ID')).toBeInTheDocument();
+  });
+});
diff --git a/kafka-ui-react-app/src/components/ConsumerGroups/List/__test__/List.spec.tsx b/kafka-ui-react-app/src/components/ConsumerGroups/List/__test__/List.spec.tsx
index 0ac9c0ceedf..d0e4ea8f21c 100644
--- a/kafka-ui-react-app/src/components/ConsumerGroups/List/__test__/List.spec.tsx
+++ b/kafka-ui-react-app/src/components/ConsumerGroups/List/__test__/List.spec.tsx
@@ -1,6 +1,6 @@
 import React from 'react';
 import List from 'components/ConsumerGroups/List/List';
-import { screen } from '@testing-library/react';
+import { screen, waitFor } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import { render } from 'lib/testHelpers';
 import { store } from 'redux/store';
@@ -15,7 +15,7 @@ describe('List', () => {
     expect(screen.getByText('No active consumer groups')).toBeInTheDocument();
   });
 
-  describe('consumerGroups are fecthed', () => {
+  describe('consumerGroups are fetched', () => {
     beforeEach(() => {
       store.dispatch({
         type: fetchConsumerGroups.fulfilled.type,
@@ -29,11 +29,14 @@ describe('List', () => {
     });
 
     describe('when searched', () => {
-      it('renders only searched consumers', () => {
-        userEvent.type(
-          screen.getByPlaceholderText('Search by Consumer Group ID'),
-          'groupId1'
-        );
+      it('renders only searched consumers', async () => {
+        await waitFor(() => {
+          userEvent.type(
+            screen.getByPlaceholderText('Search by Consumer Group ID'),
+            'groupId1'
+          );
+        });
+
         expect(screen.getByText('groupId1')).toBeInTheDocument();
         expect(screen.getByText('groupId2')).toBeInTheDocument();
       });
diff --git a/kafka-ui-react-app/src/components/ConsumerGroups/__test__/ConsumerGroups.spec.tsx b/kafka-ui-react-app/src/components/ConsumerGroups/__test__/ConsumerGroups.spec.tsx
new file mode 100644
index 00000000000..cbb90a7a2f4
--- /dev/null
+++ b/kafka-ui-react-app/src/components/ConsumerGroups/__test__/ConsumerGroups.spec.tsx
@@ -0,0 +1,65 @@
+import React from 'react';
+import { clusterConsumerGroupsPath } from 'lib/paths';
+import {
+  screen,
+  waitFor,
+  waitForElementToBeRemoved,
+} from '@testing-library/react';
+import ConsumerGroups from 'components/ConsumerGroups/ConsumerGroups';
+import { consumerGroups } from 'redux/reducers/consumerGroups/__test__/fixtures';
+import { render } from 'lib/testHelpers';
+import fetchMock from 'fetch-mock';
+import { Route } from 'react-router';
+
+const clusterName = 'cluster1';
+
+const renderComponent = () =>
+  render(
+    <Route path={clusterConsumerGroupsPath(':clusterName')}>
+      <ConsumerGroups />
+    </Route>,
+    {
+      pathname: clusterConsumerGroupsPath(clusterName),
+    }
+  );
+
+describe('ConsumerGroup', () => {
+  afterEach(() => {
+    fetchMock.reset();
+  });
+
+  it('renders with initial state', async () => {
+    renderComponent();
+
+    expect(screen.getByRole('progressbar')).toBeInTheDocument();
+  });
+
+  it('renders with 404 from consumer groups', async () => {
+    const consumerGroupsMock = fetchMock.getOnce(
+      `/api/clusters/${clusterName}/consumer-groups`,
+      404
+    );
+
+    renderComponent();
+
+    await waitFor(() => expect(consumerGroupsMock.called()).toBeTruthy());
+
+    expect(screen.queryByText('Consumers')).not.toBeInTheDocument();
+    expect(screen.queryByRole('table')).not.toBeInTheDocument();
+  });
+
+  it('renders with 200 from consumer groups', async () => {
+    const consumerGroupsMock = fetchMock.getOnce(
+      `/api/clusters/${clusterName}/consumer-groups`,
+      consumerGroups
+    );
+
+    renderComponent();
+
+    await waitForElementToBeRemoved(() => screen.getByRole('progressbar'));
+    await waitFor(() => expect(consumerGroupsMock.called()).toBeTruthy());
+
+    expect(screen.getByText('Consumers')).toBeInTheDocument();
+    expect(screen.getByRole('table')).toBeInTheDocument();
+  });
+});


Test patch: