Skip to content

Commit

Permalink
test: [M3-8555] - Add unit tests for NodeBalancersLanding package (li…
Browse files Browse the repository at this point in the history
…node#10911)

* add tests for nb action menu

* tests for nodebalancer row

* empty state test

* update landing state test

* node balancer landing unit test

* update comments

* Added changeset: Add unit tests for NodeBalancersLanding package

* move delete test to row
  • Loading branch information
coliu-akamai authored Sep 11, 2024
1 parent 4827144 commit 4d8a241
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 4 deletions.
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-10911-tests-1725912717549.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Tests
---

Add unit tests for NodeBalancersLanding package ([#10911](https://github.com/linode/manager/pull/10911))
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { fireEvent } from '@testing-library/react';
import * as React from 'react';

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

import { NodeBalancerActionMenu } from './NodeBalancerActionMenu';

const props = {
label: 'nodebalancer-1',
nodeBalancerId: 1,
toggleDialog: vi.fn(),
};

describe('NodeBalancerActionMenu', () => {
afterEach(() => {
vi.resetAllMocks();
});

it('renders the NodeBalancerActionMenu', () => {
const { getByText } = renderWithTheme(
<NodeBalancerActionMenu {...props} />
);

expect(getByText('Configurations')).toBeVisible();
expect(getByText('Settings')).toBeVisible();
expect(getByText('Delete')).toBeVisible();
});

it('triggers the action to delete the NodeBalancer', () => {
const { getByText } = renderWithTheme(
<NodeBalancerActionMenu {...props} />
);

const deleteButton = getByText('Delete');
fireEvent.click(deleteButton);
expect(props.toggleDialog).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { Theme, useTheme } from '@mui/material/styles';
import { useTheme } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
import * as React from 'react';
import { useHistory } from 'react-router-dom';

import { Action, ActionMenu } from 'src/components/ActionMenu/ActionMenu';
import { ActionMenu } from 'src/components/ActionMenu/ActionMenu';
import { Hidden } from 'src/components/Hidden';
import { InlineMenuAction } from 'src/components/InlineMenuAction/InlineMenuAction';
import { getRestrictedResourceText } from 'src/features/Account/utils';
import { useIsResourceRestricted } from 'src/hooks/useIsResourceRestricted';

import type { Theme } from '@mui/material/styles';
import type { Action } from 'src/components/ActionMenu/ActionMenu';

interface Props {
label: string;
nodeBalancerId: number;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { fireEvent } from '@testing-library/react';
import * as React from 'react';

import { nodeBalancerFactory } from 'src/factories';
import { breakpoints } from 'src/foundations/breakpoints';
import { useIsResourceRestricted } from 'src/hooks/useIsResourceRestricted';
import { renderWithTheme, resizeScreenSize } from 'src/utilities/testHelpers';

import { NodeBalancerTableRow } from './NodeBalancerTableRow';

vi.mock('src/hooks/useIsResourceRestricted');

const props = {
...nodeBalancerFactory.build(),
onDelete: vi.fn(),
};

describe('NodeBalancerTableRow', () => {
beforeEach(() => {
vi.resetAllMocks();
});

it('renders the NodeBalancer table row', () => {
const { getByText } = renderWithTheme(<NodeBalancerTableRow {...props} />);

expect(getByText('nodebalancer-id-1')).toBeVisible();
expect(getByText('0.0.0.0')).toBeVisible();
expect(getByText('Configurations')).toBeVisible();
expect(getByText('Settings')).toBeVisible();
expect(getByText('Delete')).toBeVisible();
});

it('renders the hidden columns when the screen width is large enough', () => {
resizeScreenSize(breakpoints.values.lg);
const { getByText } = renderWithTheme(<NodeBalancerTableRow {...props} />);

expect(getByText('nodebalancer-id-1')).toBeVisible();
expect(getByText('0 up')).toBeVisible();
expect(getByText('0 down')).toBeVisible();
expect(getByText('0 bytes')).toBeVisible();
expect(getByText('0.0.0.0')).toBeVisible();
expect(getByText('us-east')).toBeVisible();
});

it('deletes the NodeBalancer', () => {
const { getByText } = renderWithTheme(<NodeBalancerTableRow {...props} />);

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

it('does not delete the NodeBalancer if the delete button is disabled', () => {
vi.mocked(useIsResourceRestricted).mockReturnValue(true);
const { getByText } = renderWithTheme(<NodeBalancerTableRow {...props} />);

const deleteButton = getByText('Delete');
fireEvent.click(deleteButton);
expect(props.onDelete).not.toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { NodeBalancer } from '@linode/api-v4/lib/nodebalancers';
import * as React from 'react';
import { Link } from 'react-router-dom';

Expand All @@ -13,6 +12,8 @@ import { convertMegabytesTo } from 'src/utilities/unitConversions';

import { NodeBalancerActionMenu } from './NodeBalancerActionMenu';

import type { NodeBalancer } from '@linode/api-v4/lib/nodebalancers';

interface Props extends NodeBalancer {
onDelete: () => void;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { waitForElementToBeRemoved } from '@testing-library/react';
import * as React from 'react';

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

import { NodeBalancersLanding } from './NodeBalancersLanding';

beforeAll(() => mockMatchMedia());

const loadingTestId = 'circle-progress';

describe('NodeBalancersLanding', () => {
it('renders the NodeBalancer empty state if there are no NodeBalancers', async () => {
server.use(
http.get('*/nodebalancers', () => {
return HttpResponse.json(makeResourcePage([]));
})
);

const { getByTestId, getByText } = renderWithTheme(
<NodeBalancersLanding />
);

// expect loading state and wait for it to disappear
expect(getByTestId(loadingTestId)).toBeInTheDocument();
await waitForElementToBeRemoved(getByTestId(loadingTestId));

expect(getByText('NodeBalancers')).toBeVisible();
expect(getByText('Cloud-based load balancing service')).toBeVisible();
expect(
getByText(
'Add high availability and horizontal scaling to web applications hosted on Linode Compute Instances.'
)
).toBeVisible();
});

it('renders the NodeBalancer table if there are NodeBalancers', async () => {
server.use(
http.get('*/nodebalancers', () => {
const nodebalancers = nodeBalancerFactory.buildList(1);
return HttpResponse.json(makeResourcePage(nodebalancers));
})
);

const { getByTestId, getByText } = renderWithTheme(
<NodeBalancersLanding />
);

// expect loading state and wait for it to disappear
expect(getByTestId(loadingTestId)).toBeInTheDocument();
await waitForElementToBeRemoved(getByTestId(loadingTestId));

expect(getByText('NodeBalancers')).toBeVisible();
expect(getByText('Create NodeBalancer')).toBeVisible();

// confirm table headers
expect(getByText('Label')).toBeVisible();
expect(getByText('Backend Status')).toBeVisible();
expect(getByText('Transferred')).toBeVisible();
expect(getByText('Ports')).toBeVisible();
expect(getByText('IP Address')).toBeVisible();
expect(getByText('Region')).toBeVisible();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGran
import { useNodeBalancersQuery } from 'src/queries/nodebalancers';

import { NodeBalancerDeleteDialog } from '../NodeBalancerDeleteDialog';
import { NodeBalancerTableRow } from './NodeBalancerTableRow';
import { NodeBalancerLandingEmptyState } from './NodeBalancersLandingEmptyState';
import { NodeBalancerTableRow } from './NodeBalancerTableRow';
const preferenceKey = 'nodebalancers';

export const NodeBalancersLanding = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { waitFor } from '@testing-library/react';
import * as React from 'react';

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

import { NodeBalancerLandingEmptyState } from './NodeBalancersLandingEmptyState';

vi.mock('src/hooks/useRestrictedGlobalGrantCheck');

// Note: An integration test confirming the helper text and enabled Create NodeBalancer button already exists, so we're just checking for a disabled create button here
describe('NodeBalancersLandingEmptyState', () => {
afterEach(() => {
vi.resetAllMocks();
});

it('disables the Create NodeBalancer button if user does not have permissions to create a NodeBalancer', async () => {
// disables the create button
vi.mocked(useRestrictedGlobalGrantCheck).mockReturnValue(true);

const { getByText } = renderWithTheme(<NodeBalancerLandingEmptyState />);

await waitFor(() => {
const createNodeBalancerButton = getByText('Create NodeBalancer').closest(
'button'
);

expect(createNodeBalancerButton).toBeDisabled();
expect(createNodeBalancerButton).toHaveAttribute(
'data-qa-tooltip',
"You don't have permissions to create NodeBalancers. Please contact your account administrator to request the necessary permissions."
);
});
});
});

0 comments on commit 4d8a241

Please sign in to comment.