Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
"flag": "OPERATOR_LIFECYCLE_MANAGER"
}
},
{
"type": "console.flag",
"properties": {
"handler": { "$codeRef": "features.detectLifecycleMetadata" }
}
},
{
"type": "console.model-metadata",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@
"Status": "Status",
"Last updated": "Last updated",
"Provided APIs": "Provided APIs",
"Cluster compatibility": "Cluster compatibility",
"Support phase": "Support phase",
"Support phase ends": "Support phase ends",
"Installed Operators": "Installed Operators",
"Operator": "Operator",
"Installed Operators are represented by ClusterServiceVersions within this Namespace.": "Installed Operators are represented by ClusterServiceVersions within this Namespace.",
" For more information, see the <3>Understanding Operators documentation</3>. Or create an Operator and ClusterServiceVersion using the <6>Operator SDK</6>.": " For more information, see the <3>Understanding Operators documentation</3>. Or create an Operator and ClusterServiceVersion using the <6>Operator SDK</6>.",
"Required": "Required",
Expand Down Expand Up @@ -146,7 +150,6 @@
"Confirm change": "Confirm change",
"An error occurred": "An error occurred",
"Key": "Key",
"Operator": "Operator",
"Values": "Values",
"Match expressions": "Match expressions",
"Add expression": "Add expression",
Expand Down Expand Up @@ -372,6 +375,11 @@
"Error: {{loadError}}": "Error: {{loadError}}",
"Failed to load installation status": "Failed to load installation status",
"Installing...": "Installing...",
"Compatible": "Compatible",
"Incompatible": "Incompatible",
"Lifecycle dates": "Lifecycle dates",
"May not reflect your actual SKU. Check your actual SKU for extended support.": "May not reflect your actual SKU. Check your actual SKU for extended support.",
"Self-support": "Self-support",
"No PackageManifests Found": "No PackageManifests Found",
"The CatalogSource author has not added any packages.": "The CatalogSource author has not added any packages.",
"Catalogs are groups of Operators you can make available on the cluster. Use the <2>Software Catalog</2> to subscribe and grant namespaces access to use installed Operators.": "Catalogs are groups of Operators you can make available on the cluster. Use the <2>Software Catalog</2> to subscribe and grant namespaces access to use installed Operators.",
Expand Down
3 changes: 2 additions & 1 deletion frontend/packages/operator-lifecycle-manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"catalogSourceActionsProvider": "src/actions/providers/catalog-source-provider.ts",
"useOperatorCatalogCategories": "src/hooks/useOperatorCatalogCategories.ts",
"useOperatorCatalogItems": "src/hooks/useOperatorCatalogItems.tsx",
"utils": "src/components/dashboard/utils.ts"
"utils": "src/components/dashboard/utils.ts",
"features": "src/features.ts"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
import { render, screen } from '@testing-library/react';
import type { LifecycleData } from '../operator-lifecycle-status';
import {
getClusterCompatibility,
getSupportPhase,
ClusterCompatibilityStatus,
SupportPhaseBadge,
SupportPhaseEndDate,
} from '../operator-lifecycle-status';

jest.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key.replace(/^[^~]+~/, ''),
i18n: { language: 'en' },
}),
}));

