-
Notifications
You must be signed in to change notification settings - Fork 666
CONSOLE-4605: Migrate OLM core "packages/operator-lifecycle-manager" unit tests to React Testing Library #15704
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,219 @@ | ||
| import * as React from 'react'; | ||
| import { screen } from '@testing-library/react'; | ||
| import * as _ from 'lodash'; | ||
| import * as Router from 'react-router-dom-v5-compat'; | ||
| import { DetailsPage } from '@console/internal/components/factory'; | ||
| import { Firehose } from '@console/internal/components/utils'; | ||
| import { useK8sWatchResource } from '@console/internal/components/utils/k8s-watch-hook'; | ||
| import { referenceForModel } from '@console/internal/module/k8s'; | ||
| import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils'; | ||
| import { testCatalogSource, testPackageManifest, dummyPackageManifest } from '../../../mocks'; | ||
| import { CatalogSourceModel, PackageManifestModel } from '../../models'; | ||
| import { | ||
| CatalogSourceDetails, | ||
| CatalogSourceDetailsPage, | ||
| CreateSubscriptionYAML, | ||
| CatalogSourceOperatorsPage, | ||
| } from '../catalog-source'; | ||
|
|
||
| jest.mock('@console/internal/components/utils/k8s-watch-hook', () => ({ | ||
| useK8sWatchResource: jest.fn(), | ||
| })); | ||
|
|
||
| jest.mock('react-router-dom-v5-compat', () => ({ | ||
| ...jest.requireActual('react-router-dom-v5-compat'), | ||
| useParams: jest.fn(), | ||
| useLocation: jest.fn(), | ||
| })); | ||
|
|
||
| jest.mock('@console/internal/components/factory', () => ({ | ||
| DetailsPage: jest.fn(() => null), | ||
| Table: jest.fn(() => null), | ||
| TableData: jest.fn(({ children }) => children), | ||
| MultiListPage: jest.fn(() => null), | ||
| })); | ||
|
|
||
| jest.mock('@console/internal/components/utils', () => ({ | ||
| ...jest.requireActual('@console/internal/components/utils'), | ||
| Firehose: jest.fn(({ children }) => { | ||
| const props = { packageManifest: { loaded: false } }; | ||
| return typeof children === 'function' ? children(props) : children; | ||
| }), | ||
| LoadingBox: jest.fn(() => 'Loading...'), | ||
| ResourceSummary: jest.fn(() => null), | ||
| SectionHeading: jest.fn(() => null), | ||
| DetailsItem: jest.fn(({ obj, path, children }) => { | ||
| if (children) return children; | ||
| if (path) { | ||
| const value = path.split('.').reduce((acc, key) => acc?.[key], obj); | ||
| return value || null; | ||
| } | ||
| return null; | ||
| }), | ||
| })); | ||
|
|
||
| jest.mock('@console/internal/components/create-yaml', () => ({ | ||
| CreateYAML: jest.fn(({ template }) => template), | ||
| })); | ||
|
|
||
| jest.mock('../operator-group', () => ({ | ||
| requireOperatorGroup: jest.fn((component) => component), | ||
| })); | ||
|
|
||
| jest.mock('@console/shared/src/components/error', () => ({ | ||
| ErrorBoundary: jest.fn(({ children }) => children), | ||
| withFallback: jest.fn((successComponent) => (props) => successComponent(props)), | ||
| })); | ||
|
|
||
| jest.mock('../package-manifest', () => ({ | ||
| PackageManifestsPage: jest.fn(() => null), | ||
| })); | ||
|
|
||
| jest.mock('../registry-poll-interval-details', () => ({ | ||
| RegistryPollIntervalDetailItem: jest.fn(() => null), | ||
| })); | ||
|
|
||
| jest.mock('@console/shared/src/components/layout/PaneBody', () => ({ | ||
| __esModule: true, | ||
| default: jest.fn(({ children }) => children), | ||
| })); | ||
|
|
||
| jest.mock('@patternfly/react-core', () => ({ | ||
| ...jest.requireActual('@patternfly/react-core'), | ||
| Grid: jest.fn(({ children }) => children), | ||
| GridItem: jest.fn(({ children }) => children), | ||
| DescriptionList: jest.fn(({ children }) => children), | ||
| })); | ||
|
|
||
| const mockDetailsPage = (DetailsPage as unknown) as jest.Mock; | ||
| const mockFirehose = (Firehose as unknown) as jest.Mock; | ||
| const mockUseK8sWatchResource = useK8sWatchResource as jest.Mock; | ||
|
|
||
| describe('CatalogSourceDetails', () => { | ||
| let obj; | ||
|
|
||
| beforeEach(() => { | ||
| obj = _.cloneDeep(testCatalogSource); | ||
| }); | ||
|
|
||
| it('displays catalog source name and publisher', () => { | ||
| renderWithProviders( | ||
| <CatalogSourceDetails obj={obj} packageManifests={[testPackageManifest]} />, | ||
| ); | ||
|
|
||
| expect(screen.getByText(obj.spec.displayName, { exact: false })).toBeVisible(); | ||
| expect(screen.getByText(obj.spec.publisher, { exact: false })).toBeVisible(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('CatalogSourceDetailsPage', () => { | ||
| beforeEach(() => { | ||
| mockUseK8sWatchResource.mockReturnValue([dummyPackageManifest, true, null]); | ||
| jest.spyOn(Router, 'useParams').mockReturnValue({ ns: 'default', name: 'some-catalog' }); | ||
| mockDetailsPage.mockClear(); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| jest.restoreAllMocks(); | ||
| }); | ||
|
|
||
| it('renders catalog source details page without errors', () => { | ||
| expect(() => { | ||
| renderWithProviders(<CatalogSourceDetailsPage />); | ||
| }).not.toThrow(); | ||
| }); | ||
|
|
||
| // TODO: Refactor to test user behavior instead of implementation details | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let remove this comment since it is captured in the Extend RTL tests story. |
||
| it('configures DetailsPage with correct navigation and resources', () => { | ||
| renderWithProviders(<CatalogSourceDetailsPage />); | ||
|
|
||
| expect(mockDetailsPage).toHaveBeenCalledTimes(1); | ||
| const [detailsPageProps] = mockDetailsPage.mock.calls[0]; | ||
|
|
||
| expect(detailsPageProps.kind).toEqual(referenceForModel(CatalogSourceModel)); | ||
|
|
||
| expect(detailsPageProps.pages).toHaveLength(3); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This tests implementation details (how DetailsPage is called), not user behavior. I would make it a follow-up in the story to extend RTL tests.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Opened follow-on story |
||
| expect(detailsPageProps.pages[0]).toMatchObject({ | ||
| nameKey: 'public~Details', | ||
| component: CatalogSourceDetails, | ||
| }); | ||
| expect(detailsPageProps.pages[1]).toMatchObject({ | ||
| nameKey: 'public~YAML', | ||
| }); | ||
| expect(detailsPageProps.pages[2]).toMatchObject({ | ||
| nameKey: 'olm~Operators', | ||
| component: CatalogSourceOperatorsPage, | ||
| }); | ||
|
|
||
| expect(detailsPageProps.resources).toEqual([ | ||
| { | ||
| kind: referenceForModel(PackageManifestModel), | ||
| isList: true, | ||
| prop: 'packageManifests', | ||
| namespace: 'default', | ||
| }, | ||
| ]); | ||
| }); | ||
| }); | ||
|
|
||
| describe('CreateSubscriptionYAML', () => { | ||
| beforeEach(() => { | ||
| jest.spyOn(Router, 'useParams').mockReturnValue({ | ||
| ns: 'default', | ||
| pkgName: testPackageManifest.metadata.name, | ||
| }); | ||
| jest.spyOn(Router, 'useLocation').mockReturnValue({ | ||
| ...window.location, | ||
| search: `?pkg=${testPackageManifest.metadata.name}&catalog=ocs&catalogNamespace=default`, | ||
| }); | ||
| mockFirehose.mockClear(); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| jest.restoreAllMocks(); | ||
| }); | ||
|
|
||
| it('displays package name in the subscription YAML when loaded', () => { | ||
| mockFirehose.mockImplementationOnce((firehoseProps) => { | ||
| const childElement = firehoseProps.children; | ||
| return React.cloneElement(childElement, { | ||
| packageManifest: { loaded: true, data: testPackageManifest }, | ||
| operatorGroup: { loaded: true, data: [] }, | ||
| }); | ||
| }); | ||
|
|
||
| renderWithProviders(<CreateSubscriptionYAML />); | ||
|
|
||
| expect(screen.getByText(new RegExp(testPackageManifest.metadata.name))).toBeVisible(); | ||
| }); | ||
|
|
||
| it('displays loading indicator when package manifest is not yet loaded', () => { | ||
| mockFirehose.mockImplementationOnce((firehoseProps) => { | ||
| const childElement = firehoseProps.children; | ||
| return React.cloneElement(childElement, { | ||
| packageManifest: { loaded: false }, | ||
| operatorGroup: { loaded: false }, | ||
| }); | ||
| }); | ||
|
|
||
| renderWithProviders(<CreateSubscriptionYAML />); | ||
|
|
||
| expect(screen.getByText('Loading...')).toBeVisible(); | ||
| }); | ||
|
|
||
| it('displays subscription YAML with default channel information', () => { | ||
| mockFirehose.mockImplementationOnce((firehoseProps) => { | ||
| const childElement = firehoseProps.children; | ||
| return React.cloneElement(childElement, { | ||
| packageManifest: { loaded: true, data: testPackageManifest }, | ||
| operatorGroup: { loaded: true, data: [] }, | ||
| }); | ||
| }); | ||
|
|
||
| renderWithProviders(<CreateSubscriptionYAML />); | ||
|
|
||
| expect(screen.getByText(/channel:\s*alpha/)).toBeInTheDocument(); | ||
| expect(screen.getByText(/source:\s*ocs/)).toBeInTheDocument(); | ||
| expect(screen.getByText(/startingCSV:\s*testapp/)).toBeInTheDocument(); | ||
| }); | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing
afterEach(jest.restoreAllMocks())after usingjest.spyOn()