@@ -779,4 +779,223 @@ test.describe('table editor', () => {
779779 // Cleanup
780780 await deleteTable ( page , ref , tableName )
781781 } )
782+
783+ test ( 'boolean fields can be edited correctly' , async ( { page, ref } ) => {
784+ const tableName = 'pw_table_boolean_edits'
785+ const boolColName = 'is_active'
786+
787+ if ( ! page . url ( ) . includes ( '/editor' ) ) {
788+ await page . goto ( toUrl ( `/project/${ ref } /editor?schema=public` ) )
789+ await waitForTableToLoad ( page , ref )
790+ }
791+
792+ await dismissToastsIfAny ( page )
793+
794+ // Create a simple table with a boolean column
795+ await page . getByRole ( 'button' , { name : 'New table' , exact : true } ) . click ( )
796+ await page . getByTestId ( 'table-name-input' ) . fill ( tableName )
797+ await page . getByTestId ( 'created_at-extra-options' ) . click ( )
798+ await page . getByRole ( 'checkbox' , { name : 'Is Nullable' } ) . click ( )
799+ await page . getByTestId ( 'created_at-extra-options' ) . click ( { force : true } )
800+
801+ // Add boolean column
802+ await page . getByRole ( 'button' , { name : 'Add column' } ) . click ( )
803+ await page . getByRole ( 'textbox' , { name : 'column_name' } ) . fill ( boolColName )
804+ await page . getByText ( 'Choose a column type...' ) . click ( )
805+ await page . getByPlaceholder ( 'Search types...' ) . fill ( 'bool' )
806+ await page . getByRole ( 'option' , { name : 'bool' } ) . first ( ) . click ( )
807+
808+ await page . getByRole ( 'button' , { name : 'Save' } ) . click ( )
809+ await expect (
810+ page . getByText ( `Table ${ tableName } is good to go!` ) ,
811+ 'Success toast should be visible after table creation'
812+ ) . toBeVisible ( { timeout : 50000 } )
813+
814+ await expect (
815+ page . getByRole ( 'button' , { name : `View ${ tableName } ` , exact : true } ) ,
816+ 'Table should be visible after creation'
817+ ) . toBeVisible ( )
818+
819+ // Navigate to the table
820+ await page . getByRole ( 'button' , { name : `View ${ tableName } ` , exact : true } ) . click ( )
821+ await page . waitForURL ( / \/ e d i t o r \/ \d + \? s c h e m a = p u b l i c $ / )
822+
823+ // Insert a row with TRUE value via side panel
824+ await page . getByTestId ( 'table-editor-insert-new-row' ) . click ( )
825+ await page . getByRole ( 'menuitem' , { name : 'Insert row Insert a new row' } ) . click ( )
826+ await page . getByRole ( 'combobox' ) . click ( )
827+ await page . getByRole ( 'option' , { name : 'TRUE' } ) . click ( )
828+ await page . getByTestId ( 'action-bar-save-row' ) . click ( )
829+ await waitForApiResponse ( page , 'pg-meta' , ref , 'query?key=' , { method : 'POST' } )
830+
831+ await expect (
832+ page . getByRole ( 'gridcell' , { name : 'TRUE' } ) ,
833+ 'TRUE value should be displayed'
834+ ) . toBeVisible ( )
835+
836+ // Insert a row with FALSE value via side panel
837+ await page . getByTestId ( 'table-editor-insert-new-row' ) . click ( )
838+ await page . getByRole ( 'menuitem' , { name : 'Insert row Insert a new row' } ) . click ( )
839+ await page . getByRole ( 'combobox' ) . click ( )
840+ await page . getByRole ( 'option' , { name : 'FALSE' } ) . click ( )
841+ await page . getByTestId ( 'action-bar-save-row' ) . click ( )
842+ await waitForApiResponse ( page , 'pg-meta' , ref , 'query?key=' , { method : 'POST' } )
843+
844+ // Verify FALSE value is preserved
845+ await expect (
846+ page . getByRole ( 'gridcell' , { name : 'FALSE' } ) ,
847+ 'FALSE value should be displayed and preserved'
848+ ) . toBeVisible ( )
849+
850+ // Edit the FALSE value to TRUE using inline editor
851+ const falseCell = page . getByRole ( 'gridcell' , { name : 'FALSE' } ) . first ( )
852+ await falseCell . dblclick ( )
853+
854+ // Wait for boolean editor dropdown to appear
855+ const booleanEditor = page . locator ( '#boolean-editor' )
856+ await expect ( booleanEditor , 'Boolean editor should be visible' ) . toBeVisible ( )
857+
858+ // Change from false to true
859+ await booleanEditor . selectOption ( 'true' )
860+ const updateTrueResponse = waitForApiResponse ( page , 'pg-meta' , ref , 'query?key=' , {
861+ method : 'POST' ,
862+ } )
863+ await page . getByRole ( 'columnheader' , { name : 'id' } ) . click ( )
864+ await updateTrueResponse
865+
866+ // Verify the value changed to TRUE (now there should be 2 TRUE values in the table)
867+ await expect (
868+ page . getByRole ( 'gridcell' , { name : 'TRUE' } ) ,
869+ 'Value should change to TRUE after inline edit'
870+ ) . toHaveCount ( 2 )
871+
872+ // Edit TRUE value back to FALSE using inline editor
873+ // Use the second TRUE cell (the one we just edited from FALSE to TRUE)
874+ const trueCell = page . getByRole ( 'gridcell' , { name : 'TRUE' } ) . nth ( 1 )
875+ await trueCell . dblclick ( )
876+
877+ await expect ( booleanEditor , 'Boolean editor should be visible for second edit' ) . toBeVisible ( )
878+ await booleanEditor . selectOption ( 'false' )
879+ const updateFalseResponse = waitForApiResponse ( page , 'pg-meta' , ref , 'query?key=' , {
880+ method : 'POST' ,
881+ } )
882+ await page . getByRole ( 'columnheader' , { name : 'id' } ) . click ( )
883+ await updateFalseResponse
884+
885+ // Verify FALSE value is preserved and not converted to NULL (this is the critical regression test)
886+ const falseCells = page . getByRole ( 'gridcell' , { name : 'FALSE' } )
887+ await expect (
888+ falseCells . first ( ) ,
889+ 'FALSE value should be preserved and not become NULL after inline edit'
890+ ) . toBeVisible ( )
891+
892+ // Cleanup
893+ await deleteTable ( page , ref , tableName )
894+ } )
895+
896+ test ( 'nullable boolean fields support NULL values' , async ( { page, ref } ) => {
897+ const tableName = 'pw_table_boolean_nullable'
898+ const boolColName = 'is_enabled'
899+
900+ if ( ! page . url ( ) . includes ( '/editor' ) ) {
901+ await page . goto ( toUrl ( `/project/${ ref } /editor?schema=public` ) )
902+ await waitForTableToLoad ( page , ref )
903+ }
904+
905+ await dismissToastsIfAny ( page )
906+
907+ // Create a table with a nullable boolean column
908+ await page . getByRole ( 'button' , { name : 'New table' , exact : true } ) . click ( )
909+ await page . getByTestId ( 'table-name-input' ) . fill ( tableName )
910+ await page . getByTestId ( 'created_at-extra-options' ) . click ( )
911+ await page . getByRole ( 'checkbox' , { name : 'Is Nullable' } ) . click ( )
912+ await page . getByTestId ( 'created_at-extra-options' ) . click ( { force : true } )
913+
914+ // Add nullable boolean column
915+ await page . getByRole ( 'button' , { name : 'Add column' } ) . click ( )
916+ await page . getByRole ( 'textbox' , { name : 'column_name' } ) . fill ( boolColName )
917+ await page . getByText ( 'Choose a column type...' ) . click ( )
918+ await page . getByPlaceholder ( 'Search types...' ) . fill ( 'bool' )
919+ await page . getByRole ( 'option' , { name : 'bool' } ) . first ( ) . click ( )
920+
921+ await page . getByRole ( 'button' , { name : 'Save' } ) . click ( )
922+ await expect (
923+ page . getByText ( `Table ${ tableName } is good to go!` ) ,
924+ 'Success toast should be visible after table creation'
925+ ) . toBeVisible ( { timeout : 50000 } )
926+
927+ await expect (
928+ page . getByRole ( 'button' , { name : `View ${ tableName } ` , exact : true } ) ,
929+ 'Table should be visible after creation'
930+ ) . toBeVisible ( )
931+
932+ // Navigate to the table
933+ await page . getByRole ( 'button' , { name : `View ${ tableName } ` , exact : true } ) . click ( )
934+ await page . waitForURL ( / \/ e d i t o r \/ \d + \? s c h e m a = p u b l i c $ / )
935+
936+ // Insert a row with TRUE value
937+ await page . getByTestId ( 'table-editor-insert-new-row' ) . click ( )
938+ await page . getByRole ( 'menuitem' , { name : 'Insert row Insert a new row' } ) . click ( )
939+ await page . getByRole ( 'combobox' ) . click ( )
940+ await page . getByRole ( 'option' , { name : 'TRUE' } ) . click ( )
941+ await page . getByTestId ( 'action-bar-save-row' ) . click ( )
942+ await waitForApiResponse ( page , 'pg-meta' , ref , 'query?key=' , { method : 'POST' } )
943+
944+ await expect (
945+ page . getByRole ( 'gridcell' , { name : 'TRUE' } ) ,
946+ 'TRUE value should be displayed'
947+ ) . toBeVisible ( )
948+
949+ // Insert a row with FALSE value
950+ await page . getByTestId ( 'table-editor-insert-new-row' ) . click ( )
951+ await page . getByRole ( 'menuitem' , { name : 'Insert row Insert a new row' } ) . click ( )
952+ await page . getByRole ( 'combobox' ) . click ( )
953+ await page . getByRole ( 'option' , { name : 'FALSE' } ) . click ( )
954+ await page . getByTestId ( 'action-bar-save-row' ) . click ( )
955+ await waitForApiResponse ( page , 'pg-meta' , ref , 'query?key=' , { method : 'POST' } )
956+
957+ await expect (
958+ page . getByRole ( 'gridcell' , { name : 'FALSE' } ) ,
959+ 'FALSE value should be displayed'
960+ ) . toBeVisible ( )
961+
962+ // Edit FALSE to NULL using inline editor
963+ const falseCellToNull = page . getByRole ( 'gridcell' , { name : 'FALSE' } )
964+ await falseCellToNull . dblclick ( )
965+
966+ const booleanEditor = page . locator ( '#boolean-editor' )
967+ await expect ( booleanEditor , 'Boolean editor should be visible' ) . toBeVisible ( )
968+
969+ await booleanEditor . selectOption ( 'null' )
970+
971+ const updateNullResponse = waitForApiResponse ( page , 'pg-meta' , ref , 'query?key=' , {
972+ method : 'POST' ,
973+ } )
974+ await page . getByRole ( 'columnheader' , { name : 'id' } ) . click ( )
975+ await updateNullResponse
976+
977+ // Verify value changed to NULL on the second row
978+ const nullCells = page . getByRole ( 'gridcell' , { name : 'NULL' } )
979+ await expect ( nullCells , 'FALSE should change to NULL after inline edit' ) . toBeVisible ( )
980+
981+ // Edit NULL to FALSE using inline editor
982+ const nullCellToFalse = page . getByRole ( 'gridcell' , { name : 'NULL' } )
983+ await nullCellToFalse . dblclick ( )
984+
985+ await booleanEditor . selectOption ( 'false' )
986+
987+ const updateFalseResponse = waitForApiResponse ( page , 'pg-meta' , ref , 'query?key=' , {
988+ method : 'POST' ,
989+ } )
990+ await page . getByRole ( 'columnheader' , { name : 'id' } ) . click ( )
991+ await updateFalseResponse
992+
993+ await expect (
994+ page . getByRole ( 'gridcell' , { name : 'FALSE' } ) ,
995+ 'NULL should change to FALSE after inline edit'
996+ ) . toBeVisible ( )
997+
998+ // Cleanup
999+ await deleteTable ( page , ref , tableName )
1000+ } )
7821001} )
0 commit comments