From 4f5180bf98667df47b7263e840386412a6c15b59 Mon Sep 17 00:00:00 2001 From: ArtemHoruzhenko Date: Wed, 3 Dec 2025 09:26:29 +0200 Subject: [PATCH 1/2] fix(ui): prevent duplicate notifications and improve search in cloud discovery - Calculate notification counts from original data instead of filtered items to prevent re-triggering toast when search filters change - Expand search fields in subscriptions page (type, provider, region, status) - Add statusAdded field to search in databases result page - Add comprehensive search tests for subscriptions page Fixes #RI-7776 --- .../RedisCloudDatabasesResult.tsx | 3 +- .../RedisCloudSubscriptions.spec.tsx | 200 +++++++++++++++--- .../RedisCloudSubscriptions.tsx | 19 +- 3 files changed, 188 insertions(+), 34 deletions(-) diff --git a/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-databases-result/RedisCloudDatabasesResult.tsx b/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-databases-result/RedisCloudDatabasesResult.tsx index ed390c78a9..e0784a84fa 100644 --- a/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-databases-result/RedisCloudDatabasesResult.tsx +++ b/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-databases-result/RedisCloudDatabasesResult.tsx @@ -57,7 +57,8 @@ const RedisCloudDatabaseListResult = ({ (item.publicEndpoint || '')?.toLowerCase().indexOf(value) !== -1 || item.subscriptionId?.toString()?.indexOf(value) !== -1 || item.subscriptionName?.toLowerCase().indexOf(value) !== -1 || - item.databaseId?.toString()?.indexOf(value) !== -1, + item.databaseId?.toString()?.indexOf(value) !== -1 || + item.statusAdded?.toLowerCase()?.indexOf(value) !== -1, ) if (!itemsTemp.length) { diff --git a/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-subscriptions/RedisCloudSubscriptions/RedisCloudSubscriptions.spec.tsx b/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-subscriptions/RedisCloudSubscriptions/RedisCloudSubscriptions.spec.tsx index 8ff00a038d..3cf09c5538 100644 --- a/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-subscriptions/RedisCloudSubscriptions/RedisCloudSubscriptions.spec.tsx +++ b/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-subscriptions/RedisCloudSubscriptions/RedisCloudSubscriptions.spec.tsx @@ -1,46 +1,192 @@ import React from 'react' import { instance, mock } from 'ts-mockito' +import { faker } from '@faker-js/faker' import { RedisCloudSubscription, RedisCloudSubscriptionStatus, RedisCloudSubscriptionType, } from 'uiSrc/slices/interfaces' -import { render } from 'uiSrc/utils/test-utils' +import { fireEvent, render, screen, waitFor } from 'uiSrc/utils/test-utils' import RedisCloudSubscriptions, { Props } from './RedisCloudSubscriptions' const mockedProps = mock() +const columnsMock = [ + { + id: 'name', + accessorKey: 'name', + header: 'Name', + enableSorting: true, + }, + { + id: 'id', + accessorKey: 'id', + header: 'Subscription ID', + enableSorting: true, + }, +] + +const createSubscription = ( + overrides?: Partial, +): RedisCloudSubscription => ({ + id: faker.number.int(), + name: faker.company.name(), + numberOfDatabases: faker.number.int({ min: 1, max: 10 }), + provider: faker.helpers.arrayElement(['AWS', 'GCP', 'Azure']), + region: faker.helpers.arrayElement(['us-east-1', 'eu-west-1', 'ap-south-1']), + status: RedisCloudSubscriptionStatus.Active, + type: RedisCloudSubscriptionType.Fixed, + free: faker.datatype.boolean(), + ...overrides, +}) + describe('RedisCloudSubscriptions', () => { + const defaultProps: Partial = { + columns: columnsMock, + subscriptions: [], + selection: [], + loading: false, + account: null, + error: '', + onClose: jest.fn(), + onBack: jest.fn(), + onSubmit: jest.fn(), + onSelectionChange: jest.fn(), + } + + const renderComponent = (propsOverride?: Partial) => { + const props = { ...defaultProps, ...propsOverride } as Props + return render( + , + ) + } + it('should render', () => { - const columnsMock = [ - { - id: 'subscriptionId', - accessorKey: 'subscriptionId', - header: 'Subscription ID', - enableSorting: true, - }, - ] + const subscriptionsMock: RedisCloudSubscription[] = [createSubscription()] + expect(renderComponent({ subscriptions: subscriptionsMock })).toBeTruthy() + }) - const subscriptionsMock: RedisCloudSubscription[] = [ - { - id: 123, - name: 'name', - numberOfDatabases: 123, - provider: 'provider', - region: 'region', + describe('search functionality', () => { + const subscriptions: RedisCloudSubscription[] = [ + createSubscription({ + id: 111, + name: 'Production Database', + provider: 'AWS', + region: 'us-east-1', + status: RedisCloudSubscriptionStatus.Active, + type: RedisCloudSubscriptionType.Flexible, + }), + createSubscription({ + id: 222, + name: 'Staging Environment', + provider: 'GCP', + region: 'eu-west-1', status: RedisCloudSubscriptionStatus.Active, type: RedisCloudSubscriptionType.Fixed, - free: false, - }, + }), + createSubscription({ + id: 333, + name: 'Development', + provider: 'Azure', + region: 'ap-south-1', + status: RedisCloudSubscriptionStatus.Error, + type: RedisCloudSubscriptionType.Fixed, + }), ] - expect( - render( - , - ), - ).toBeTruthy() + + it('should filter by name', async () => { + renderComponent({ subscriptions }) + + const searchInput = screen.getByTestId('search') + fireEvent.change(searchInput, { target: { value: 'Production' } }) + + await waitFor(() => { + expect(screen.getByText('Production Database')).toBeInTheDocument() + expect( + screen.queryByText('Staging Environment'), + ).not.toBeInTheDocument() + }) + }) + + it('should filter by id', async () => { + renderComponent({ subscriptions }) + + const searchInput = screen.getByTestId('search') + fireEvent.change(searchInput, { target: { value: '222' } }) + + await waitFor(() => { + expect(screen.getByText('Staging Environment')).toBeInTheDocument() + expect( + screen.queryByText('Production Database'), + ).not.toBeInTheDocument() + }) + }) + + it('should filter by provider', async () => { + renderComponent({ subscriptions }) + + const searchInput = screen.getByTestId('search') + fireEvent.change(searchInput, { target: { value: 'AWS' } }) + + await waitFor(() => { + expect(screen.getByText('Production Database')).toBeInTheDocument() + expect( + screen.queryByText('Staging Environment'), + ).not.toBeInTheDocument() + }) + }) + + it('should filter by region', async () => { + renderComponent({ subscriptions }) + + const searchInput = screen.getByTestId('search') + fireEvent.change(searchInput, { target: { value: 'eu-west' } }) + + await waitFor(() => { + expect(screen.getByText('Staging Environment')).toBeInTheDocument() + expect( + screen.queryByText('Production Database'), + ).not.toBeInTheDocument() + }) + }) + + it('should filter by status', async () => { + renderComponent({ subscriptions }) + + const searchInput = screen.getByTestId('search') + fireEvent.change(searchInput, { target: { value: 'error' } }) + + await waitFor(() => { + expect(screen.getByText('Development')).toBeInTheDocument() + expect( + screen.queryByText('Production Database'), + ).not.toBeInTheDocument() + }) + }) + + it('should filter by type', async () => { + renderComponent({ subscriptions }) + + const searchInput = screen.getByTestId('search') + fireEvent.change(searchInput, { target: { value: 'flexible' } }) + + await waitFor(() => { + expect(screen.getByText('Production Database')).toBeInTheDocument() + expect( + screen.queryByText('Staging Environment'), + ).not.toBeInTheDocument() + }) + }) + + it('should be case-insensitive', async () => { + renderComponent({ subscriptions }) + + const searchInput = screen.getByTestId('search') + fireEvent.change(searchInput, { target: { value: 'PRODUCTION' } }) + + await waitFor(() => { + expect(screen.getByText('Production Database')).toBeInTheDocument() + }) + }) }) }) diff --git a/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-subscriptions/RedisCloudSubscriptions/RedisCloudSubscriptions.tsx b/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-subscriptions/RedisCloudSubscriptions/RedisCloudSubscriptions.tsx index c6e240eb0d..7b024a6588 100644 --- a/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-subscriptions/RedisCloudSubscriptions/RedisCloudSubscriptions.tsx +++ b/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-subscriptions/RedisCloudSubscriptions/RedisCloudSubscriptions.tsx @@ -75,12 +75,15 @@ const RedisCloudSubscriptions = ({ } }, [subscriptions, loading]) - const countStatusActive = items.filter( - ({ status, numberOfDatabases }: RedisCloudSubscription) => - status === RedisCloudSubscriptionStatus.Active && numberOfDatabases !== 0, - )?.length + // Calculate counts from original subscriptions to prevent notification re-triggering on search + const countStatusActive = + subscriptions?.filter( + ({ status, numberOfDatabases }: RedisCloudSubscription) => + status === RedisCloudSubscriptionStatus.Active && + numberOfDatabases !== 0, + )?.length || 0 - const countStatusFailed = items.length - countStatusActive + const countStatusFailed = (subscriptions?.length || 0) - countStatusActive const handleSubmit = () => { onSubmit( @@ -106,7 +109,11 @@ const RedisCloudSubscriptions = ({ subscriptions?.filter( (item: RedisCloudSubscription) => item.name?.toLowerCase()?.indexOf(value) !== -1 || - item.id?.toString()?.toLowerCase().indexOf(value) !== -1, + item.id?.toString()?.indexOf(value) !== -1 || + item.type?.toLowerCase()?.indexOf(value) !== -1 || + item.provider?.toLowerCase()?.indexOf(value) !== -1 || + item.region?.toLowerCase()?.indexOf(value) !== -1 || + item.status?.toLowerCase()?.indexOf(value) !== -1, ) ?? [] if (!itemsTemp?.length) { From 93f8edbc0ac7b409b9b28da2ff6f6797550bc450 Mon Sep 17 00:00:00 2001 From: ArtemHoruzhenko Date: Wed, 3 Dec 2025 09:56:41 +0200 Subject: [PATCH 2/2] fix(ui): handle undefined statusAdded in search filter Use empty string fallback to prevent false positives when statusAdded is undefined --- .../redis-cloud-databases-result/RedisCloudDatabasesResult.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-databases-result/RedisCloudDatabasesResult.tsx b/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-databases-result/RedisCloudDatabasesResult.tsx index e0784a84fa..5d4a564169 100644 --- a/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-databases-result/RedisCloudDatabasesResult.tsx +++ b/redisinsight/ui/src/pages/autodiscover-cloud/redis-cloud-databases-result/RedisCloudDatabasesResult.tsx @@ -58,7 +58,7 @@ const RedisCloudDatabaseListResult = ({ item.subscriptionId?.toString()?.indexOf(value) !== -1 || item.subscriptionName?.toLowerCase().indexOf(value) !== -1 || item.databaseId?.toString()?.indexOf(value) !== -1 || - item.statusAdded?.toLowerCase()?.indexOf(value) !== -1, + (item.statusAdded || '').toLowerCase().indexOf(value) !== -1, ) if (!itemsTemp.length) {