1+ const { expect } = require ( '@playwright/test' ) ;
12const { GridRendererComponent } = require ( './grid-renderer.component' ) ;
23const { GridHeaderComponent } = require ( './grid-header.component' ) ;
34
@@ -7,7 +8,7 @@ class GridEditorComponent {
78 this . container = page . locator ( '#main-grid-view' ) ;
89 this . grid = page . locator ( '#myGrid' ) ;
910 this . renderer = new GridRendererComponent ( page , this . grid ) ;
10- this . header = new GridHeaderComponent ( page , this . grid ) ;
11+ this . header = new GridHeaderComponent ( page , this . grid , this . renderer ) ;
1112 this . addRowButton = page . getByRole ( 'button' , { name : 'Add Row' , exact : true } ) ;
1213 this . addRowsAboveButton = page . getByRole ( 'button' , { name : 'Add Rows Above' } ) ;
1314 this . addRowsBelowButton = page . getByRole ( 'button' , { name : 'Add Rows Below' } ) ;
@@ -19,21 +20,21 @@ class GridEditorComponent {
1920 }
2021
2122 async expectVisible ( ) {
22- await this . container . waitFor ( { state : 'visible' } ) ;
23- await this . grid . waitFor ( { state : 'visible' } ) ;
24- await this . addRowButton . waitFor ( { state : 'visible' } ) ;
25- await this . addRowsAboveButton . waitFor ( { state : 'visible' } ) ;
26- await this . addRowsBelowButton . waitFor ( { state : 'visible' } ) ;
27- await this . deleteSelectedRowsButton . waitFor ( { state : 'visible' } ) ;
28- await this . quickFilterInput . waitFor ( { state : 'visible' } ) ;
29- await this . clearFiltersButton . waitFor ( { state : 'visible' } ) ;
30- await this . clearSortButton . waitFor ( { state : 'visible' } ) ;
31- await this . resetTableButton . waitFor ( { state : 'visible' } ) ;
23+ await expect ( this . container ) . toBeVisible ( ) ;
24+ await expect ( this . grid ) . toBeVisible ( ) ;
25+ await expect ( this . addRowButton ) . toBeVisible ( ) ;
26+ await expect ( this . addRowsAboveButton ) . toBeVisible ( ) ;
27+ await expect ( this . addRowsBelowButton ) . toBeVisible ( ) ;
28+ await expect ( this . deleteSelectedRowsButton ) . toBeVisible ( ) ;
29+ await expect ( this . quickFilterInput ) . toBeVisible ( ) ;
30+ await expect ( this . clearFiltersButton ) . toBeVisible ( ) ;
31+ await expect ( this . clearSortButton ) . toBeVisible ( ) ;
32+ await expect ( this . resetTableButton ) . toBeVisible ( ) ;
3233 }
3334
3435 async expectReady ( ) {
3536 await this . expectVisible ( ) ;
36- await this . grid . locator ( '.tabulator-col-title' ) . first ( ) . waitFor ( { state : 'visible' } ) ;
37+ await expect ( this . grid . locator ( '.tabulator-col-title' ) . first ( ) ) . toBeVisible ( ) ;
3738 }
3839
3940 async addRow ( ) {
@@ -84,21 +85,93 @@ class GridEditorComponent {
8485 await this . renderer . selectRows ( rowIndexes ) ;
8586 }
8687
87- async clearFilters ( ) {
88- for ( let attempt = 0 ; attempt < 3 ; attempt += 1 ) {
89- await this . clearFiltersButton . click ( ) ;
90- for ( let check = 0 ; check < 20 ; check += 1 ) {
91- const quickFilterValue = await this . quickFilterInput . inputValue ( ) ;
92- const hasActiveColumnFilter = await this . grid
93- . locator ( '.tabulator-header-filter input' )
94- . evaluateAll ( ( inputs ) => inputs . some ( ( input ) => String ( input . value || '' ) . trim ( ) . length > 0 ) ) ;
95- if ( quickFilterValue === '' && ! hasActiveColumnFilter ) {
96- return ;
97- }
98- await this . page . waitForTimeout ( 50 ) ;
99- }
88+ async clearFilters ( { expectedActiveRowCount } = { } ) {
89+ const context = await this . _buildClearFiltersContext ( expectedActiveRowCount ) ;
90+
91+ try {
92+ await expect ( async ( ) => {
93+ await this . _attemptClearFilters ( context ) ;
94+ } ) . toPass ( { timeout : 15000 , intervals : [ 100 , 200 , 400 , 800 ] } ) ;
95+ return ;
96+ } catch ( error ) {
97+ // fall through to detailed diagnostics
10098 }
101- throw new Error ( 'Failed to clear all filters.' ) ;
99+
100+ await this . _throwClearFiltersDiagnostics ( context ) ;
101+ }
102+
103+ async _attemptClearFilters ( context ) {
104+ await this . clearFiltersButton . click ( ) ;
105+ await expect ( this . quickFilterInput ) . toHaveValue ( '' ) ;
106+ await this . _expectHeaderFiltersCleared ( ) ;
107+ await this . _expectActiveRowCountRecovered ( context ) ;
108+ await this . renderer . waitForGridSettle ( { columnName : context . diagnosticColumn , stableForMs : 2000 , timeoutMs : 7000 } ) ;
109+ await this . _expectHeaderFiltersCleared ( ) ;
110+ await this . _expectActiveRowCountRecovered ( context ) ;
111+ }
112+
113+ async _buildClearFiltersContext ( expectedActiveRowCount ) {
114+ const initialActiveRowCount = await this . renderer . getActiveRowCount ( ) ;
115+ const columnNames = await this . header . getColumnNames ( ) ;
116+ const diagnosticColumn = columnNames [ 0 ] ;
117+ const hadActiveFiltersBeforeClear = await this . _hasActiveFilters ( ) ;
118+ const minRecoveredRowCount =
119+ hadActiveFiltersBeforeClear && ! Number . isFinite ( expectedActiveRowCount )
120+ ? initialActiveRowCount + 1
121+ : initialActiveRowCount ;
122+ return {
123+ expectedActiveRowCount,
124+ initialActiveRowCount,
125+ diagnosticColumn,
126+ minRecoveredRowCount,
127+ } ;
128+ }
129+
130+ async _hasActiveFilters ( ) {
131+ const quickFilterValue = await this . quickFilterInput . inputValue ( ) ;
132+ if ( String ( quickFilterValue || '' ) . trim ( ) . length > 0 ) {
133+ return true ;
134+ }
135+ const headerFilterValues = await this . _getHeaderFilterValues ( ) ;
136+ return headerFilterValues . some ( ( value ) => value . length > 0 ) ;
137+ }
138+
139+ async _getHeaderFilterValues ( ) {
140+ return this . grid
141+ . locator ( '.tabulator-header-filter input' )
142+ . evaluateAll ( ( inputs ) => inputs . map ( ( input ) => String ( input . value || '' ) . trim ( ) ) ) ;
143+ }
144+
145+ async _expectHeaderFiltersCleared ( ) {
146+ const headerFilterValues = await this . _getHeaderFilterValues ( ) ;
147+ expect ( headerFilterValues . some ( ( value ) => value . length > 0 ) ) . toBe ( false ) ;
148+ }
149+
150+ async _expectActiveRowCountRecovered ( context ) {
151+ const activeRowCount = await this . renderer . getActiveRowCount ( ) ;
152+ if ( Number . isFinite ( context . expectedActiveRowCount ) ) {
153+ expect ( activeRowCount ) . toBe ( context . expectedActiveRowCount ) ;
154+ } else {
155+ expect ( activeRowCount ) . toBeGreaterThanOrEqual ( context . minRecoveredRowCount ) ;
156+ }
157+ }
158+
159+ async _throwClearFiltersDiagnostics ( context ) {
160+ const quickFilterValue = await this . quickFilterInput . inputValue ( ) ;
161+ const headerFilterValues = await this . _getHeaderFilterValues ( ) ;
162+ const activeRowCount = await this . renderer . getActiveRowCount ( ) ;
163+ const snapshot = await this . renderer . getActiveTableSnapshot ( context . diagnosticColumn , 3 ) ;
164+ throw new Error (
165+ [
166+ 'Failed to clear all filters and restore active rows.' ,
167+ `quickFilter="${ quickFilterValue } "` ,
168+ `headerFilters=${ JSON . stringify ( headerFilterValues ) } ` ,
169+ `activeRowCount=${ activeRowCount } ` ,
170+ `expectedActiveRowCount=${ Number . isFinite ( context . expectedActiveRowCount ) ? context . expectedActiveRowCount : 'n/a' } ` ,
171+ `diagnosticColumn=${ context . diagnosticColumn || 'n/a' } ` ,
172+ `topValues=${ JSON . stringify ( snapshot . topValues || [ ] ) } ` ,
173+ ] . join ( ' ' )
174+ ) ;
102175 }
103176
104177 async clearSort ( ) {
0 commit comments