@ -7,6 +7,8 @@ import { AccessControlAction } from 'app/types';
import { setupMswServer } from '../mockApi' ;
import { grantUserPermissions } from '../mocks' ;
import { alertingFactory } from '../mocks/server/db' ;
import { RulesFilter } from '../search/rulesSearchParser' ;
import { testWithFeatureToggles } from '../test/test-utils' ;
import RuleList , { RuleListActions } from './RuleList.v2' ;
@ -23,12 +25,18 @@ jest.mock('./GroupedView', () => ({
const ui = {
filterView : byTestId ( 'filter-view' ) ,
groupedView : byTestId ( 'grouped-view' ) ,
modeSelector : {
grouped : byRole ( 'radio' , { name : /grouped/i } ) ,
list : byRole ( 'radio' , { name : /list/i } ) ,
} ,
searchInput : byTestId ( 'search-query-input' ) ,
} ;
setPluginLinksHook ( ( ) = > ( { links : [ ] , isLoading : false } ) ) ;
setPluginComponentsHook ( ( ) = > ( { components : [ ] , isLoading : false } ) ) ;
grantUserPermissions ( [ AccessControlAction . AlertingRuleExternalRead ] ) ;
testWithFeatureToggles ( [ 'alertingListViewV2' ] ) ;
setupMswServer ( ) ;
@ -61,8 +69,84 @@ describe('RuleList v2', () => {
expect ( ui . groupedView . query ( ) ) . not . toBeInTheDocument ( ) ;
} ) ;
it ( 'should show list view when a filter is applied' , ( ) = > {
render ( < RuleList / > , { historyOptions : { initialEntries : [ '/?search=rule:cpu-alert' ] } } ) ;
it ( 'should show grouped view when only group filter is applied' , ( ) = > {
render ( < RuleList / > , { historyOptions : { initialEntries : [ '/?search=group:cpu-usage' ] } } ) ;
expect ( ui . groupedView . get ( ) ) . toBeInTheDocument ( ) ;
expect ( ui . filterView . query ( ) ) . not . toBeInTheDocument ( ) ;
} ) ;
it ( 'should show grouped view when only namespace filter is applied' , ( ) = > {
render ( < RuleList / > , { historyOptions : { initialEntries : [ '/?search=namespace:global' ] } } ) ;
expect ( ui . groupedView . get ( ) ) . toBeInTheDocument ( ) ;
expect ( ui . filterView . query ( ) ) . not . toBeInTheDocument ( ) ;
} ) ;
it ( 'should show grouped view when both group and namespace filters are applied' , ( ) = > {
render ( < RuleList / > , { historyOptions : { initialEntries : [ '/?search=group:cpu-usage namespace:global' ] } } ) ;
expect ( ui . groupedView . get ( ) ) . toBeInTheDocument ( ) ;
expect ( ui . filterView . query ( ) ) . not . toBeInTheDocument ( ) ;
} ) ;
it ( 'should show list view when group and namespace filters are combined with other filter types' , ( ) = > {
render ( < RuleList / > , {
historyOptions : { initialEntries : [ '/?search=group:cpu-usage namespace:global state:firing' ] } ,
} ) ;
expect ( ui . filterView . get ( ) ) . toBeInTheDocument ( ) ;
expect ( ui . groupedView . query ( ) ) . not . toBeInTheDocument ( ) ;
} ) ;
it ( 'should show grouped view when view parameter is empty' , ( ) = > {
render ( < RuleList / > , { historyOptions : { initialEntries : [ '/?view=' ] } } ) ;
expect ( ui . groupedView . get ( ) ) . toBeInTheDocument ( ) ;
expect ( ui . filterView . query ( ) ) . not . toBeInTheDocument ( ) ;
} ) ;
it ( 'should show grouped view when search parameter is empty' , ( ) = > {
render ( < RuleList / > , { historyOptions : { initialEntries : [ '/?search=' ] } } ) ;
expect ( ui . groupedView . get ( ) ) . toBeInTheDocument ( ) ;
expect ( ui . filterView . query ( ) ) . not . toBeInTheDocument ( ) ;
} ) ;
it . each < { filterType : keyof RulesFilter ; searchQuery : string } > ( [
{ filterType : 'freeFormWords' , searchQuery : 'cpu alert' } ,
{ filterType : 'ruleName' , searchQuery : 'rule:"cpu 80%"' } ,
{ filterType : 'ruleState' , searchQuery : 'state:firing' } ,
{ filterType : 'ruleType' , searchQuery : 'type:alerting' } ,
{ filterType : 'dataSourceNames' , searchQuery : 'datasource:prometheus' } ,
{ filterType : 'labels' , searchQuery : 'label:team=backend' } ,
{ filterType : 'ruleHealth' , searchQuery : 'health:error' } ,
{ filterType : 'contactPoint' , searchQuery : 'contactPoint:slack' } ,
] ) ( 'should show list view when %s filter is applied' , ( { filterType , searchQuery } ) = > {
render ( < RuleList / > , { historyOptions : { initialEntries : [ ` /?search= ${ encodeURIComponent ( searchQuery ) } ` ] } } ) ;
expect ( ui . filterView . get ( ) ) . toBeInTheDocument ( ) ;
expect ( ui . groupedView . query ( ) ) . not . toBeInTheDocument ( ) ;
} ) ;
it ( 'should show list view when "view=list" URL parameter is present with group filter' , ( ) = > {
render ( < RuleList / > , { historyOptions : { initialEntries : [ '/?view=list&search=group:cpu-usage' ] } } ) ;
expect ( ui . filterView . get ( ) ) . toBeInTheDocument ( ) ;
expect ( ui . groupedView . query ( ) ) . not . toBeInTheDocument ( ) ;
} ) ;
it ( 'should show list view when "view=list" URL parameter is present with namespace filter' , ( ) = > {
render ( < RuleList / > , { historyOptions : { initialEntries : [ '/?view=list&search=namespace:global' ] } } ) ;
expect ( ui . filterView . get ( ) ) . toBeInTheDocument ( ) ;
expect ( ui . groupedView . query ( ) ) . not . toBeInTheDocument ( ) ;
} ) ;
it ( 'should show list view when "view=list" URL parameter is present with both group and namespace filters' , ( ) = > {
render ( < RuleList / > , {
historyOptions : { initialEntries : [ '/?view=list&search=group:cpu-usage namespace:global' ] } ,
} ) ;
expect ( ui . filterView . get ( ) ) . toBeInTheDocument ( ) ;
expect ( ui . groupedView . query ( ) ) . not . toBeInTheDocument ( ) ;
@ -160,3 +244,47 @@ describe('RuleListActions', () => {
expect ( ui . menuOptions . newDataSourceRecordingRule . query ( menu ) ) . toBeInTheDocument ( ) ;
} ) ;
} ) ;
describe ( 'RuleList v2 - View switching' , ( ) = > {
it ( 'should preserve both group and namespace filters when switching from list view to grouped view' , async ( ) = > {
// Start with list view and both group and namespace filters
const { user } = render ( < RuleList / > , {
historyOptions : { initialEntries : [ '/?view=list&search=group:cpu-usage namespace:global' ] } ,
} ) ;
expect ( ui . filterView . get ( ) ) . toBeInTheDocument ( ) ;
// Click the "Grouped" view button
const groupedButton = await ui . modeSelector . grouped . find ( ) ;
await user . click ( groupedButton ) ;
// Should preserve both filters and switch to grouped view
expect ( ui . groupedView . get ( ) ) . toBeInTheDocument ( ) ;
expect ( ui . filterView . query ( ) ) . not . toBeInTheDocument ( ) ;
// Verify filters are preserved
expect ( ui . searchInput . get ( ) ) . toHaveValue ( 'group:cpu-usage namespace:global' ) ;
expect ( ui . modeSelector . list . query ( ) ) . not . toBeChecked ( ) ;
} ) ;
it ( 'should clear all filters when switching from list view to grouped view with group, namespace and other filters' , async ( ) = > {
// Start with list view with all types of filters
const { user } = render ( < RuleList / > , {
historyOptions : {
initialEntries : [ '/?view=list&search=group:cpu-usage namespace:global state:firing rule:"test"' ] ,
} ,
} ) ;
expect ( ui . filterView . get ( ) ) . toBeInTheDocument ( ) ;
// Click the "Grouped" view button
const groupedButton = await ui . modeSelector . grouped . find ( ) ;
await user . click ( groupedButton ) ;
// Should clear all filters because other filters are present
expect ( ui . groupedView . get ( ) ) . toBeInTheDocument ( ) ;
expect ( ui . filterView . query ( ) ) . not . toBeInTheDocument ( ) ;
// Verify all filters are cleared
expect ( ui . searchInput . get ( ) ) . toHaveValue ( '' ) ;
expect ( ui . modeSelector . list . query ( ) ) . not . toBeChecked ( ) ;
} ) ;
} ) ;