From bdcd7b6fd043f5738055ed11e35af470eb9827dd Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 30 Oct 2026 01:27:03 +0000 Subject: [PATCH 1/2] test(database): add failing regression test for mode toggle state loss Switching between the dynamic form and the SQLAlchemy URI form in the database modal replaces the whole form state, discarding typed values. Add tests that fill each form, toggle to the other and back, and assert the typed values are still present. --- .../databases/DatabaseModal/index.test.tsx | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx b/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx index f605e6e328f1..35b0b4732b7a 100644 --- a/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx +++ b/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx @@ -1879,6 +1879,63 @@ describe('DatabaseModal', () => { expect(screen.getByTestId('sqlalchemy-uri-input')).toBeInTheDocument(); }); + test('keeps dynamic form values when toggling to the URI form and back', async () => { + setup(); + await selectPostgres(); + + const values: Record = { + host: 'db.example.com', + port: '5432', + database: 'example_db', + username: 'example_user', + password: 'example_password', + }; + Object.entries(values).forEach(([name, value]) => { + fireEvent.change(fieldByName(name), { target: { value } }); + expect(fieldByName(name)).toHaveDisplayValue(value); + }); + + await userEvent.click(screen.getByTestId('sqla-connect-btn')); + expect( + await screen.findByTestId('sqlalchemy-uri-input'), + ).toBeInTheDocument(); + await userEvent.click( + screen.getByRole('button', { + name: /connect this database using the dynamic form instead/i, + }), + ); + + expect(await screen.findByText(/step 2 of 3/i)).toBeInTheDocument(); + Object.entries(values).forEach(([name, value]) => { + expect(fieldByName(name)).toHaveDisplayValue(value); + }); + expect(fieldByName('database_name')).toHaveDisplayValue('PostgreSQL'); + }); + + test('keeps a typed SQLAlchemy URI when toggling to the dynamic form and back', async () => { + setup(); + await selectPostgres(); + + await userEvent.click(screen.getByTestId('sqla-connect-btn')); + const typedUri = + 'postgresql://example_user:example_password@db.example.com:5432/example_db'; + const uriInput = await screen.findByTestId('sqlalchemy-uri-input'); + await userEvent.type(uriInput, typedUri); + expect(uriInput).toHaveValue(typedUri); + + await userEvent.click( + screen.getByRole('button', { + name: /connect this database using the dynamic form instead/i, + }), + ); + expect(await screen.findByText(/step 2 of 3/i)).toBeInTheDocument(); + await userEvent.click(screen.getByTestId('sqla-connect-btn')); + + expect(await screen.findByTestId('sqlalchemy-uri-input')).toHaveValue( + typedUri, + ); + }); + test.each([ { field: 'host', From 5665540ca991b854538ef91c3de136feb0a0c06b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 30 Oct 2026 01:32:31 +0000 Subject: [PATCH 2/2] fix(database): preserve form state when switching connection modes Switching between the dynamic form and the SQLAlchemy URI form in the database modal replaced the whole form state with the three keys sent by the toggle buttons, discarding every typed value (and the engine metadata the form relies on). Merge the toggle payload onto the previous state instead, falling back to the previous database name and engine when the payload leaves them undefined. --- .../databases/DatabaseModal/index.test.tsx | 56 +++++++++++++++++++ .../databases/DatabaseModal/index.tsx | 6 ++ 2 files changed, 62 insertions(+) diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx b/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx index 35b0b4732b7a..88d472f74a15 100644 --- a/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx +++ b/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx @@ -2744,6 +2744,62 @@ describe('dbReducer', () => { }); }); + test('ConfigMethodChange preserves previous fields and applies the new configuration method', () => { + const initialState: Partial = { + database_name: 'Example DB', + engine: 'postgresql', + driver: 'psycopg2', + configuration_method: ConfigurationMethod.DynamicForm, + parameters: { + host: 'db.example.com', + port: 5432, + database: 'example_db', + username: 'example_user', + }, + sqlalchemy_uri: + 'postgresql://example_user@db.example.com:5432/example_db', + masked_encrypted_extra: '{}', + extra: '{"allows_virtual_table_explore":true}', + expose_in_sqllab: true, + engine_information: { supports_file_upload: true }, + }; + const action: DBReducerActionType = { + type: ActionType.ConfigMethodChange, + payload: { + database_name: initialState.database_name, + engine: initialState.engine, + configuration_method: ConfigurationMethod.SqlalchemyUri, + }, + }; + + expect(dbReducer(initialState, action)).toEqual({ + ...initialState, + configuration_method: ConfigurationMethod.SqlalchemyUri, + }); + }); + + test('ConfigMethodChange falls back to the previous database_name and engine', () => { + const initialState: Partial = { + database_name: 'Example DB', + engine: 'postgresql', + configuration_method: ConfigurationMethod.DynamicForm, + }; + const action: DBReducerActionType = { + type: ActionType.ConfigMethodChange, + payload: { + database_name: undefined, + engine: undefined, + configuration_method: ConfigurationMethod.SqlalchemyUri, + }, + }; + + expect(dbReducer(initialState, action)).toEqual({ + database_name: 'Example DB', + engine: 'postgresql', + configuration_method: ConfigurationMethod.SqlalchemyUri, + }); + }); + // Regression test for https://github.com/apache/superset/issues/30504 // When creating a database, the POST response doesn't include engine_information, // but it should be preserved from the state populated when the user selects a diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.tsx b/superset-frontend/src/features/databases/DatabaseModal/index.tsx index c8e41e957d09..96435a7fec95 100644 --- a/superset-frontend/src/features/databases/DatabaseModal/index.tsx +++ b/superset-frontend/src/features/databases/DatabaseModal/index.tsx @@ -634,8 +634,14 @@ export function dbReducer( expose_in_sqllab: true, }; case ActionType.ConfigMethodChange: + // Switching connection mode keeps everything already entered so that + // toggling back and forth does not discard the user's input. return { + ...trimmedState, ...action.payload, + database_name: + action.payload.database_name ?? trimmedState.database_name, + engine: action.payload.engine ?? trimmedState.engine, }; case ActionType.Reset: