-
Notifications
You must be signed in to change notification settings - Fork 692
CONSOLE-4809: Extend RTL Test Coverage for Components Migrated from Enzyme #16249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
cajieh
wants to merge
1
commit into
openshift:main
Choose a base branch
from
cajieh:extend-rtl-ests-overage-omponents-igrated-enzyme
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,009
−410
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
240 changes: 187 additions & 53 deletions
240
.../console-app/src/components/modals/resource-limits/__tests__/ResourceLimitsModal.spec.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,85 +1,219 @@ | ||
| import type { ComponentProps } from 'react'; | ||
| import { screen, fireEvent } from '@testing-library/react'; | ||
| import type { FormikProps, FormikValues } from 'formik'; | ||
| import { formikFormProps } from '@console/shared/src/test-utils/formik-props-utils'; | ||
| import { screen, waitFor } from '@testing-library/react'; | ||
| import userEvent from '@testing-library/user-event'; | ||
| import { Formik } from 'formik'; | ||
| import type { ObjectSchema } from 'yup'; | ||
| import * as yup from 'yup'; | ||
| import { limitsValidationSchema } from '@console/dev-console/src/components/import/validation-schema'; | ||
| import type { K8sResourceKind } from '@console/internal/module/k8s'; | ||
| import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils'; | ||
| import { getLimitsDataFromResource } from '@console/shared/src/utils/resource-utils'; | ||
| import { t } from '../../../../../../../__mocks__/i18next'; | ||
| import ResourceLimitsModal from '../ResourceLimitsModal'; | ||
|
|
||
| jest.mock('@console/dev-console/src/components/import/advanced/ResourceLimitSection', () => ({ | ||
| default: () => null, | ||
| })); | ||
| jest.mock('@patternfly/react-topology', () => ({})); | ||
|
|
||
| type ResourceLimitsModalProps = ComponentProps<typeof ResourceLimitsModal>; | ||
| const emptyLimits = { | ||
| cpu: { | ||
| request: '', | ||
| requestUnit: '', | ||
| defaultRequestUnit: '', | ||
| limit: '', | ||
| limitUnit: '', | ||
| defaultLimitUnit: '', | ||
| }, | ||
| memory: { | ||
| request: '', | ||
| requestUnit: 'Mi', | ||
| defaultRequestUnit: 'Mi', | ||
| limit: '', | ||
| limitUnit: 'Mi', | ||
| defaultLimitUnit: 'Mi', | ||
| }, | ||
| }; | ||
|
|
||
| describe('ResourceLimitsModal Form', () => { | ||
| let formProps: ResourceLimitsModalProps; | ||
| const resourceLimitsSchema = yup.object().shape({ | ||
| limits: limitsValidationSchema(t), | ||
| }); | ||
|
|
||
| type Props = FormikProps<FormikValues> & ResourceLimitsModalProps; | ||
| const limitsFormValues = { | ||
| limits: emptyLimits, | ||
| container: 'hello-openshift', | ||
| }; | ||
|
|
||
| beforeEach(() => { | ||
| jest.clearAllMocks(); | ||
| formProps = { | ||
| ...formikFormProps, | ||
| isSubmitting: false, | ||
| cancel: jest.fn(), | ||
| resource: { | ||
| apiVersion: 'apps/v1', | ||
| kind: 'Deployment', | ||
| const baseDeployment = (): K8sResourceKind => | ||
| ({ | ||
| apiVersion: 'apps/v1', | ||
| kind: 'Deployment', | ||
| metadata: { | ||
| name: 'xyz-deployment', | ||
| }, | ||
| spec: { | ||
| selector: { | ||
| matchLabels: { | ||
| app: 'hello-openshift', | ||
| }, | ||
| }, | ||
| replicas: 1, | ||
| template: { | ||
| metadata: { | ||
| name: 'xyz-deployment', | ||
| labels: { | ||
| app: 'hello-openshift', | ||
| }, | ||
| }, | ||
| spec: { | ||
| selector: { | ||
| matchLabels: { | ||
| app: 'hello-openshift', | ||
| }, | ||
| }, | ||
| replicas: 1, | ||
| template: { | ||
| metadata: { | ||
| labels: { | ||
| app: 'hello-openshift', | ||
| }, | ||
| }, | ||
| spec: { | ||
| containers: [ | ||
| containers: [ | ||
| { | ||
| name: 'hello-openshift', | ||
| image: 'openshift/hello-openshift', | ||
| ports: [ | ||
| { | ||
| name: 'hello-openshift', | ||
| image: 'openshift/hello-openshift', | ||
| ports: [ | ||
| { | ||
| containerPort: 8080, | ||
| }, | ||
| ], | ||
| containerPort: 8080, | ||
| }, | ||
| ], | ||
| }, | ||
| }, | ||
| ], | ||
| }, | ||
| }, | ||
| } as Props; | ||
| }, | ||
| } as K8sResourceKind); | ||
|
|
||
| type RenderResourceLimitsModalOptions = { | ||
| resource?: K8sResourceKind; | ||
| validationSchema?: ObjectSchema<unknown>; | ||
| onSubmit?: jest.Mock; | ||
| cancel?: jest.Mock; | ||
| }; | ||
|
|
||
| const renderResourceLimitsModal = ({ | ||
| resource, | ||
| validationSchema, | ||
| onSubmit: onSubmitOption, | ||
| cancel: cancelOption, | ||
| }: RenderResourceLimitsModalOptions = {}) => { | ||
| const onSubmit = onSubmitOption ?? jest.fn(); | ||
| const cancel = cancelOption ?? jest.fn(); | ||
| const initialValues = resource | ||
| ? { | ||
| limits: getLimitsDataFromResource(resource), | ||
| container: resource.spec.template.spec.containers[0].name, | ||
| } | ||
| : limitsFormValues; | ||
|
|
||
| return { | ||
| onSubmit, | ||
| cancel, | ||
| ...renderWithProviders( | ||
| <Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={onSubmit}> | ||
| {(formikProps) => ( | ||
| <ResourceLimitsModal {...formikProps} cancel={cancel} isSubmitting={false} /> | ||
| )} | ||
| </Formik>, | ||
| ), | ||
| }; | ||
| }; | ||
|
|
||
| describe('ResourceLimitsModal Form', () => { | ||
| beforeEach(() => { | ||
| jest.clearAllMocks(); | ||
| }); | ||
|
|
||
| it('renders the modal with the correct title and initial elements', () => { | ||
| renderWithProviders(<ResourceLimitsModal {...formProps} />); | ||
| it('renders the modal title', () => { | ||
| renderResourceLimitsModal(); | ||
|
|
||
| expect(screen.getByText('Edit resource limits')).toBeVisible(); | ||
| }); | ||
|
|
||
| it('renders the form with Cancel and Save actions', () => { | ||
| renderResourceLimitsModal(); | ||
|
|
||
| expect(screen.getByRole('form')).toBeVisible(); | ||
| expect(screen.getByRole('button', { name: 'Cancel' })).toBeVisible(); | ||
| expect(screen.getByRole('button', { name: 'Save' })).toBeVisible(); | ||
| }); | ||
|
|
||
| it('calls the cancel function when the Cancel button is clicked', async () => { | ||
| renderWithProviders(<ResourceLimitsModal {...formProps} />); | ||
| const user = userEvent.setup(); | ||
| const cancel = jest.fn(); | ||
| renderResourceLimitsModal({ cancel }); | ||
|
|
||
| await fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); | ||
| expect(formProps.cancel).toHaveBeenCalledTimes(1); | ||
| await user.click(screen.getByRole('button', { name: 'Cancel' })); | ||
| expect(cancel).toHaveBeenCalledTimes(1); | ||
| }); | ||
|
|
||
| it('calls the handleSubmit function when the form is submitted', async () => { | ||
| renderWithProviders(<ResourceLimitsModal {...formProps} />); | ||
| it('submits the form when Save is clicked', async () => { | ||
| const user = userEvent.setup(); | ||
| const onSubmit = jest.fn(); | ||
| renderResourceLimitsModal({ onSubmit }); | ||
|
|
||
| await user.click(screen.getByRole('button', { name: 'Save' })); | ||
| expect(onSubmit).toHaveBeenCalledTimes(1); | ||
| }); | ||
| }); | ||
|
|
||
| describe('ResourceLimitsModal with validation (resource limits schema)', () => { | ||
| beforeEach(() => { | ||
| jest.clearAllMocks(); | ||
| }); | ||
|
|
||
| it('populates CPU and Memory request/limit fields from the workload resource', () => { | ||
| const resource = baseDeployment(); | ||
| resource.spec.template.spec.containers[0].resources = { | ||
| requests: { cpu: '100m', memory: '128Mi' }, | ||
| limits: { cpu: '500m', memory: '256Mi' }, | ||
| }; | ||
|
|
||
| renderResourceLimitsModal({ resource, validationSchema: resourceLimitsSchema }); | ||
|
|
||
| expect(screen.getByDisplayValue('100')).toBeVisible(); | ||
| expect(screen.getByDisplayValue('500')).toBeVisible(); | ||
| expect(screen.getByDisplayValue('128')).toBeVisible(); | ||
| expect(screen.getByDisplayValue('256')).toBeVisible(); | ||
| }); | ||
|
|
||
| it('disables Save when CPU request is greater than CPU limit', async () => { | ||
| const user = userEvent.setup(); | ||
| const resource = baseDeployment(); | ||
| resource.spec.template.spec.containers[0].resources = { | ||
| requests: { cpu: '100m', memory: '128Mi' }, | ||
| limits: { cpu: '200m', memory: '256Mi' }, | ||
| }; | ||
|
|
||
| renderResourceLimitsModal({ resource, validationSchema: resourceLimitsSchema }); | ||
|
|
||
| const save = screen.getByRole('button', { name: 'Save' }); | ||
| expect(save).not.toBeDisabled(); | ||
|
|
||
| const cpuRequest = screen.getByRole('spinbutton', { name: 'CPU request' }); | ||
| await user.click(cpuRequest); | ||
| await user.keyboard('{Control>}a{/Control}'); | ||
| await user.keyboard('300'); | ||
|
|
||
| await waitFor(() => expect(save).toBeDisabled()); | ||
| expect( | ||
| await screen.findByText('CPU request must be less than or equal to limit.'), | ||
| ).toBeVisible(); | ||
| }); | ||
|
|
||
| it('disables Save when Memory request is greater than Memory limit', async () => { | ||
| const user = userEvent.setup(); | ||
| const resource = baseDeployment(); | ||
| resource.spec.template.spec.containers[0].resources = { | ||
| requests: { cpu: '100m', memory: '128Mi' }, | ||
| limits: { cpu: '500m', memory: '256Mi' }, | ||
| }; | ||
|
|
||
| renderResourceLimitsModal({ resource, validationSchema: resourceLimitsSchema }); | ||
|
|
||
| const save = screen.getByRole('button', { name: 'Save' }); | ||
| const memoryRequest = screen.getByRole('spinbutton', { name: 'Memory request' }); | ||
|
|
||
| await user.click(memoryRequest); | ||
| await user.keyboard('{Control>}a{/Control}'); | ||
| await user.keyboard('512'); | ||
|
|
||
| await fireEvent.submit(screen.getByRole('form')); | ||
| expect(formProps.handleSubmit).toHaveBeenCalledTimes(1); | ||
| await waitFor(() => expect(save).toBeDisabled()); | ||
| expect( | ||
| await screen.findByText('Memory request must be less than or equal to limit.'), | ||
| ).toBeVisible(); | ||
| }); | ||
| }); | ||
84 changes: 67 additions & 17 deletions
84
frontend/packages/console-shared/src/components/empty-state/__tests__/empty-state.spec.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,33 +1,83 @@ | ||
| import { render } from '@testing-library/react'; | ||
| import { Button } from '@patternfly/react-core'; | ||
| import { screen } from '@testing-library/react'; | ||
| import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils'; | ||
| import { AccessDenied, EmptyBox, ConsoleEmptyState } from '..'; | ||
|
|
||
| const TestIcon = () => 'TestIcon'; | ||
|
|
||
| describe('EmptyBox', () => { | ||
| it('should render without label', () => { | ||
| const { getByText } = render(<EmptyBox />); | ||
| getByText('Not found'); | ||
| it('renders default "Not found" message without label', () => { | ||
| renderWithProviders(<EmptyBox />); | ||
| expect(screen.getByText('Not found')).toBeVisible(); | ||
| }); | ||
|
|
||
| it('should render with label', () => { | ||
| const { getByText } = render(<EmptyBox label="test-label" />); | ||
| getByText('No test-label found'); | ||
| it('renders message with label when provided', () => { | ||
| renderWithProviders(<EmptyBox label="resources" />); | ||
| expect(screen.getByText('No resources found')).toBeVisible(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('MsgBox', () => { | ||
| it('should render title', () => { | ||
| const { getByText } = render(<ConsoleEmptyState title="test-title" />); | ||
| getByText('test-title'); | ||
| describe('ConsoleEmptyState', () => { | ||
| it('renders title and children in body', () => { | ||
| renderWithProviders( | ||
| <ConsoleEmptyState title="Empty State Title">Body content</ConsoleEmptyState>, | ||
| ); | ||
| expect(screen.getByText('Empty State Title')).toBeVisible(); | ||
| expect(screen.getByText('Body content')).toBeVisible(); | ||
| }); | ||
|
|
||
| it('renders Icon when provided', () => { | ||
| renderWithProviders(<ConsoleEmptyState Icon={TestIcon} title="With Icon" />); | ||
| expect(screen.getByText('TestIcon')).toBeVisible(); | ||
| }); | ||
|
|
||
| it('should render children', () => { | ||
| const { getByText } = render(<ConsoleEmptyState>test-child</ConsoleEmptyState>); | ||
| getByText('test-child'); | ||
| it('renders primary and secondary actions when provided', () => { | ||
| const primaryActions = [<Button key="create">Create Resource</Button>]; | ||
| const secondaryActions = [ | ||
| <Button key="learn" variant="link"> | ||
| Learn more | ||
| </Button>, | ||
| ]; | ||
| renderWithProviders( | ||
| <ConsoleEmptyState | ||
| title="Test" | ||
| primaryActions={primaryActions} | ||
| secondaryActions={secondaryActions} | ||
| />, | ||
| ); | ||
| expect(screen.getByRole('button', { name: 'Create Resource' })).toBeVisible(); | ||
| expect(screen.getByRole('button', { name: 'Learn more' })).toBeVisible(); | ||
| }); | ||
|
|
||
| it('does not render body or footer when not provided', () => { | ||
| renderWithProviders(<ConsoleEmptyState title="No Body" />); | ||
| expect(screen.queryByTestId('console-empty-state-body')).not.toBeInTheDocument(); | ||
| expect(screen.queryByTestId('console-empty-state-footer')).not.toBeInTheDocument(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('AccessDenied', () => { | ||
| it('should render message', () => { | ||
| const { getByText } = render(<AccessDenied>test-message</AccessDenied>); | ||
| getByText('test-message'); | ||
| it('renders restricted access title and message', () => { | ||
| renderWithProviders(<AccessDenied />); | ||
| expect(screen.getByText('Restricted access')).toBeVisible(); | ||
| expect( | ||
| screen.getByText("You don't have access to this section due to cluster policy"), | ||
| ).toBeVisible(); | ||
| }); | ||
|
|
||
| it('renders error details alert when children provided', () => { | ||
| renderWithProviders(<AccessDenied>Permission denied for resource xyz</AccessDenied>); | ||
| expect(screen.getByText('Error details')).toBeVisible(); | ||
| expect(screen.getByText('Permission denied for resource xyz')).toBeVisible(); | ||
| }); | ||
|
|
||
| it('does not render error alert when no children provided', () => { | ||
| renderWithProviders(<AccessDenied />); | ||
| expect(screen.queryByText('Error details')).not.toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('renders restricted sign icon', () => { | ||
| renderWithProviders(<AccessDenied />); | ||
| expect(screen.getByAltText('Restricted access')).toBeVisible(); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.