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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,30 @@ const mockData: K8sResourceCommon[] = [

const initialFilters: ResourceFilters = { name: '', label: '' };

const projectMockData: K8sResourceCommon[] = [
{
metadata: {
name: 'test-proj',
annotations: { 'openshift.io/display-name': 'My Test Project' },
},
kind: 'Project',
apiVersion: 'v1',
},
{
metadata: {
name: 'other-proj',
annotations: { 'openshift.io/display-name': 'Other Project' },
},
kind: 'Project',
apiVersion: 'v1',
},
{
metadata: { name: 'no-display-name' },
kind: 'Project',
apiVersion: 'v1',
},
];

const createWrapper = (initialEntries: string[] = ['/']): FC<{ children: ReactNode }> => {
const Wrapper: FC<{ children: ReactNode }> = ({ children }) => (
<MemoryRouter initialEntries={initialEntries}>{children}</MemoryRouter>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since useConsoleDataViewFilters only depends on React Router, using the heavy renderHookWithProviders setup is unnecessary here. I've opened CONSOLE-5360 to introduce a lightweight renderHookWithRouter helper in a follow-up PR to clean up this boilerplate.

Expand Down Expand Up @@ -100,6 +124,75 @@ describe('useConsoleDataViewFilters', () => {
expect(result.current.filteredData[0].metadata.name).toBe('web-frontend');
});

it('should filter by openshift.io/display-name using fuzzy matching', () => {
const { result } = renderHook(
() => useConsoleDataViewFilters({ data: projectMockData, initialFilters }),
{ wrapper: createWrapper(['/?name=My%20Test']) },
);

expect(result.current.filteredData).toHaveLength(1);
expect(result.current.filteredData[0].metadata.name).toBe('test-proj');
});

it('should filter by metadata.name when display name differs', () => {
const { result } = renderHook(
() => useConsoleDataViewFilters({ data: projectMockData, initialFilters }),
{ wrapper: createWrapper(['/?name=test-proj']) },
);

expect(result.current.filteredData).toHaveLength(1);
expect(result.current.filteredData[0].metadata.name).toBe('test-proj');
});

it('should match project by display name case-insensitively in fuzzy mode', () => {
// Default fuzzy mode - no mock override
const { result } = renderHook(
() => useConsoleDataViewFilters({ data: projectMockData, initialFilters }),
{ wrapper: createWrapper(['/?name=other%20project']) }, // lowercase search
);

// Matches "Other Project" even though we searched "other project"
expect(result.current.filteredData).toHaveLength(1);
expect(result.current.filteredData[0].metadata.name).toBe('other-proj');
});

it('should not match resources without display-name annotation when searching by display name', () => {
const { result } = renderHook(
() => useConsoleDataViewFilters({ data: projectMockData, initialFilters }),
{ wrapper: createWrapper(['/?name=Project']) },
);

// "Project" appears in display names of test-proj and other-proj, but not in "no-display-name"
expect(result.current.filteredData).toHaveLength(2);
expect(result.current.filteredData.map((d) => d.metadata.name)).toEqual([
'test-proj',
'other-proj',
]);
});

it('should filter by openshift.io/display-name using exact matching when exact search is enabled', () => {
useExactSearch.mockReturnValue([true, true]);

const { result } = renderHook(
() => useConsoleDataViewFilters({ data: projectMockData, initialFilters }),
{ wrapper: createWrapper(['/?name=My%20Test%20Project']) },
);

expect(result.current.filteredData).toHaveLength(1);
expect(result.current.filteredData[0].metadata.name).toBe('test-proj');
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

it('should require case-sensitive match for display-name in exact search mode', () => {
useExactSearch.mockReturnValue([true, true]);

const { result } = renderHook(
() => useConsoleDataViewFilters({ data: projectMockData, initialFilters }),
{ wrapper: createWrapper(['/?name=my%20test']) },
);

expect(result.current.filteredData).toHaveLength(0);
});

Comment thread
cajieh marked this conversation as resolved.
it('should filter by name using exact matching when exact search is enabled', () => {
useExactSearch.mockReturnValue([true, true]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const getK8sResourceMetadata = (obj: K8sResourceCommon): ResourceMetadata => ({
labels: obj.metadata?.labels,
});

const getOpenShiftDisplayName = (resource: K8sResourceCommon): string | undefined =>
resource.metadata?.annotations?.['openshift.io/display-name'];

export const useConsoleDataViewFilters = <
TData,
TFilters extends ResourceFilters = ResourceFilters
Expand Down Expand Up @@ -75,12 +78,14 @@ export const useConsoleDataViewFilters = <
() =>
data?.filter((resource) => {
const { name: resourceName, labels } = getObjectMetadata(resource);
const displayName = getOpenShiftDisplayName(resource as K8sResourceCommon);

// Filter by K8s resource name
// Filter by K8s resource name or display name
const matchFn = isExactSearch ? exactMatch : fuzzyCaseInsensitive;
const matchesName =
!filters.name || isExactSearch
? exactMatch(filters.name, resourceName)
: fuzzyCaseInsensitive(filters.name, resourceName);
!filters.name ||
matchFn(filters.name, resourceName) ||
matchFn(filters.name, displayName);

const resourceLabels = mapLabelsToStrings(labels);
const filterLabelsArray = filters.label?.split(',') ?? [];
Expand Down