@@ -4,11 +4,6 @@ import { initializeMockApp } from '@edx/frontend-platform/testing';
44import { renderWrapper } from '@src/setupTest' ;
55import { getCellHeader } from './utils' ;
66
7- const renderCellHeader = ( columnId : string , columnTitle : string , filtersApplied : string [ ] ) => {
8- const component = getCellHeader ( columnId , columnTitle , filtersApplied ) ;
9- return renderWrapper ( < div > { component } </ div > ) ;
10- } ;
11-
127describe ( 'getCellHeader' , ( ) => {
138 beforeEach ( ( ) => {
149 initializeMockApp ( {
@@ -21,64 +16,74 @@ describe('getCellHeader', () => {
2116 } ) ;
2217
2318 it ( 'displays column title without filter icon when no filters are applied' , ( ) => {
24- const { container } = renderCellHeader ( 'scope' , 'Scope' , [ ] ) ;
25-
26- expect ( screen . getByText ( 'Scope' ) ) . toBeInTheDocument ( ) ;
27- expect ( container . querySelector ( 'svg' ) ) . not . toBeInTheDocument ( ) ;
19+ const result = getCellHeader ( 'scope' , 'Scope' , [ ] ) ;
20+ expect ( result ) . toBe ( 'Scope' ) ;
2821 } ) ;
2922
3023 it ( 'displays column title without filter icon when column is not in filters applied' , ( ) => {
31- const { container } = renderCellHeader ( 'scope' , 'Scope' , [ 'role' , 'organization' ] ) ;
24+ const result = getCellHeader ( 'scope' , 'Scope' , [ 'role' , 'organization' ] ) ;
25+ expect ( result ) . toBe ( 'Scope' ) ;
26+ } ) ;
3227
33- expect ( screen . getByText ( 'Scope' ) ) . toBeInTheDocument ( ) ;
34- expect ( container . querySelector ( 'svg' ) ) . not . toBeInTheDocument ( ) ;
28+ it ( 'returns a component function when column has filter applied' , ( ) => {
29+ const result = getCellHeader ( 'scope' , 'Scope' , [ 'scope' , 'role' ] ) ;
30+ expect ( typeof result ) . toBe ( 'function' ) ;
3531 } ) ;
3632
37- it ( 'displays column title with filter icon when column has filter applied' , ( ) => {
38- const { container } = renderCellHeader ( 'scope' , 'Scope' , [ 'scope' , 'role' ] ) ;
33+ it ( 'renders filter icon and title when component is rendered' , ( ) => {
34+ const Header = getCellHeader ( 'scope' , 'Scope' , [ 'scope' , 'role' ] ) as React . FC ;
35+ renderWrapper ( < Header /> ) ;
3936
4037 expect ( screen . getByText ( 'Scope' ) ) . toBeInTheDocument ( ) ;
41- expect ( container . querySelector ( 'svg' ) ) . toBeInTheDocument ( ) ;
38+ expect ( screen . getByText ( 'Scope' ) . closest ( 'span' ) ) . toHaveClass ( 'd-flex' , 'flex-row' , 'align-items-center' ) ;
4239 } ) ;
4340
4441 it ( 'displays filter icon only for matching column when multiple filters applied' , ( ) => {
45- const { container } = renderCellHeader ( 'organization' , 'Organization' , [ 'scope' , 'organization' , 'role' ] ) ;
42+ const Header = getCellHeader ( 'organization' , 'Organization' , [ 'scope' , 'organization' , 'role' ] ) as React . FC ;
43+ renderWrapper ( < Header /> ) ;
4644
4745 expect ( screen . getByText ( 'Organization' ) ) . toBeInTheDocument ( ) ;
48- expect ( container . querySelector ( 'svg' ) ) . toBeInTheDocument ( ) ;
4946 } ) ;
5047
5148 it ( 'handles empty column title' , ( ) => {
52- const { container } = renderCellHeader ( 'scope' , '' , [ 'scope' ] ) ;
49+ const Header = getCellHeader ( 'scope' , '' , [ 'scope' ] ) as React . FC ;
50+ const { container } = renderWrapper ( < Header /> ) ;
5351
5452 expect ( container . querySelector ( 'svg' ) ) . toBeInTheDocument ( ) ;
5553 } ) ;
5654
5755 it ( 'handles special characters in column title' , ( ) => {
58- const { container } = renderCellHeader ( 'scope' , 'Scope & Context' , [ 'scope' ] ) ;
56+ const Header = getCellHeader ( 'scope' , 'Scope & Context' , [ 'scope' ] ) as React . FC ;
57+ renderWrapper ( < Header /> ) ;
5958
6059 expect ( screen . getByText ( 'Scope & Context' ) ) . toBeInTheDocument ( ) ;
61- expect ( container . querySelector ( 'svg' ) ) . toBeInTheDocument ( ) ;
6260 } ) ;
6361
6462 it ( 'is case sensitive when matching column ID' , ( ) => {
65- const { container } = renderCellHeader ( 'scope' , 'Scope' , [ 'SCOPE' , 'Role' ] ) ;
66-
67- expect ( screen . getByText ( 'Scope' ) ) . toBeInTheDocument ( ) ;
68- expect ( container . querySelector ( 'svg' ) ) . not . toBeInTheDocument ( ) ;
63+ const result = getCellHeader ( 'scope' , 'Scope' , [ 'SCOPE' , 'Role' ] ) ;
64+ expect ( result ) . toBe ( 'Scope' ) ;
6965 } ) ;
7066
7167 it ( 'handles long column titles with filters' , ( ) => {
72- const { container } = renderCellHeader ( 'organization' , 'Very Long Organization Column Title' , [ 'organization' ] ) ;
68+ const Header = getCellHeader ( 'organization' , 'Very Long Organization Column Title' , [ 'organization' ] ) as React . FC ;
69+ renderWrapper ( < Header /> ) ;
7370
7471 expect ( screen . getByText ( 'Very Long Organization Column Title' ) ) . toBeInTheDocument ( ) ;
75- expect ( container . querySelector ( 'svg' ) ) . toBeInTheDocument ( ) ;
7672 } ) ;
7773
78- it ( 'displays correct structure when filter is applied' , ( ) => {
79- renderCellHeader ( 'role' , 'Role' , [ 'role' ] ) ;
74+ it ( 'produces unique toString per column to avoid duplicate React keys' , ( ) => {
75+ const scopeHeader = getCellHeader ( 'scope' , 'Scope' , [ 'scope' , 'role' ] ) ;
76+ const roleHeader = getCellHeader ( 'role' , 'Role' , [ 'scope' , 'role' ] ) ;
77+
78+ // When used in template literals (as Paragon does for keys), each column
79+ // must produce a unique string to prevent duplicate key warnings.
80+ expect ( `${ scopeHeader } ` ) . not . toBe ( `${ roleHeader } ` ) ;
81+ expect ( `${ scopeHeader } ` ) . toBe ( 'FilteredHeader_scope' ) ;
82+ expect ( `${ roleHeader } ` ) . toBe ( 'FilteredHeader_role' ) ;
83+ } ) ;
8084
81- const container = screen . getByText ( 'Role' ) . closest ( 'span' ) ;
82- expect ( container ) . toHaveClass ( 'd-flex' , 'flex-row' , 'align-items-center' ) ;
85+ it ( 'toString does not produce [object Object]' , ( ) => {
86+ const result = getCellHeader ( 'scope' , 'Scope' , [ 'scope' ] ) ;
87+ expect ( `${ result } ` ) . not . toBe ( '[object Object]' ) ;
8388 } ) ;
8489} ) ;
0 commit comments