Skip to content

Commit 057ffde

Browse files
Merge pull request #15704 from sg00dwin/CONSOLE-4605-migration-to-RTL
CONSOLE-4605: Migrate OLM core "packages/operator-lifecycle-manager" unit tests to React Testing Library
2 parents f2eb83d + e19a479 commit 057ffde

21 files changed

+2474
-2919
lines changed
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import * as React from 'react';
2+
import { screen } from '@testing-library/react';
3+
import * as _ from 'lodash';
4+
import * as Router from 'react-router-dom-v5-compat';
5+
import { DetailsPage } from '@console/internal/components/factory';
6+
import { Firehose } from '@console/internal/components/utils';
7+
import { useK8sWatchResource } from '@console/internal/components/utils/k8s-watch-hook';
8+
import { referenceForModel } from '@console/internal/module/k8s';
9+
import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils';
10+
import { testCatalogSource, testPackageManifest, dummyPackageManifest } from '../../../mocks';
11+
import { CatalogSourceModel, PackageManifestModel } from '../../models';
12+
import {
13+
CatalogSourceDetails,
14+
CatalogSourceDetailsPage,
15+
CreateSubscriptionYAML,
16+
CatalogSourceOperatorsPage,
17+
} from '../catalog-source';
18+
19+
jest.mock('@console/internal/components/utils/k8s-watch-hook', () => ({
20+
useK8sWatchResource: jest.fn(),
21+
}));
22+
23+
jest.mock('react-router-dom-v5-compat', () => ({
24+
...jest.requireActual('react-router-dom-v5-compat'),
25+
useParams: jest.fn(),
26+
useLocation: jest.fn(),
27+
}));
28+
29+
jest.mock('@console/internal/components/factory', () => ({
30+
DetailsPage: jest.fn(() => null),
31+
Table: jest.fn(() => null),
32+
TableData: jest.fn(({ children }) => children),
33+
MultiListPage: jest.fn(() => null),
34+
}));
35+
36+
jest.mock('@console/internal/components/utils', () => ({
37+
...jest.requireActual('@console/internal/components/utils'),
38+
Firehose: jest.fn(({ children }) => {
39+
const props = { packageManifest: { loaded: false } };
40+
return typeof children === 'function' ? children(props) : children;
41+
}),
42+
LoadingBox: jest.fn(() => 'Loading...'),
43+
ResourceSummary: jest.fn(() => null),
44+
SectionHeading: jest.fn(() => null),
45+
DetailsItem: jest.fn(({ obj, path, children }) => {
46+
if (children) return children;
47+
if (path) {
48+
const value = path.split('.').reduce((acc, key) => acc?.[key], obj);
49+
return value || null;
50+
}
51+
return null;
52+
}),
53+
}));
54+
55+
jest.mock('@console/internal/components/create-yaml', () => ({
56+
CreateYAML: jest.fn(({ template }) => template),
57+
}));
58+
59+
jest.mock('../operator-group', () => ({
60+
requireOperatorGroup: jest.fn((component) => component),
61+
}));
62+
63+
jest.mock('@console/shared/src/components/error', () => ({
64+
ErrorBoundary: jest.fn(({ children }) => children),
65+
withFallback: jest.fn((successComponent) => (props) => successComponent(props)),
66+
}));
67+
68+
jest.mock('../package-manifest', () => ({
69+
PackageManifestsPage: jest.fn(() => null),
70+
}));
71+
72+
jest.mock('../registry-poll-interval-details', () => ({
73+
RegistryPollIntervalDetailItem: jest.fn(() => null),
74+
}));
75+
76+
jest.mock('@console/shared/src/components/layout/PaneBody', () => ({
77+
__esModule: true,
78+
default: jest.fn(({ children }) => children),
79+
}));
80+
81+
jest.mock('@patternfly/react-core', () => ({
82+
...jest.requireActual('@patternfly/react-core'),
83+
Grid: jest.fn(({ children }) => children),
84+
GridItem: jest.fn(({ children }) => children),
85+
DescriptionList: jest.fn(({ children }) => children),
86+
}));
87+
88+
const mockDetailsPage = (DetailsPage as unknown) as jest.Mock;
89+
const mockFirehose = (Firehose as unknown) as jest.Mock;
90+
const mockUseK8sWatchResource = useK8sWatchResource as jest.Mock;
91+
92+
describe('CatalogSourceDetails', () => {
93+
let obj;
94+
95+
beforeEach(() => {
96+
obj = _.cloneDeep(testCatalogSource);
97+
});
98+
99+
it('displays catalog source name and publisher', () => {
100+
renderWithProviders(
101+
<CatalogSourceDetails obj={obj} packageManifests={[testPackageManifest]} />,
102+
);
103+
104+
expect(screen.getByText(obj.spec.displayName, { exact: false })).toBeVisible();
105+
expect(screen.getByText(obj.spec.publisher, { exact: false })).toBeVisible();
106+
});
107+
});
108+
109+
describe('CatalogSourceDetailsPage', () => {
110+
beforeEach(() => {
111+
mockUseK8sWatchResource.mockReturnValue([dummyPackageManifest, true, null]);
112+
jest.spyOn(Router, 'useParams').mockReturnValue({ ns: 'default', name: 'some-catalog' });
113+
mockDetailsPage.mockClear();
114+
});
115+
116+
afterEach(() => {
117+
jest.restoreAllMocks();
118+
});
119+
120+
it('renders catalog source details page without errors', () => {
121+
expect(() => {
122+
renderWithProviders(<CatalogSourceDetailsPage />);
123+
}).not.toThrow();
124+
});
125+
126+
// TODO: Refactor to test user behavior instead of implementation details
127+
it('configures DetailsPage with correct navigation and resources', () => {
128+
renderWithProviders(<CatalogSourceDetailsPage />);
129+
130+
expect(mockDetailsPage).toHaveBeenCalledTimes(1);
131+
const [detailsPageProps] = mockDetailsPage.mock.calls[0];
132+
133+
expect(detailsPageProps.kind).toEqual(referenceForModel(CatalogSourceModel));
134+
135+
expect(detailsPageProps.pages).toHaveLength(3);
136+
expect(detailsPageProps.pages[0]).toMatchObject({
137+
nameKey: 'public~Details',
138+
component: CatalogSourceDetails,
139+
});
140+
expect(detailsPageProps.pages[1]).toMatchObject({
141+
nameKey: 'public~YAML',
142+
});
143+
expect(detailsPageProps.pages[2]).toMatchObject({
144+
nameKey: 'olm~Operators',
145+
component: CatalogSourceOperatorsPage,
146+
});
147+
148+
expect(detailsPageProps.resources).toEqual([
149+
{
150+
kind: referenceForModel(PackageManifestModel),
151+
isList: true,
152+
prop: 'packageManifests',
153+
namespace: 'default',
154+
},
155+
]);
156+
});
157+
});
158+
159+
describe('CreateSubscriptionYAML', () => {
160+
beforeEach(() => {
161+
jest.spyOn(Router, 'useParams').mockReturnValue({
162+
ns: 'default',
163+
pkgName: testPackageManifest.metadata.name,
164+
});
165+
jest.spyOn(Router, 'useLocation').mockReturnValue({
166+
...window.location,
167+
search: `?pkg=${testPackageManifest.metadata.name}&catalog=ocs&catalogNamespace=default`,
168+
});
169+
mockFirehose.mockClear();
170+
});
171+
172+
afterEach(() => {
173+
jest.restoreAllMocks();
174+
});
175+
176+
it('displays package name in the subscription YAML when loaded', () => {
177+
mockFirehose.mockImplementationOnce((firehoseProps) => {
178+
const childElement = firehoseProps.children;
179+
return React.cloneElement(childElement, {
180+
packageManifest: { loaded: true, data: testPackageManifest },
181+
operatorGroup: { loaded: true, data: [] },
182+
});
183+
});
184+
185+
renderWithProviders(<CreateSubscriptionYAML />);
186+
187+
expect(screen.getByText(new RegExp(testPackageManifest.metadata.name))).toBeVisible();
188+
});
189+
190+
it('displays loading indicator when package manifest is not yet loaded', () => {
191+
mockFirehose.mockImplementationOnce((firehoseProps) => {
192+
const childElement = firehoseProps.children;
193+
return React.cloneElement(childElement, {
194+
packageManifest: { loaded: false },
195+
operatorGroup: { loaded: false },
196+
});
197+
});
198+
199+
renderWithProviders(<CreateSubscriptionYAML />);
200+
201+
expect(screen.getByText('Loading...')).toBeVisible();
202+
});
203+
204+
it('displays subscription YAML with default channel information', () => {
205+
mockFirehose.mockImplementationOnce((firehoseProps) => {
206+
const childElement = firehoseProps.children;
207+
return React.cloneElement(childElement, {
208+
packageManifest: { loaded: true, data: testPackageManifest },
209+
operatorGroup: { loaded: true, data: [] },
210+
});
211+
});
212+
213+
renderWithProviders(<CreateSubscriptionYAML />);
214+
215+
expect(screen.getByText(/channel:\s*alpha/)).toBeInTheDocument();
216+
expect(screen.getByText(/source:\s*ocs/)).toBeInTheDocument();
217+
expect(screen.getByText(/startingCSV:\s*testapp/)).toBeInTheDocument();
218+
});
219+
});

0 commit comments

Comments
 (0)