describe('getClusterCompatibility', () => {
const lifecycle: LifecycleData = {
package: 'test-operator',
schema: 'io.openshift.operators.lifecycles.v1alpha1',
versions: [
{
name: '1.0',
platformCompatibility: [{ name: 'openshift', versions: ['4.14', '4.15', '4.16'] }],
phases: [],
},
{
name: '2.0',
platformCompatibility: [{ name: 'openshift', versions: ['4.16', '4.17'] }],
phases: [],
},
],
};

it('returns compatible when cluster version is in the list', () => {
expect(getClusterCompatibility(lifecycle, '1.0', '4.15.3')).toBe('compatible');
});

it('returns incompatible when cluster version is not in the list', () => {
expect(getClusterCompatibility(lifecycle, '1.0', '4.17.0')).toBe('incompatible');
});

it('returns no-data when lifecycle data is undefined', () => {
expect(getClusterCompatibility(undefined, '1.0', '4.15')).toBe('no-data');
});

it('returns no-data when cluster version is undefined', () => {
expect(getClusterCompatibility(lifecycle, '1.0', undefined)).toBe('no-data');
});

it('uses first version when operator version is not specified', () => {
expect(getClusterCompatibility(lifecycle, undefined, '4.14.0')).toBe('compatible');
});

it('returns no-data when versions array is empty', () => {
const emptyLifecycle: LifecycleData = {
package: 'test',
schema: 'test',
versions: [],
};
expect(getClusterCompatibility(emptyLifecycle, '1.0', '4.15')).toBe('no-data');
});

it('returns no-data when platformCompatibility is not defined', () => {
const noCompatLifecycle: LifecycleData = {
package: 'test',
schema: 'test',
versions: [{ name: '1.0' }],
};
expect(getClusterCompatibility(noCompatLifecycle, '1.0', '4.15')).toBe('no-data');
});

it('checks compatibility for specific version', () => {
expect(getClusterCompatibility(lifecycle, '2.0', '4.17.1')).toBe('compatible');
expect(getClusterCompatibility(lifecycle, '2.0', '4.14.0')).toBe('incompatible');
});

it('matches operator version by minor version when exact match fails', () => {
expect(getClusterCompatibility(lifecycle, '1.0.3', '4.15.0')).toBe('compatible');
expect(getClusterCompatibility(lifecycle, '2.0.1', '4.17.0')).toBe('compatible');
expect(getClusterCompatibility(lifecycle, '2.0.1', '4.14.0')).toBe('incompatible');
});
});

describe('getSupportPhase', () => {
const lifecycle: LifecycleData = {
package: 'test-operator',
schema: 'io.openshift.operators.lifecycles.v1alpha1',
versions: [
{
name: '1.0',
platformCompatibility: [{ name: 'openshift', versions: ['4.15'] }],
phases: [
{
name: 'Maintenance support',
startDate: '2024-01-01',
endDate: '2024-06-30',
},
{
name: 'Extended life cycle support',
startDate: '2024-07-01',
endDate: '2025-06-30',
},
],
},
],
};

const allPhases = lifecycle.versions[0].phases;

it('returns current phase and all phases when date falls within a phase', () => {
const result = getSupportPhase(lifecycle, '1.0', new Date('2024-03-15'));
expect(result).toEqual({
currentPhase: {
name: 'Maintenance support',
startDate: '2024-01-01',
endDate: '2024-06-30',
},
allPhases,
});
});

it('returns the second phase when date falls within it', () => {
const result = getSupportPhase(lifecycle, '1.0', new Date('2025-01-15'));
expect(result).toEqual({
currentPhase: {
name: 'Extended life cycle support',
startDate: '2024-07-01',
endDate: '2025-06-30',
},
allPhases,
});
});

it('returns self-support when all phases have ended', () => {
const result = getSupportPhase(lifecycle, '1.0', new Date('2026-01-01'));
expect(result).toBe('self-support');
});

it('returns first phase when date is before all phases', () => {
const result = getSupportPhase(lifecycle, '1.0', new Date('2023-06-01'));
expect(result).toEqual({
currentPhase: {
name: 'Maintenance support',
startDate: '2024-01-01',
endDate: '2024-06-30',
},
allPhases,
});
});

it('returns no-data when lifecycle data is undefined', () => {
expect(getSupportPhase(undefined, '1.0')).toBe('no-data');
});

it('returns no-data when versions array is missing', () => {
const noVersions: LifecycleData = {
package: 'test',
schema: 'test',
};
expect(getSupportPhase(noVersions, '1.0')).toBe('no-data');
});

it('returns no-data when phases array is empty', () => {
const noPhases: LifecycleData = {
package: 'test',
schema: 'test',
versions: [{ name: '1.0', phases: [] }],
};
expect(getSupportPhase(noPhases, '1.0')).toBe('no-data');
});

it('returns no-data when phases are not defined', () => {
const noPhases: LifecycleData = {
package: 'test',
schema: 'test',
versions: [{ name: '1.0' }],
};
expect(getSupportPhase(noPhases, '1.0')).toBe('no-data');
});

it('matches operator version by minor version when exact match fails', () => {
const result = getSupportPhase(lifecycle, '1.0.5', new Date('2024-03-15'));
expect(result).toEqual({
currentPhase: {
name: 'Maintenance support',
startDate: '2024-01-01',
endDate: '2024-06-30',
},
allPhases,
});
});

it('handles unsorted phases by sorting by endDate', () => {
const unsortedLifecycle: LifecycleData = {
package: 'test',
schema: 'test',
versions: [
{
name: '1.0',
phases: [
{
name: 'Extended life cycle support',
startDate: '2024-07-01',
endDate: '2025-06-30',
},
{
name: 'Maintenance support',
startDate: '2024-01-01',
endDate: '2024-06-30',
},
],
},
],
};
const result = getSupportPhase(unsortedLifecycle, '1.0', new Date('2024-03-15'));
expect(result).not.toBe('no-data');
expect(result).not.toBe('self-support');
expect((result as { currentPhase: { name: string } }).currentPhase.name).toBe(
'Maintenance support',
);
});

it('returns self-support for unsorted phases when all have ended', () => {
const unsortedLifecycle: LifecycleData = {
package: 'test',
schema: 'test',
versions: [
{
name: '1.0',
phases: [
{
name: 'Extended life cycle support',
startDate: '2024-07-01',
endDate: '2025-06-30',
},
{
name: 'Maintenance support',
startDate: '2024-01-01',
endDate: '2024-06-30',
},
],
},
],
};
expect(getSupportPhase(unsortedLifecycle, '1.0', new Date('2026-01-01'))).toBe('self-support');
});
});

