Skip to content

Commit

Permalink
test: [M3-8565] - Add unit tests for rest of NodeBalancers package (#…
Browse files Browse the repository at this point in the history
…10945)

* quick eslint fixes

* unit tests for nb passive check and delete dialog

* starting tests for NodeBalancerConfigNode

* nodebalancerconfignode tests

* update tests in config panel to account for active check component

* unit test for NodeBalancerCreate to confirm it renders

* Added changeset: Add unit tests for rest of NodeBalancers package

* Update packages/manager/src/features/NodeBalancers/NodeBalancerConfigNode.test.tsx

Co-authored-by: Dajahi Wiley <114682940+dwiley-akamai@users.noreply.github.com>

* Update packages/manager/src/features/NodeBalancers/NodeBalancerCreate.test.tsx

Co-authored-by: Dajahi Wiley <114682940+dwiley-akamai@users.noreply.github.com>

---------

Co-authored-by: Dajahi Wiley <114682940+dwiley-akamai@users.noreply.github.com>
  • Loading branch information
coliu-akamai and dwiley-akamai authored Sep 18, 2024
1 parent df32f28 commit 75c5986
Show file tree
Hide file tree
Showing 9 changed files with 303 additions and 44 deletions.
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-10945-tests-1726507532580.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Tests
---

