Skip to content

Commit

Permalink
test: [M3-8556] - Add unit tests for NodeBalancer details package (li…
Browse files Browse the repository at this point in the history
…node#10916)

* summary panel tests

* tables panel tests

* nb settings tests

* nb firewall row

* nb firewall table tests

* NodeBalancersConfigurations.test.tsx

* nb confirmation dialog delete test

* Added changeset: Added unit tests for the NodeBalancerDetail package

* switch to userEvent instead of fireEvent @bnussman-akamai

* figured out mocking path/params, may need to rearrange stuff

* update some naming
  • Loading branch information
coliu-akamai authored Sep 24, 2024
1 parent a2b642c commit 0882740
Show file tree
Hide file tree
Showing 22 changed files with 826 additions and 125 deletions.
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-10916-tests-1726065357292.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Tests
---

Added unit tests for the NodeBalancerDetail package ([#10916](https://github.com/linode/manager/pull/10916))
5 changes: 3 additions & 2 deletions packages/manager/src/factories/nodebalancer.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {
import Factory from 'src/factories/factoryProxy';

import type {
NodeBalancer,
NodeBalancerConfig,
NodeBalancerConfigNode,
} from '@linode/api-v4/lib/nodebalancers/types';
import Factory from 'src/factories/factoryProxy';

export const nodeBalancerFactory = Factory.Sync.makeFactory<NodeBalancer>({
client_conn_throttle: 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react';

import { renderWithTheme } from 'src/utilities/testHelpers';
Expand Down Expand Up @@ -65,12 +65,12 @@ describe('NodeBalancerConfigNode', () => {
expect(queryByText('Remove')).not.toBeInTheDocument();
});

it('removes the node', () => {
it('removes the node', async () => {
const { getByText } = renderWithTheme(
<NodeBalancerConfigNode {...props} />
);

fireEvent.click(getByText('Remove'));
await userEvent.click(getByText('Remove'));
expect(props.removeNode).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react';

import { renderWithTheme } from 'src/utilities/testHelpers';
Expand Down Expand Up @@ -264,13 +264,13 @@ describe('NodeBalancerConfigPanel', () => {
).not.toBeInTheDocument();
});

it('adds another backend node', () => {
it('adds another backend node', async () => {
const { getByText } = renderWithTheme(
<NodeBalancerConfigPanel {...nbConfigPanelMockPropsForTest} />
);

const addNodeButton = getByText('Add a Node');
fireEvent.click(addNodeButton);
await userEvent.click(addNodeButton);
expect(nbConfigPanelMockPropsForTest.addNode).toHaveBeenCalled();
});

Expand All @@ -282,7 +282,7 @@ describe('NodeBalancerConfigPanel', () => {
expect(queryByText('Remove')).not.toBeInTheDocument();
});

it('removes a backend node', () => {
it('removes a backend node', async () => {
const { getByText } = renderWithTheme(
<NodeBalancerConfigPanel
{...nbConfigPanelMockPropsForTest}
Expand All @@ -291,21 +291,21 @@ describe('NodeBalancerConfigPanel', () => {
);

const removeNodeButton = getByText('Remove');
fireEvent.click(removeNodeButton);
await userEvent.click(removeNodeButton);
expect(nbConfigPanelMockPropsForTest.removeNode).toHaveBeenCalled();
});

it('deletes the configuration panel', () => {
it('deletes the configuration panel', async () => {
const { getByText } = renderWithTheme(
<NodeBalancerConfigPanel {...nbConfigPanelMockPropsForTest} />
);

const deleteConfigButton = getByText('Delete');
fireEvent.click(deleteConfigButton);
await userEvent.click(deleteConfigButton);
expect(nbConfigPanelMockPropsForTest.onDelete).toHaveBeenCalled();
});

it('saves the input after editing the configuration', () => {
it('saves the input after editing the configuration', async () => {
const { getByText } = renderWithTheme(
<NodeBalancerConfigPanel
{...nbConfigPanelMockPropsForTest}
Expand All @@ -314,7 +314,7 @@ describe('NodeBalancerConfigPanel', () => {
);

const editConfigButton = getByText('Save');
fireEvent.click(editConfigButton);
await userEvent.click(editConfigButton);
expect(nbConfigPanelMockPropsForTest.onSave).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react';

import { renderWithTheme } from 'src/utilities/testHelpers';
Expand Down Expand Up @@ -32,12 +32,12 @@ describe('NodeBalancerDeleteDialog', () => {
expect(getByText('Delete')).toBeVisible();
});

it('calls the onClose function of the dialog', () => {
it('calls the onClose function of the dialog', async () => {
const { getByText } = renderWithTheme(
<NodeBalancerDeleteDialog {...props} />
);

fireEvent.click(getByText('Cancel'));
await userEvent.click(getByText('Cancel'));
expect(props.onClose).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import { waitForElementToBeRemoved } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react';

import {
nodeBalancerConfigFactory,
nodeBalancerConfigNodeFactory,
} from 'src/factories';
import { makeResourcePage } from 'src/mocks/serverHandlers';
import { HttpResponse, http, server } from 'src/mocks/testServer';
import { renderWithTheme } from 'src/utilities/testHelpers';

import NodeBalancerConfigurations from './NodeBalancerConfigurations';

const props = {
grants: undefined,
nodeBalancerLabel: 'nb-1',
nodeBalancerRegion: 'us-east',
};

const loadingTestId = 'circle-progress';
const memoryRouter = { initialEntries: ['nodebalancers/1/configurations'] };
const routePath = 'nodebalancers/:nodeBalancerId/configurations';

const nodeBalancerConfig = nodeBalancerConfigFactory.build({
id: 1,
port: 3000,
});

describe('NodeBalancerConfigurations', () => {
beforeEach(() => {
server.resetHandlers();
});

it('renders the NodeBalancerConfigurations component with one configuration', async () => {
server.use(
http.get(`*/nodebalancers/:id/configs`, () => {
return HttpResponse.json(makeResourcePage([nodeBalancerConfig]));
}),
http.get(`*/nodebalancers/:id/configs/1/nodes`, () => {
return HttpResponse.json(
makeResourcePage([nodeBalancerConfigNodeFactory.build({ id: 1 })])
);
})
);

const { getByLabelText, getByTestId, getByText } = renderWithTheme(
<NodeBalancerConfigurations {...props} />,
{
MemoryRouter: memoryRouter,
routePath,
}
);

expect(getByTestId(loadingTestId)).toBeInTheDocument();

await waitForElementToBeRemoved(getByTestId(loadingTestId));

// Expected after mocking the configs returned
expect(getByText('Port 3000')).toBeVisible();
expect(getByLabelText('Protocol')).toBeInTheDocument();
expect(getByLabelText('Algorithm')).toBeInTheDocument();
expect(getByLabelText('Session Stickiness')).toBeInTheDocument();
expect(getByLabelText('Type')).toBeInTheDocument();
expect(getByLabelText('Label')).toBeInTheDocument();
expect(getByLabelText('IP Address')).toBeInTheDocument();
expect(getByLabelText('Weight')).toBeInTheDocument();
expect(getByLabelText('Port')).toBeInTheDocument();
expect(getByText('Listen on this port.')).toBeInTheDocument();
expect(getByText('Active Health Checks')).toBeInTheDocument();
expect(
getByText(
'Route subsequent requests from the client to the same backend.'
)
).toBeInTheDocument();
expect(
getByText(
'Enable passive checks based on observing communication with back-end nodes.'
)
).toBeInTheDocument();
expect(
getByText(
"Active health checks proactively check the health of back-end nodes. 'HTTP Valid Status' requires a 2xx or 3xx response from the backend node. 'HTTP Body Regex' uses a regex to match against an expected result body."
)
).toBeInTheDocument();
expect(getByText('Add a Node')).toBeInTheDocument();
expect(getByText('Backend Nodes')).toBeInTheDocument();

// Since there is an existing configuration, the Add Configuration button says 'Add Another Configuration'
expect(getByText('Add Another Configuration')).toBeVisible();
});

it('renders the NodeBalancerConfigurations component with no configurations', async () => {
server.use(
http.get(`*/nodebalancers/:id/configs`, () => {
return HttpResponse.json(makeResourcePage([]));
})
);

const { getByTestId, getByText, queryByLabelText } = renderWithTheme(
<NodeBalancerConfigurations {...props} />,
{
MemoryRouter: memoryRouter,
routePath,
}
);

expect(getByTestId(loadingTestId)).toBeInTheDocument();

await waitForElementToBeRemoved(getByTestId(loadingTestId));

// confirm there are no configs
expect(queryByLabelText('Protocol')).not.toBeInTheDocument();
expect(queryByLabelText('Algorithm')).not.toBeInTheDocument();
expect(queryByLabelText('Session Stickiness')).not.toBeInTheDocument();

// Since there are no existing configurations, the Add Configuration button says 'Add a Configuration'
expect(getByText('Add a Configuration')).toBeVisible();
});

it('adds another configuration', async () => {
server.use(
http.get(`*/nodebalancers/:id/configs`, () => {
return HttpResponse.json(makeResourcePage([]));
})
);

const { getByTestId, getByText, queryByLabelText } = renderWithTheme(
<NodeBalancerConfigurations {...props} />,
{
MemoryRouter: memoryRouter,
routePath,
}
);

expect(getByTestId(loadingTestId)).toBeInTheDocument();

await waitForElementToBeRemoved(getByTestId(loadingTestId));

// confirm no configuration exists yet
expect(queryByLabelText('Protocol')).not.toBeInTheDocument();
expect(queryByLabelText('Algorithm')).not.toBeInTheDocument();
expect(queryByLabelText('Session Stickiness')).not.toBeInTheDocument();

await userEvent.click(getByText('Add a Configuration'));

// confirm new configuration has been added
expect(queryByLabelText('Protocol')).toBeVisible();
expect(queryByLabelText('Algorithm')).toBeVisible();
expect(queryByLabelText('Session Stickiness')).toBeVisible();
});

it('opens the Delete Configuration dialog', async () => {
server.use(
http.get(`*/nodebalancers/:id/configs`, () => {
return HttpResponse.json(makeResourcePage([nodeBalancerConfig]));
}),
http.get(`*/nodebalancers/:id/configs/1/nodes`, () => {
return HttpResponse.json(makeResourcePage([]));
})
);

const { getByLabelText, getByTestId, getByText } = renderWithTheme(
<NodeBalancerConfigurations {...props} />,
{
MemoryRouter: memoryRouter,
routePath,
}
);

expect(getByTestId(loadingTestId)).toBeInTheDocument();

await waitForElementToBeRemoved(getByTestId(loadingTestId));

expect(getByText('Port 3000')).toBeVisible();
expect(getByLabelText('Protocol')).toBeInTheDocument();
expect(getByLabelText('Algorithm')).toBeInTheDocument();

await userEvent.click(getByText('Delete'));

expect(getByText('Delete this configuration on port 3000?')).toBeVisible();
expect(
getByText(
'Are you sure you want to delete this NodeBalancer Configuration?'
)
).toBeVisible();
});
});
Loading

0 comments on commit 0882740

Please sign in to comment.