describe('ClusterCompatibilityStatus', () => {
it('renders Compatible label', () => {
render(<ClusterCompatibilityStatus compatible="compatible" />);
expect(screen.getByText('Compatible')).toBeInTheDocument();
expect(screen.getByTestId('cluster-compatibility-compatible')).toBeInTheDocument();
});

it('renders Incompatible label', () => {
render(<ClusterCompatibilityStatus compatible="incompatible" />);
expect(screen.getByText('Incompatible')).toBeInTheDocument();
expect(screen.getByTestId('cluster-compatibility-incompatible')).toBeInTheDocument();
});

it('renders dash when no data', () => {
render(<ClusterCompatibilityStatus compatible="no-data" />);
expect(screen.getByText('-')).toBeInTheDocument();
expect(screen.getByTestId('cluster-compatibility-no-data')).toBeInTheDocument();
});
});

describe('SupportPhaseBadge', () => {
const phases = [
{ name: 'Maintenance support', startDate: '2024-01-01', endDate: '2024-06-30' },
{ name: 'Extended life cycle support', startDate: '2024-07-01', endDate: '2025-12-31' },
];

it('renders the current phase name in a badge', () => {
const phase = { currentPhase: phases[0], allPhases: phases };
render(<SupportPhaseBadge phase={phase} />);
expect(screen.getByText('Maintenance support')).toBeInTheDocument();
expect(screen.getByTestId('support-phase-badge')).toBeInTheDocument();
});

it('renders Self-support when phase is self-support', () => {
render(<SupportPhaseBadge phase="self-support" />);
expect(screen.getByText('Self-support')).toBeInTheDocument();
expect(screen.getByTestId('support-phase-self-support')).toBeInTheDocument();
});

it('renders dash when phase is no-data', () => {
render(<SupportPhaseBadge phase="no-data" />);
expect(screen.getByText('-')).toBeInTheDocument();
expect(screen.getByTestId('support-phase-no-data')).toBeInTheDocument();
});
});

describe('SupportPhaseEndDate', () => {
const phases = [
{ name: 'Maintenance support', startDate: '2024-01-01', endDate: '2024-06-30' },
{ name: 'Extended life cycle support', startDate: '2024-07-01', endDate: '2025-12-31' },
];

it('renders the current phase end date', () => {
const phase = { currentPhase: phases[0], allPhases: phases };
render(<SupportPhaseEndDate phase={phase} />);
expect(screen.getByTestId('support-phase-end-date')).toBeInTheDocument();
expect(screen.getByText(/Jun 30, 2024/)).toBeInTheDocument();
});

it('renders dash when phase is self-support', () => {
render(<SupportPhaseEndDate phase="self-support" />);
expect(screen.getByText('-')).toBeInTheDocument();
expect(screen.getByTestId('support-phase-end-no-data')).toBeInTheDocument();
});

it('renders dash when phase is no-data', () => {
render(<SupportPhaseEndDate phase="no-data" />);
expect(screen.getByText('-')).toBeInTheDocument();
expect(screen.getByTestId('support-phase-end-no-data')).toBeInTheDocument();
});
});
Loading