Add unit tests for rest of NodeBalancers package ([#10945](https://github.com/linode/manager/pull/10945))
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Linode } from '@linode/api-v4/lib/linodes';
import { Box } from '@mui/material';
import * as React from 'react';

import { SelectedIcon } from 'src/components/Autocomplete/Autocomplete.styles';
import { LinodeSelect } from 'src/features/Linodes/LinodeSelect/LinodeSelect';
import { privateIPRegex } from 'src/utilities/ipUtils';

import type { Linode } from '@linode/api-v4/lib/linodes';
import type { TextFieldProps } from 'src/components/TextField';

interface ConfigNodeIPSelectProps {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { fireEvent } from '@testing-library/react';
import * as React from 'react';

import { renderWithTheme } from 'src/utilities/testHelpers';

import { NodeBalancerConfigNode } from './NodeBalancerConfigNode';

import type { NodeBalancerConfigNodeProps } from './NodeBalancerConfigNode';

const node = {
address: 'some address',
label: 'some label',
};

const props: NodeBalancerConfigNodeProps = {
configIdx: 1,
disabled: false,
forEdit: true,
idx: 1,
node,
onNodeAddressChange: vi.fn(),
onNodeLabelChange: vi.fn(),
onNodeModeChange: vi.fn(),
onNodePortChange: vi.fn(),
onNodeWeightChange: vi.fn(),
removeNode: vi.fn(),
};

describe('NodeBalancerConfigNode', () => {
it('renders the NodeBalancerConfigNode', () => {
const { getByLabelText, getByText, queryByText } = renderWithTheme(
<NodeBalancerConfigNode {...props} />
);

expect(getByLabelText('Label')).toBeVisible();
expect(getByLabelText('Port')).toBeVisible();
expect(getByLabelText('Weight')).toBeVisible();
expect(getByText('Mode')).toBeVisible();
expect(getByText('Remove')).toBeVisible();
expect(queryByText('Status')).not.toBeInTheDocument();
});

it('renders the node status', () => {
const { getByText } = renderWithTheme(
<NodeBalancerConfigNode {...props} node={{ ...node, status: 'DOWN' }} />
);

expect(getByText('Status')).toBeVisible();
expect(getByText('DOWN')).toBeVisible();
});

it('cannot change the mode if the node is not for edit', () => {
const { queryByText } = renderWithTheme(
<NodeBalancerConfigNode {...props} forEdit={false} />
);

expect(queryByText('Mode')).not.toBeInTheDocument();
});

it('cannot remove the node if the node is not for edit or is the first node', () => {
const { queryByText } = renderWithTheme(
<NodeBalancerConfigNode {...props} forEdit={false} idx={0} />
);

expect(queryByText('Remove')).not.toBeInTheDocument();
});

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

fireEvent.click(getByText('Remove'));
expect(props.removeNode).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Grid from '@mui/material/Unstable_Grid2';
import { styled } from '@mui/material/styles';
import Grid from '@mui/material/Unstable_Grid2';
import * as React from 'react';

import { Autocomplete } from 'src/components/Autocomplete/Autocomplete';
Expand All @@ -13,8 +13,8 @@ import { Typography } from 'src/components/Typography';
import { getErrorMap } from 'src/utilities/errorUtils';

import { ConfigNodeIPSelect } from './ConfigNodeIPSelect';
import { NodeBalancerConfigNodeFields } from './types';

import type { NodeBalancerConfigNodeFields } from './types';
import type { NodeBalancerConfigNodeMode } from '@linode/api-v4';

export interface NodeBalancerConfigNodeProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const node: NodeBalancerConfigNodeFields = {
weight: 100,
};

const props: NodeBalancerConfigPanelProps = {
export const nbConfigPanelMockPropsForTest: NodeBalancerConfigPanelProps = {
addNode: vi.fn(),
algorithm: 'roundrobin',
checkBody: '',
Expand Down Expand Up @@ -70,7 +70,17 @@ const props: NodeBalancerConfigPanelProps = {
sslCertificate: '',
};

const activeHealthChecks = ['Interval', 'Timeout', 'Attempts'];
const activeHealthChecksFormInputs = ['Interval', 'Timeout', 'Attempts'];

const activeHealthChecksHelperText = [
'Seconds between health check probes',
'Seconds to wait before considering the probe a failure. 1-30. Must be less than check_interval.',
'Number of failed probes before taking a node out of rotation. 1-30',
];

const sslCertificate = 'ssl-certificate';
const privateKey = 'private-key';
const proxyProtocol = 'Proxy Protocol';

describe('NodeBalancerConfigPanel', () => {
it('renders the NodeBalancerConfigPanel', () => {
Expand All @@ -79,7 +89,10 @@ describe('NodeBalancerConfigPanel', () => {
getByText,
queryByLabelText,
queryByTestId,
} = renderWithTheme(<NodeBalancerConfigPanel {...props} />);
queryByText,
} = renderWithTheme(
<NodeBalancerConfigPanel {...nbConfigPanelMockPropsForTest} />
);

expect(getByLabelText('Protocol')).toBeVisible();
expect(getByLabelText('Algorithm')).toBeVisible();
Expand Down Expand Up @@ -109,75 +122,107 @@ describe('NodeBalancerConfigPanel', () => {
expect(getByText('Add a Node')).toBeVisible();
expect(getByText('Backend Nodes')).toBeVisible();

activeHealthChecks.forEach((type) => {
expect(queryByLabelText(type)).not.toBeInTheDocument();
activeHealthChecksFormInputs.forEach((formLabel) => {
expect(queryByLabelText(formLabel)).not.toBeInTheDocument();
});
expect(queryByTestId('ssl-certificate')).not.toBeInTheDocument();
expect(queryByTestId('private-key')).not.toBeInTheDocument();
activeHealthChecksHelperText.forEach((helperText) => {
expect(queryByText(helperText)).not.toBeInTheDocument();
});
expect(queryByTestId(sslCertificate)).not.toBeInTheDocument();
expect(queryByTestId(privateKey)).not.toBeInTheDocument();
expect(queryByTestId('http-path')).not.toBeInTheDocument();
expect(queryByTestId('http-body')).not.toBeInTheDocument();
expect(queryByLabelText('Proxy Protocol')).not.toBeInTheDocument();
expect(queryByLabelText(proxyProtocol)).not.toBeInTheDocument();
});

it('renders form fields specific to the HTTPS protocol', () => {
const { getByTestId, queryByLabelText } = renderWithTheme(
<NodeBalancerConfigPanel {...props} protocol="https" />
<NodeBalancerConfigPanel
{...nbConfigPanelMockPropsForTest}
protocol="https"
/>
);

expect(getByTestId('ssl-certificate')).toBeVisible();
expect(getByTestId('private-key')).toBeVisible();
expect(queryByLabelText('Proxy Protocol')).not.toBeInTheDocument();
expect(getByTestId(sslCertificate)).toBeVisible();
expect(getByTestId(privateKey)).toBeVisible();
expect(queryByLabelText(proxyProtocol)).not.toBeInTheDocument();
});

it('renders form fields specific to the TCP protocol', () => {
const { getByLabelText, queryByTestId } = renderWithTheme(
<NodeBalancerConfigPanel {...props} protocol="tcp" />
<NodeBalancerConfigPanel
{...nbConfigPanelMockPropsForTest}
protocol="tcp"
/>
);

expect(getByLabelText('Proxy Protocol')).toBeVisible();
expect(queryByTestId('ssl-certificate')).not.toBeInTheDocument();
expect(queryByTestId('private-key')).not.toBeInTheDocument();
expect(getByLabelText(proxyProtocol)).toBeVisible();
expect(queryByTestId(sslCertificate)).not.toBeInTheDocument();
expect(queryByTestId(privateKey)).not.toBeInTheDocument();
});

it('renders fields specific to the Active Health Check type of TCP Connection', () => {
const { getByLabelText, queryByTestId } = renderWithTheme(
<NodeBalancerConfigPanel {...props} healthCheckType="connection" />
const { getByLabelText, getByText, queryByTestId } = renderWithTheme(
<NodeBalancerConfigPanel
{...nbConfigPanelMockPropsForTest}
healthCheckType="connection"
/>
);

activeHealthChecks.forEach((type) => {
expect(getByLabelText(type)).toBeVisible();
activeHealthChecksFormInputs.forEach((formLabel) => {
expect(getByLabelText(formLabel)).toBeVisible();
});
activeHealthChecksHelperText.forEach((helperText) => {
expect(getByText(helperText)).toBeVisible();
});
expect(queryByTestId('http-path')).not.toBeInTheDocument();
expect(queryByTestId('http-body')).not.toBeInTheDocument();
});

it('renders fields specific to the Active Health Check type of HTTP Status', () => {
const { getByLabelText, getByTestId, queryByTestId } = renderWithTheme(
<NodeBalancerConfigPanel {...props} healthCheckType="http" />
const {
getByLabelText,
getByTestId,
getByText,
queryByTestId,
} = renderWithTheme(
<NodeBalancerConfigPanel
{...nbConfigPanelMockPropsForTest}
healthCheckType="http"
/>
);

activeHealthChecks.forEach((type) => {
expect(getByLabelText(type)).toBeVisible();
activeHealthChecksFormInputs.forEach((formLabel) => {
expect(getByLabelText(formLabel)).toBeVisible();
});
activeHealthChecksHelperText.forEach((helperText) => {
expect(getByText(helperText)).toBeVisible();
});
expect(getByTestId('http-path')).toBeVisible();
expect(queryByTestId('http-body')).not.toBeInTheDocument();
});

it('renders fields specific to the Active Health Check type of HTTP Body', () => {
const { getByLabelText, getByTestId } = renderWithTheme(
<NodeBalancerConfigPanel {...props} healthCheckType="http_body" />
const { getByLabelText, getByTestId, getByText } = renderWithTheme(
<NodeBalancerConfigPanel
{...nbConfigPanelMockPropsForTest}
healthCheckType="http_body"
/>
);

activeHealthChecks.forEach((type) => {
expect(getByLabelText(type)).toBeVisible();
activeHealthChecksFormInputs.forEach((formLabel) => {
expect(getByLabelText(formLabel)).toBeVisible();
});
activeHealthChecksHelperText.forEach((helperText) => {
expect(getByText(helperText)).toBeVisible();
});
expect(getByTestId('http-path')).toBeVisible();
expect(getByTestId('http-body')).toBeVisible();
});

it('renders the relevant helper text for the Round Robin algorithm', () => {
const { getByText, queryByText } = renderWithTheme(
<NodeBalancerConfigPanel {...props} />
<NodeBalancerConfigPanel {...nbConfigPanelMockPropsForTest} />
);

expect(getByText(ROUND_ROBIN_ALGORITHM_HELPER_TEXT)).toBeVisible();
Expand All @@ -189,7 +234,10 @@ describe('NodeBalancerConfigPanel', () => {

it('renders the relevant helper text for the Least Connections algorithm', () => {
const { getByText, queryByText } = renderWithTheme(
<NodeBalancerConfigPanel {...props} algorithm={'leastconn'} />
<NodeBalancerConfigPanel
{...nbConfigPanelMockPropsForTest}
algorithm={'leastconn'}
/>
);

expect(getByText(LEAST_CONNECTIONS_ALGORITHM_HELPER_TEXT)).toBeVisible();
Expand All @@ -201,7 +249,10 @@ describe('NodeBalancerConfigPanel', () => {

it('renders the relevant helper text for the Source algorithm', () => {
const { getByText, queryByText } = renderWithTheme(
<NodeBalancerConfigPanel {...props} algorithm={'source'} />
<NodeBalancerConfigPanel
{...nbConfigPanelMockPropsForTest}
algorithm={'source'}
/>
);

expect(getByText(SOURCE_ALGORITHM_HELPER_TEXT)).toBeVisible();
Expand All @@ -215,49 +266,55 @@ describe('NodeBalancerConfigPanel', () => {

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

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

it('cannot remove a backend node if there is only one node', () => {
const { queryByText } = renderWithTheme(
<NodeBalancerConfigPanel {...props} />
<NodeBalancerConfigPanel {...nbConfigPanelMockPropsForTest} />
);

expect(queryByText('Remove')).not.toBeInTheDocument();
});

it('removes a backend node', () => {
const { getByText } = renderWithTheme(
<NodeBalancerConfigPanel {...props} nodes={[{ ...node }, node]} />
<NodeBalancerConfigPanel
{...nbConfigPanelMockPropsForTest}
nodes={[{ ...node }, node]}
/>
);

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

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

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

it('saves the input after editing the configuration', () => {
const { getByText } = renderWithTheme(
<NodeBalancerConfigPanel {...props} forEdit={true} />
<NodeBalancerConfigPanel
{...nbConfigPanelMockPropsForTest}
forEdit={true}
/>
);

const editConfigButton = getByText('Save');
fireEvent.click(editConfigButton);
expect(props.onSave).toHaveBeenCalled();
expect(nbConfigPanelMockPropsForTest.onSave).toHaveBeenCalled();
});
});
Loading

0 comments on commit 75c5986

Please sign in to comment.