codeant-ai-for-open-source[bot] commented on code in PR #36644:
URL: https://github.com/apache/superset/pull/36644#discussion_r2655887290


##########
superset-frontend/packages/superset-ui-core/src/components/AsyncAceEditor/index.tsx:
##########
@@ -286,6 +287,7 @@ export function AsyncAceEditor(
 
                 /* Adjust gutter colors */
                 .ace_editor .ace_gutter {
+                  z-index: 2;
                   background-color: ${token.colorBgElevated} !important;

Review Comment:
   **Suggestion:** Adding `z-index` on `.ace_gutter` without setting a 
positioning context (position: relative/absolute/fixed) is ineffective; make 
the gutter positioned so the z-index takes effect and the intended stacking 
order is reliable. [logic error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
                     position: relative;
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Technically z-index only affects stacking order on positioned elements; 
adding position: relative ensures the gutter's z-index will be honored. It's a 
small, targeted CSS fix to make the intent explicit.
   Be mindful that adding positioning can subtly affect layout, but in this 
case position: relative is the least intrusive option.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/packages/superset-ui-core/src/components/AsyncAceEditor/index.tsx
   **Line:** 291:291
   **Comment:**
        *Logic Error: Adding `z-index` on `.ace_gutter` without setting a 
positioning context (position: relative/absolute/fixed) is ineffective; make 
the gutter positioned so the z-index takes effect and the intended stacking 
order is reliable.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/SaveDatasetActionButton/SaveDatasetActionButton.test.tsx:
##########
@@ -45,10 +41,11 @@ describe('SaveDatasetActionButton', () => {
   });
 
   test('renders a "save dataset" dropdown menu item when user clicks caret 
button', async () => {
+    const onSaveAsExplore = jest.fn();
     render(
       <SaveDatasetActionButton
         setShowSave={() => true}
-        overlayMenu={overlayMenu}
+        onSaveAsExplore={onSaveAsExplore}
       />,
     );
 

Review Comment:
   **Suggestion:** The tests call `userEvent.click` without awaiting it; modern 
userEvent calls are asynchronous and should be awaited to avoid flaky tests β€” 
convert `userEvent.click(...)` to `await userEvent.click(...)` wherever a click 
leads to DOM updates or assertions immediately afterward. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
           await userEvent.click(caretBtn);
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   userEvent.click is asynchronous in modern testing-library; awaiting it 
prevents race conditions between the click and subsequent assertions. This 
change reduces flakiness and is a correct improvement to the test.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/SqlLab/components/SaveDatasetActionButton/SaveDatasetActionButton.test.tsx
   **Line:** 47:51
   **Comment:**
        *Possible Bug: The tests call `userEvent.click` without awaiting it; 
modern userEvent calls are asynchronous and should be awaited to avoid flaky 
tests β€” convert `userEvent.click(...)` to `await userEvent.click(...)` wherever 
a click leads to DOM updates or assertions immediately afterward.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/RunQueryActionButton/index.tsx:
##########
@@ -96,12 +96,12 @@ const StyledButton = styled.span`
 `;
 
 const RunQueryActionButton = ({
-  allowAsync = false,
   queryEditorId,
   queryState,
   overlayCreateAsMenu,
   runQuery,

Review Comment:
   **Suggestion:** The component's props include `allowAsync` but the function 
does not destructure it from props, so `allowAsync` is not available inside the 
component; this leaves the prop unused and prevents forwarding it to 
`runQuery`, causing behavioral regression when callers rely on `allowAsync`. 
[logic error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
     allowAsync,
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The props interface includes allowAsync but the component does not 
destructure it, so any callers setting allowAsync will have no effect. This is 
a real behavioral regression risk and the suggested destructuring is the 
correct, minimal fix.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/SqlLab/components/RunQueryActionButton/index.tsx
   **Line:** 102:102
   **Comment:**
        *Logic Error: The component's props include `allowAsync` but the 
function does not destructure it from props, so `allowAsync` is not available 
inside the component; this leaves the prop unused and prevents forwarding it to 
`runQuery`, causing behavioral regression when callers rely on `allowAsync`.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/StatusBar/index.tsx:
##########
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { styled } from '@apache-superset/core';
+import { Flex } from '@superset-ui/core/components';
+import ViewExtension from 'src/components/ViewExtension';
+import { SQL_EDITOR_STATUSBAR_HEIGHT } from 'src/SqlLab/constants';
+import { ViewContribution } from 'src/SqlLab/contributions';
+
+const Container = styled(Flex)`
+  flex-direction: row-reverse;
+  height: ${SQL_EDITOR_STATUSBAR_HEIGHT}px;
+  background-color: ${({ theme }) => theme.colorPrimary};
+  color: ${({ theme }) => theme.colorWhite};
+  padding: 0 ${({ theme }) => theme.sizeUnit * 4}px;
+
+  & .ant-tag {
+    color: ${({ theme }) => theme.colorWhite};
+    background-color: transparent;
+    border: 0;
+  }
+`;
+
+export interface StatusBarProps {
+  queryEditorId: string;
+}
+
+const StatusBar = () => (
+  <Container align="center" justify="space-bewteen">

Review Comment:
   **Suggestion:** Typo in the `justify` prop value: `"space-bewteen"` is 
misspelled and won't match the expected CSS/flexbox value (`"space-between"`), 
so the layout will not behave as intended. [logic error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
     <Container align="center" justify="space-between">
   ```
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/SqlLab/components/StatusBar/index.tsx
   **Line:** 44:44
   **Comment:**
        *Logic Error: Typo in the `justify` prop value: `"space-bewteen"` is 
misspelled and won't match the expected CSS/flexbox value (`"space-between"`), 
so the layout will not behave as intended.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/components/ViewExtension/ViewExtension.test.tsx:
##########
@@ -0,0 +1,202 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { ReactElement } from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import type { contributions, core } from '@apache-superset/core';
+import ExtensionsManager from 'src/extensions/ExtensionsManager';
+import { ExtensionsProvider } from 'src/extensions/ExtensionsContext';
+import ViewExtension from 'src/components/ViewExtension';
+
+function createMockView(
+  id: string,
+  overrides: Partial<contributions.ViewContribution> = {},
+): contributions.ViewContribution {
+  return {
+    id,
+    name: `${id} View`,
+    ...overrides,
+  };
+}
+
+function createMockExtension(
+  options: Partial<core.Extension> & {
+    views?: Record<string, contributions.ViewContribution[]>;
+  } = {},
+): core.Extension {
+  const {
+    id = 'test-extension',
+    name = 'Test Extension',
+    views = {},
+  } = options;
+
+  return {
+    id,
+    name,
+    description: 'A test extension',
+    version: '1.0.0',
+    dependencies: [],
+    remoteEntry: '',
+    exposedModules: [],
+    extensionDependencies: [],
+    contributions: {
+      commands: [],
+      menus: {},
+      views,
+    },
+    activate: jest.fn(),
+    deactivate: jest.fn(),
+  };
+}
+
+function setupActivatedExtension(
+  manager: ExtensionsManager,
+  extension: core.Extension,
+) {
+  const context = { disposables: [] };
+  (manager as any).contextIndex.set(extension.id, context);
+  (manager as any).extensionContributions.set(extension.id, {
+    commands: extension.contributions.commands,
+    menus: extension.contributions.menus,
+    views: extension.contributions.views,
+  });
+}
+
+async function createActivatedExtension(
+  manager: ExtensionsManager,
+  extensionOptions: Parameters<typeof createMockExtension>[0] = {},
+): Promise<core.Extension> {
+  const mockExtension = createMockExtension(extensionOptions);
+  await manager.initializeExtension(mockExtension);
+  setupActivatedExtension(manager, mockExtension);
+  return mockExtension;
+}
+
+const TEST_VIEW_ID = 'test.view';
+
+const renderWithExtensionsProvider = (ui: ReactElement) => {
+  return render(ui, { wrapper: ExtensionsProvider as any });
+};
+
+beforeEach(() => {
+  (ExtensionsManager as any).instance = undefined;
+});
+
+afterEach(() => {

Review Comment:
   **Suggestion:** Tests only null out the singleton 
`ExtensionsManager.instance` but do not call any cleanup on an existing 
instance, which can leave internal state, timers or subscriptions from a prior 
instance alive and cause test flakiness or memory leaks; call an explicit 
cleanup/dispose on any existing instance before resetting to undefined. 
[resource leak]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
     const existing = (ExtensionsManager as any).instance as ExtensionsManager 
| undefined;
     if (existing && typeof (existing as any).dispose === 'function') {
       (existing as any).dispose();
     }
     (ExtensionsManager as any).instance = undefined;
   });
   
   afterEach(() => {
     const existing = (ExtensionsManager as any).instance as ExtensionsManager 
| undefined;
     if (existing && typeof (existing as any).dispose === 'function') {
       (existing as any).dispose();
     }
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   This is a sensible improvement for test hygiene. The current tests just null 
the singleton which can leave timers/subscriptions alive if the manager holds 
any. Calling a dispose (guarded by a typeof check) is defensive and won't 
change behavior when dispose is absent. It's not speculative β€” it prevents 
potential flakiness and memory leaks in long test runs.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/components/ViewExtension/ViewExtension.test.tsx
   **Line:** 97:100
   **Comment:**
        *Resource Leak: Tests only null out the singleton 
`ExtensionsManager.instance` but do not call any cleanup on an existing 
instance, which can leave internal state, timers or subscriptions from a prior 
instance alive and cause test flakiness or memory leaks; call an explicit 
cleanup/dispose on any existing instance before resetting to undefined.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/components/ViewExtension/ViewExtension.test.tsx:
##########
@@ -0,0 +1,202 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { ReactElement } from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import type { contributions, core } from '@apache-superset/core';
+import ExtensionsManager from 'src/extensions/ExtensionsManager';
+import { ExtensionsProvider } from 'src/extensions/ExtensionsContext';
+import ViewExtension from 'src/components/ViewExtension';
+
+function createMockView(
+  id: string,
+  overrides: Partial<contributions.ViewContribution> = {},
+): contributions.ViewContribution {
+  return {
+    id,
+    name: `${id} View`,
+    ...overrides,
+  };
+}
+
+function createMockExtension(
+  options: Partial<core.Extension> & {
+    views?: Record<string, contributions.ViewContribution[]>;
+  } = {},
+): core.Extension {
+  const {
+    id = 'test-extension',
+    name = 'Test Extension',
+    views = {},
+  } = options;
+
+  return {
+    id,
+    name,
+    description: 'A test extension',
+    version: '1.0.0',
+    dependencies: [],
+    remoteEntry: '',
+    exposedModules: [],
+    extensionDependencies: [],
+    contributions: {
+      commands: [],
+      menus: {},
+      views,
+    },
+    activate: jest.fn(),
+    deactivate: jest.fn(),
+  };
+}
+
+function setupActivatedExtension(
+  manager: ExtensionsManager,
+  extension: core.Extension,
+) {
+  const context = { disposables: [] };
+  (manager as any).contextIndex.set(extension.id, context);
+  (manager as any).extensionContributions.set(extension.id, {
+    commands: extension.contributions.commands,
+    menus: extension.contributions.menus,
+    views: extension.contributions.views,
+  });
+}
+
+async function createActivatedExtension(
+  manager: ExtensionsManager,
+  extensionOptions: Parameters<typeof createMockExtension>[0] = {},
+): Promise<core.Extension> {
+  const mockExtension = createMockExtension(extensionOptions);
+  await manager.initializeExtension(mockExtension);
+  setupActivatedExtension(manager, mockExtension);
+  return mockExtension;
+}
+
+const TEST_VIEW_ID = 'test.view';
+
+const renderWithExtensionsProvider = (ui: ReactElement) => {
+  return render(ui, { wrapper: ExtensionsProvider as any });
+};
+
+beforeEach(() => {
+  (ExtensionsManager as any).instance = undefined;
+});
+
+afterEach(() => {
+  (ExtensionsManager as any).instance = undefined;
+});
+
+test('renders nothing when no view contributions exist', () => {
+  const { container } = renderWithExtensionsProvider(
+    <ViewExtension viewId={TEST_VIEW_ID} />,
+  );
+
+  expect(container.firstChild?.childNodes.length ?? 0).toBe(0);
+});
+
+test('renders placeholder for unregistered view provider', async () => {
+  const manager = ExtensionsManager.getInstance();
+
+  await createActivatedExtension(manager, {
+    views: {
+      [TEST_VIEW_ID]: [createMockView('test-view-1')],
+    },
+  });
+
+  renderWithExtensionsProvider(<ViewExtension viewId={TEST_VIEW_ID} />);
+
+  expect(screen.getByText(/test-view-1/)).toBeInTheDocument();
+});
+
+test('renders multiple view placeholders for multiple contributions', async () 
=> {
+  const manager = ExtensionsManager.getInstance();
+
+  await createActivatedExtension(manager, {
+    views: {
+      [TEST_VIEW_ID]: [
+        createMockView('test-view-1'),
+        createMockView('test-view-2'),
+      ],
+    },
+  });
+
+  renderWithExtensionsProvider(<ViewExtension viewId={TEST_VIEW_ID} />);
+
+  expect(screen.getByText(/test-view-1/)).toBeInTheDocument();
+  expect(screen.getByText(/test-view-2/)).toBeInTheDocument();
+});
+
+test('renders nothing for viewId with no matching contributions', () => {
+  const { container } = renderWithExtensionsProvider(
+    <ViewExtension viewId="nonexistent.view" />,
+  );
+
+  expect(container.firstChild?.childNodes.length ?? 0).toBe(0);
+});
+
+test('handles multiple extensions with views for same viewId', async () => {
+  const manager = ExtensionsManager.getInstance();
+
+  await createActivatedExtension(manager, {
+    id: 'extension-1',
+    views: {
+      [TEST_VIEW_ID]: [createMockView('ext1-view')],
+    },
+  });
+
+  await createActivatedExtension(manager, {
+    id: 'extension-2',
+    views: {
+      [TEST_VIEW_ID]: [createMockView('ext2-view')],
+    },
+  });
+
+  renderWithExtensionsProvider(<ViewExtension viewId={TEST_VIEW_ID} />);
+
+  expect(screen.getByText(/ext1-view/)).toBeInTheDocument();
+  expect(screen.getByText(/ext2-view/)).toBeInTheDocument();
+});
+
+test('renders views for different viewIds independently', async () => {
+  const manager = ExtensionsManager.getInstance();
+  const VIEW_ID_A = 'view.a';
+  const VIEW_ID_B = 'view.b';
+
+  await createActivatedExtension(manager, {
+    views: {
+      [VIEW_ID_A]: [createMockView('view-a-component')],
+      [VIEW_ID_B]: [createMockView('view-b-component')],
+    },
+  });
+
+  const { rerender } = renderWithExtensionsProvider(
+    <ViewExtension viewId={VIEW_ID_A} />,
+  );
+
+  expect(screen.getByText(/view-a-component/)).toBeInTheDocument();
+  expect(screen.queryByText(/view-b-component/)).not.toBeInTheDocument();
+
+  rerender(
+    <ExtensionsProvider>
+      <ViewExtension viewId={VIEW_ID_B} />
+    </ExtensionsProvider>,
+  );

Review Comment:
   **Suggestion:** In the rerender call you re-wrap the UI with a new 
`ExtensionsProvider` instance instead of relying on the original test wrapper; 
this can create nested/different provider instances and make the test behavior 
inconsistent β€” rerender with only the new `ViewExtension` element so the 
original wrapper remains in effect. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
     rerender(<ViewExtension viewId={VIEW_ID_B} />);
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Correct β€” passing a new provider wrapper to rerender may create a different 
provider instance or nesting, altering context state and making the test flaky. 
Rerendering the inner component so the original test wrapper remains in effect 
is the correct and minimal fix.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/components/ViewExtension/ViewExtension.test.tsx
   **Line:** 194:198
   **Comment:**
        *Possible Bug: In the rerender call you re-wrap the UI with a new 
`ExtensionsProvider` instance instead of relying on the original test wrapper; 
this can create nested/different provider instances and make the test behavior 
inconsistent β€” rerender with only the new `ViewExtension` element so the 
original wrapper remains in effect.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/QueryLimitSelect/index.tsx:
##########
@@ -34,6 +34,19 @@ export function convertToNumWithSpaces(num: number) {
   return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1 ');
 }
 
+export function convertToShortNum(num: number) {
+  if (num < 1000) {
+    return num;
+  }
+  if (num < 1_000_000) {
+    return `${num / 1000}K`;
+  }
+  if (num < 1_000_000_000) {
+    return `${num / 1000_000}M`;
+  }
+  return num;

Review Comment:
   **Suggestion:** The function `convertToShortNum` returns numbers in some 
branches and strings in others, producing an inconsistent return type 
(number|string). This can cause unexpected behavior in callers that expect a 
string (rendering, concatenation, or downstream formatting) and defeats 
TypeScript's type-safety; make the function always return a string. [type error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
   export function convertToShortNum(num: number): string {
     if (num < 1000) {
       return `${num}`;
     }
     if (num < 1_000_000) {
       return `${num / 1000}K`;
     }
     if (num < 1_000_000_000) {
       return `${num / 1_000_000}M`;
     }
     return `${num}`;
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The current implementation returns numbers for small and very large inputs 
and strings for K/M branches, which is inconsistent and can surprise callers 
and renderers. Making the function always return a string fixes the 
type/formatting inconsistency and is the correct, minimal change for a UI 
formatting helper.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/SqlLab/components/QueryLimitSelect/index.tsx
   **Line:** 37:47
   **Comment:**
        *Type Error: The function `convertToShortNum` returns numbers in some 
branches and strings in others, producing an inconsistent return type 
(number|string). This can cause unexpected behavior in callers that expect a 
string (rendering, concatenation, or downstream formatting) and defeats 
TypeScript's type-safety; make the function always return a string.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/RunQueryActionButton/index.tsx:
##########
@@ -138,11 +138,11 @@ const RunQueryActionButton = ({
   );
 
   return (
-    <StyledButton>
+    <StyledButton compact={compactMode}>
       <ButtonComponent
         data-test="run-query-action"
         onClick={() =>
-          onClick(shouldShowStopBtn, allowAsync, runQuery, stopQuery, 
logAction)
+          onClick(shouldShowStopBtn, runQuery, stopQuery, logAction)

Review Comment:
   **Suggestion:** `runQuery` accepts an optional boolean (`c?: boolean`) but 
the call site now invokes `runQuery` with no argument; the new `allowAsync` 
prop should be forwarded to `runQuery` to preserve previous behavior β€” wrap 
`runQuery` in a lambda that passes `allowAsync`. [logic error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
             onClick(shouldShowStopBtn, () => runQuery(allowAsync), stopQuery, 
logAction)
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   runQuery has signature (c?: boolean) => void. The PR now calls runQuery with 
no args which can change behavior if callers expected allowAsync to be passed. 
Wrapping runQuery as () => runQuery(allowAsync) (and destructuring allowAsync) 
preserves prior semantics β€” this fixes a real logic issue.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/SqlLab/components/RunQueryActionButton/index.tsx
   **Line:** 145:145
   **Comment:**
        *Logic Error: `runQuery` accepts an optional boolean (`c?: boolean`) 
but the call site now invokes `runQuery` with no argument; the new `allowAsync` 
prop should be forwarded to `runQuery` to preserve previous behavior β€” wrap 
`runQuery` in a lambda that passes `allowAsync`.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/SaveDatasetActionButton/index.tsx:
##########
@@ -19,27 +19,61 @@
 import { t } from '@superset-ui/core';
 import { useTheme } from '@apache-superset/core/ui';
 import { Icons } from '@superset-ui/core/components/Icons';
+import { Menu } from '@superset-ui/core/components/Menu';
 import { Button, DropdownButton } from '@superset-ui/core/components';
 
 interface SaveDatasetActionButtonProps {
   setShowSave: (arg0: boolean) => void;
-  overlayMenu: JSX.Element | null;
+  onSaveAsExplore?: () => void;
+  compactMode?: boolean;
 }
 
 const SaveDatasetActionButton = ({
   setShowSave,
-  overlayMenu,
+  onSaveAsExplore,
+  compactMode,
 }: SaveDatasetActionButtonProps) => {
   const theme = useTheme();
 
-  return !overlayMenu ? (
+  if (compactMode) {
+    return (
+      <>
+        <Button
+          onClick={() => setShowSave(true)}
+          buttonStyle="secondary"
+          icon={<Icons.SaveOutlined />}
+          tooltip={t('Save query')}
+        />
+        {onSaveAsExplore && (
+          <Button
+            onClick={() => onSaveAsExplore?.()}
+            buttonStyle="secondary"
+            icon={<Icons.TableOutlined />}
+            tooltip={t('Save or Overwrite Dataset')}
+          />
+        )}
+      </>
+    );
+  }
+
+  return !onSaveAsExplore ? (
     <Button onClick={() => setShowSave(true)} buttonStyle="primary">
       {t('Save')}
     </Button>
   ) : (
     <DropdownButton
       onClick={() => setShowSave(true)}
-      popupRender={() => overlayMenu}
+      popupRender={() => (
+        <Menu
+          items={[
+            {
+              label: t('Save dataset'),
+              key: 'save-dataset',
+              onClick: onSaveAsExplore,
+            },
+          ]}
+        />

Review Comment:
   **Suggestion:** Logic bug: `onClick={() => setShowSave(true)}` is attached 
to the `DropdownButton`, so clicking the dropdown to open the menu will 
immediately open the save modal instead of waiting for the user to select the 
"Save dataset" menu item; move the `setShowSave(true)` call into the menu 
item's click handler (and ensure the menu item handler also calls 
`onSaveAsExplore`) so the modal opens only after selecting the menu action. 
[logic error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
         popupRender={() => (
           <Menu
             items={[
               {
                 label: t('Save dataset'),
                 key: 'save-dataset',
                 onClick: () => {
                   setShowSave(true);
                   onSaveAsExplore?.();
                 },
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The current code calls setShowSave(true) on the DropdownButton's onClick, so 
simply opening the dropdown will immediately open the save modal β€” that's a 
real UX/logic bug. Moving the setShowSave call into the menu item's onClick 
(and calling onSaveAsExplore there) fixes the behaviour so the modal opens only 
after the user selects the menu action.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/SqlLab/components/SaveDatasetActionButton/index.tsx
   **Line:** 65:75
   **Comment:**
        *Logic Error: Logic bug: `onClick={() => setShowSave(true)}` is 
attached to the `DropdownButton`, so clicking the dropdown to open the menu 
will immediately open the save modal instead of waiting for the user to select 
the "Save dataset" menu item; move the `setShowSave(true)` call into the menu 
item's click handler (and ensure the menu item handler also calls 
`onSaveAsExplore`) so the modal opens only after selecting the menu action.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx:
##########
@@ -41,6 +41,40 @@ const StyledEditableTabs = styled(EditableTabs)`
   height: 100%;
   display: flex;
   flex-direction: column;
+  & .ant-tabs-nav::before {
+    border-color: ${({ theme }) => theme.colorBorder} !important;
+  }
+  & .ant-tabs-nav-add {
+    border-color: ${({ theme }) => theme.colorBorder} !important;
+    height: 34px;
+  }
+  & .ant-tabs-nav-list {
+    align-items: end;
+    padding-top: 1px;
+    column-gap: ${({ theme }) => theme.sizeUnit}px;
+  }
+  & .ant-tabs-tab-active {
+    border-left-color: ${({ theme }) => theme.colorPrimaryActive} !important;
+    border-top-color: ${({ theme }) => theme.colorPrimaryActive} !important;
+    border-right-color: ${({ theme }) => theme.colorPrimaryActive} !important;
+    box-shadow: 0 0 2px ${({ theme }) => theme.colorPrimaryActive} !important;
+    border-top: 2px;

Review Comment:
   **Suggestion:** Invalid CSS shorthand: `border-top: 2px;` is not a valid 
complete border declaration and will be ignored by browsers (missing border 
style and color), so the intended top border will not render. Replace it with a 
full border definition (width, style, color) or set explicit 
border-top-width/style/color. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
       border-top: 2px solid ${({ theme }) => theme.colorPrimaryActive} 
!important;
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The existing declaration `border-top: 2px;` is incomplete (missing style and 
color) and will be ignored by browsers β€” this is a real bug in the styling. 
Replacing it with a full declaration such as `2px solid <color>` (as suggested) 
will actually render the intended top border and align with the other 
border-color rules that are being set.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx
   **Line:** 61:61
   **Comment:**
        *Possible Bug: Invalid CSS shorthand: `border-top: 2px;` is not a valid 
complete border declaration and will be ignored by browsers (missing border 
style and color), so the intended top border will not render. Replace it with a 
full border definition (width, style, color) or set explicit 
border-top-width/style/color.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx:
##########
@@ -41,6 +41,40 @@ const StyledEditableTabs = styled(EditableTabs)`
   height: 100%;
   display: flex;
   flex-direction: column;
+  & .ant-tabs-nav::before {
+    border-color: ${({ theme }) => theme.colorBorder} !important;
+  }
+  & .ant-tabs-nav-add {
+    border-color: ${({ theme }) => theme.colorBorder} !important;
+    height: 34px;
+  }
+  & .ant-tabs-nav-list {
+    align-items: end;

Review Comment:
   **Suggestion:** Invalid CSS value: `align-items: end;` is not a standard 
value for `align-items` (should be `flex-end`, `center`, etc.), so this rule 
will be ignored and tabs may be misaligned; change it to `flex-end` to align 
items at the end of the cross axis. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
       align-items: flex-end;
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   `align-items: end;` is not a valid value for flexbox alignment in standard 
browsers β€” the correct value to align to the cross-axis end is `flex-end`. 
Changing to `flex-end` fixes alignment reliably across browsers.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx
   **Line:** 52:52
   **Comment:**
        *Possible Bug: Invalid CSS value: `align-items: end;` is not a standard 
value for `align-items` (should be `flex-end`, `center`, etc.), so this rule 
will be ignored and tabs may be misaligned; change it to `flex-end` to align 
items at the end of the cross axis.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/SouthPane/index.tsx:
##########
@@ -25,9 +25,11 @@ import { css, styled, useTheme } from 
'@apache-superset/core/ui';
 
 import { removeTables, setActiveSouthPaneTab } from 
'src/SqlLab/actions/sqlLab';
 
-import { Label } from '@superset-ui/core/components';
+import { Flex, Label } from '@superset-ui/core/components';

Review Comment:
   **Suggestion:** Import source mistake: `Label` is imported from 
'@superset-ui/core/components' but elsewhere in this codebase `Label` is 
provided by '@apache-superset/core/ui'. Importing `Label` from the wrong 
package can cause the wrong component (or no component) to be referenced at 
runtime; split the imports and import `Label` from '@apache-superset/core/ui' 
while keeping `Flex` where it belongs. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
   import { Flex } from '@superset-ui/core/components';
   import { Label } from '@apache-superset/core/ui';
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The current import groups Flex and Label from 
'@superset-ui/core/components'. In this repo most UI primitives like Label come 
from the '@apache-superset/core/ui' package (see other imports in this file 
such as css, styled, useTheme). Importing Label from the wrong package can pick 
a different component or fail at runtime. Switching only the Label import to 
'@apache-superset/core/ui' is a targeted, correct fix and resolves a potential 
runtime/consistency issue.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/SqlLab/components/SouthPane/index.tsx
   **Line:** 28:28
   **Comment:**
        *Possible Bug: Import source mistake: `Label` is imported from 
'@superset-ui/core/components' but elsewhere in this codebase `Label` is 
provided by '@apache-superset/core/ui'. Importing `Label` from the wrong 
package can cause the wrong component (or no component) to be referenced at 
runtime; split the imports and import `Label` from '@apache-superset/core/ui' 
while keeping `Flex` where it belongs.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/SqlEditorTopBar/SqlEditorTopBar.test.tsx:
##########
@@ -0,0 +1,130 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { render, screen } from 'spec/helpers/testing-library';
+import { MenuItemType } from '@superset-ui/core/components/Menu';

Review Comment:
   **Suggestion:** `MenuItemType` is only used for type annotations in the test 
mock; importing it as a value import may inadvertently include runtime module 
code. Use a type-only import (`import type { MenuItemType } ...`) to ensure 
it's erased at compile time and avoid pulling runtime code into tests. 
[possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
   import type { MenuItemType } from '@superset-ui/core/components/Menu';
   ```
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/SqlLab/components/SqlEditorTopBar/SqlEditorTopBar.test.tsx
   **Line:** 20:20
   **Comment:**
        *Possible Bug: `MenuItemType` is only used for type annotations in the 
test mock; importing it as a value import may inadvertently include runtime 
module code. Use a type-only import (`import type { MenuItemType } ...`) to 
ensure it's erased at compile time and avoid pulling runtime code into tests.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.ts:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useEffect, useCallback, useMemo, useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+
+import { SqlLabRootState } from 'src/SqlLab/types';
+import {
+  queryEditorSetDb,
+  queryEditorSetCatalog,
+  queryEditorSetSchema,
+  setDatabases,
+  addDangerToast,
+} from 'src/SqlLab/actions/sqlLab';
+import { type DatabaseObject } from 'src/components';
+import useQueryEditor from 'src/SqlLab/hooks/useQueryEditor';
+import {
+  getItem,
+  LocalStorageKeys,
+  setItem,
+} from 'src/utils/localStorageHelpers';
+
+export default function useDatabaseSelector(queryEditorId: string) {
+  const databases = useSelector<
+    SqlLabRootState,
+    SqlLabRootState['sqlLab']['databases']
+  >(({ sqlLab }) => sqlLab.databases);
+  const dispatch = useDispatch();
+  const queryEditor = useQueryEditor(queryEditorId, [
+    'dbId',
+    'catalog',
+    'schema',
+    'tabViewId',
+  ]);
+  const database = useMemo(
+    () => (queryEditor.dbId ? databases[queryEditor.dbId] : undefined),
+    [databases, queryEditor.dbId],
+  );
+  const [userSelectedDb, setUserSelected] = useState<DatabaseObject | null>(
+    null,
+  );
+  const { catalog, schema } = queryEditor;

Review Comment:
   **Suggestion:** Potential runtime error: destructuring `queryEditor` with 
`const { catalog, schema } = queryEditor;` will throw if `queryEditor` is 
undefined. Use optional chaining with safe defaults to avoid a crash when 
`queryEditor` is not present. [null pointer]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
     const catalog = queryEditor?.catalog ?? null;
     const schema = queryEditor?.schema ?? null;
   ```
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.ts
   **Line:** 57:57
   **Comment:**
        *Null Pointer: Potential runtime error: destructuring `queryEditor` 
with `const { catalog, schema } = queryEditor;` will throw if `queryEditor` is 
undefined. Use optional chaining with safe defaults to avoid a crash when 
`queryEditor` is not present.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.tsx:
##########
@@ -321,7 +321,7 @@ describe('SqlEditor', () => {
     const defaultQueryLimit = 101;
     const updatedProps = { ...mockedProps, defaultQueryLimit };
     const { findByText } = setup(updatedProps, store);
-    fireEvent.click(await findByText('LIMIT:'));
+    fireEvent.click(await findByText('Limit'));

Review Comment:
   **Suggestion:** Using `fireEvent.click` on the "Limit" control can fail to 
open a Material-UI (or similar) select/dropdown because those components listen 
for the `mouseDown` event to open; the test will be flaky or fail because the 
dropdown never appears. Replace the click with `fireEvent.mouseDown` to 
reliably open the menu. [logic error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
       fireEvent.mouseDown(await findByText('Limit'));
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Material‑UI Select/Menu components often open on mouseDown rather than 
click; replacing the click with fireEvent.mouseDown is a pragmatic, accurate 
fix that prevents a flaky test. The existing code matches the PR hunk and the 
improved code is syntactically correct and scoped to the test.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.tsx
   **Line:** 324:324
   **Comment:**
        *Logic Error: Using `fireEvent.click` on the "Limit" control can fail 
to open a Material-UI (or similar) select/dropdown because those components 
listen for the `mouseDown` event to open; the test will be flaky or fail 
because the dropdown never appears. Replace the click with 
`fireEvent.mouseDown` to reliably open the menu.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.test.ts:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import configureStore from 'redux-mock-store';
+import thunk from 'redux-thunk';
+import { renderHook, act } from '@testing-library/react-hooks';
+import { createWrapper } from 'spec/helpers/testing-library';
+import { initialState, defaultQueryEditor } from 'src/SqlLab/fixtures';
+import * as localStorageHelpers from 'src/utils/localStorageHelpers';
+
+import useDatabaseSelector from './useDatabaseSelector';
+
+const middlewares = [thunk];
+const mockStore = configureStore(middlewares);
+
+const mockDatabase = {
+  id: 1,
+  database_name: 'main',
+  backend: 'mysql',
+};
+
+const mockDatabases = {
+  [mockDatabase.id]: mockDatabase,
+};
+
+const createInitialState = (overrides = {}) => ({
+  ...initialState,
+  sqlLab: {
+    ...initialState.sqlLab,
+    databases: mockDatabases,
+    ...overrides,
+  },
+});
+
+beforeEach(() => {
+  jest.spyOn(localStorageHelpers, 'getItem').mockReturnValue(null);
+  jest.spyOn(localStorageHelpers, 'setItem').mockImplementation(() => {});
+});
+
+afterEach(() => {
+  jest.clearAllMocks();
+  jest.restoreAllMocks();
+});
+
+test('returns initial values from query editor', () => {
+  const store = mockStore(createInitialState());
+  const { result } = renderHook(
+    () => useDatabaseSelector(defaultQueryEditor.id),
+    {
+      wrapper: createWrapper({
+        useRedux: true,
+        store,
+      }),
+    },
+  );
+
+  expect(result.current.catalog).toBe(defaultQueryEditor.catalog);
+  expect(result.current.schema).toBe(defaultQueryEditor.schema);
+  expect(typeof result.current.onDbChange).toBe('function');
+  expect(typeof result.current.onCatalogChange).toBe('function');
+  expect(typeof result.current.onSchemaChange).toBe('function');
+  expect(typeof result.current.getDbList).toBe('function');
+  expect(typeof result.current.handleError).toBe('function');
+});
+
+test('returns database when dbId exists in store', () => {
+  const store = mockStore(
+    createInitialState({
+      unsavedQueryEditor: {
+        id: defaultQueryEditor.id,
+        dbId: mockDatabase.id,
+      },
+    }),
+  );
+
+  const { result, rerender } = renderHook(
+    () => useDatabaseSelector(defaultQueryEditor.id),
+    {
+      wrapper: createWrapper({
+        useRedux: true,
+        store,
+      }),
+    },
+  );
+
+  // Trigger effect by rerendering
+  rerender();

Review Comment:
   **Suggestion:** Race condition / missing act: the test triggers a state 
update in a useEffect by calling `rerender()` but does not await it or wrap it 
in `act`, so the assertion can run before React finishes updating state and 
cause flaky failures; mark the test async and wrap `rerender()` in `await 
act(async () => { ... })`. [race condition]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
   test('returns database when dbId exists in store', async () => {
     const store = mockStore(
       createInitialState({
         unsavedQueryEditor: {
           id: defaultQueryEditor.id,
           dbId: mockDatabase.id,
         },
       }),
     );
   
     const { result, rerender } = renderHook(
       () => useDatabaseSelector(defaultQueryEditor.id),
       {
         wrapper: createWrapper({
           useRedux: true,
           store,
         }),
       },
     );
   
     // Trigger effect by rerendering and wait for state updates
     await act(async () => {
       rerender();
     });
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The test triggers an effect-driven state change via rerender() and does not 
wrap/await it in act; that can produce flakiness and React warnings. Changing 
the test to async and awaiting the rerender inside act resolves a real 
timing/race issue in tests.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.test.ts
   **Line:** 81:102
   **Comment:**
        *Race Condition: Race condition / missing act: the test triggers a 
state update in a useEffect by calling `rerender()` but does not await it or 
wrap it in `act`, so the assertion can run before React finishes updating state 
and cause flaky failures; mark the test async and wrap `rerender()` in `await 
act(async () => { ... })`.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.ts:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useEffect, useCallback, useMemo, useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+
+import { SqlLabRootState } from 'src/SqlLab/types';
+import {
+  queryEditorSetDb,
+  queryEditorSetCatalog,
+  queryEditorSetSchema,
+  setDatabases,
+  addDangerToast,
+} from 'src/SqlLab/actions/sqlLab';
+import { type DatabaseObject } from 'src/components';
+import useQueryEditor from 'src/SqlLab/hooks/useQueryEditor';
+import {
+  getItem,
+  LocalStorageKeys,
+  setItem,
+} from 'src/utils/localStorageHelpers';
+
+export default function useDatabaseSelector(queryEditorId: string) {
+  const databases = useSelector<
+    SqlLabRootState,
+    SqlLabRootState['sqlLab']['databases']
+  >(({ sqlLab }) => sqlLab.databases);
+  const dispatch = useDispatch();
+  const queryEditor = useQueryEditor(queryEditorId, [
+    'dbId',
+    'catalog',
+    'schema',
+    'tabViewId',
+  ]);
+  const database = useMemo(
+    () => (queryEditor.dbId ? databases[queryEditor.dbId] : undefined),
+    [databases, queryEditor.dbId],

Review Comment:
   **Suggestion:** Logic bug: the truthy check `queryEditor.dbId ? ... : 
undefined` treats a valid database id of 0 as falsy and will incorrectly return 
undefined instead of the database; also the dependency array reads 
`queryEditor.dbId` which will throw if `queryEditor` is null/undefined. Change 
to use a null/undefined check and optional chaining in the dependency list so 
dbId === 0 is handled and safe when `queryEditor` may be absent. [logic error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
       () =>
         queryEditor?.dbId != null
           ? databases[queryEditor.dbId]
           : undefined,
       [databases, queryEditor?.dbId],
   ```
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.ts
   **Line:** 51:52
   **Comment:**
        *Logic Error: Logic bug: the truthy check `queryEditor.dbId ? ... : 
undefined` treats a valid database id of 0 as falsy and will incorrectly return 
undefined instead of the database; also the dependency array reads 
`queryEditor.dbId` which will throw if `queryEditor` is null/undefined. Change 
to use a null/undefined check and optional chaining in the dependency list so 
dbId === 0 is handled and safe when `queryEditor` may be absent.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/SqlEditorTopBar/index.tsx:
##########
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Flex } from '@superset-ui/core/components';

Review Comment:
   **Suggestion:** The file references the type `React.ReactNode` but does not 
import React; add an explicit React import so the `React` symbol is available 
for type annotations and to avoid relying on global ambient typings or a 
specific TS JSX configuration. [type error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
   import * as React from 'react';
   ```
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/SqlLab/components/SqlEditorTopBar/index.tsx
   **Line:** 19:19
   **Comment:**
        *Type Error: The file references the type `React.ReactNode` but does 
not import React; add an explicit React import so the `React` symbol is 
available for type annotations and to avoid relying on global ambient typings 
or a specific TS JSX configuration.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/StatusBar/StatusBar.test.tsx:
##########
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { render, screen } from 'spec/helpers/testing-library';
+import StatusBar from 'src/SqlLab/components/StatusBar';
+
+jest.mock('src/components/ViewExtension', () => ({
+  __esModule: true,
+  default: ({ viewId }: { viewId: string }) => (
+    <div data-test="mock-view-extension" data-view-id={viewId}>

Review Comment:
   **Suggestion:** Test queries use getByTestId (which looks for the 
data-testid attribute) but the mocked component sets data-test, so getByTestId 
will not find the element and the tests will fail; change the attribute to 
data-testid. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
       <div data-testid="mock-view-extension" data-view-id={viewId}>
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The tests call screen.getByTestId('mock-view-extension') but the mock sets 
data-test, so the queries will fail to locate the mocked element. Changing the 
mock to use data-testid fixes a real test failure without side effects.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/SqlLab/components/StatusBar/StatusBar.test.tsx
   **Line:** 25:25
   **Comment:**
        *Possible Bug: Test queries use getByTestId (which looks for the 
data-testid attribute) but the mocked component sets data-test, so getByTestId 
will not find the element and the tests will fail; change the attribute to 
data-testid.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/SqlEditorTopBar/SqlEditorTopBar.test.tsx:
##########
@@ -0,0 +1,130 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { render, screen } from 'spec/helpers/testing-library';
+import { MenuItemType } from '@superset-ui/core/components/Menu';
+import SqlEditorTopBar, {
+  SqlEditorTopBarProps,
+} from 'src/SqlLab/components/SqlEditorTopBar';
+
+jest.mock('src/components/MenuExtension', () => ({
+  __esModule: true,
+  default: ({
+    children,
+    viewId,
+    primary,
+    secondary,
+    defaultItems,
+  }: {
+    children?: React.ReactNode;
+    viewId: string;
+    primary?: boolean;
+    secondary?: boolean;
+    defaultItems?: MenuItemType[];
+  }) => (
+    <div
+      data-test="mock-menu-extension"

Review Comment:
   **Suggestion:** The test mock sets the attribute 
`data-test="mock-menu-extension"` but the tests call 
`screen.getAllByTestId('mock-menu-extension')` which queries elements by 
`data-testid`. This mismatch causes the queries to fail at runtime (no elements 
found). Change the mock to set `data-testid` instead of `data-test`. [possible 
bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
         data-testid="mock-menu-extension"
   ```
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/SqlLab/components/SqlEditorTopBar/SqlEditorTopBar.test.tsx
   **Line:** 41:41
   **Comment:**
        *Possible Bug: The test mock sets the attribute 
`data-test="mock-menu-extension"` but the tests call 
`screen.getAllByTestId('mock-menu-extension')` which queries elements by 
`data-testid`. This mismatch causes the queries to fail at runtime (no elements 
found). Change the mock to set `data-testid` instead of `data-test`.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/components/MenuExtension/index.tsx:
##########
@@ -0,0 +1,147 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useMemo } from 'react';
+import { css, useTheme } from '@apache-superset/core/ui';
+import { Button, Dropdown } from '@superset-ui/core/components';
+import { Menu, MenuItemType } from '@superset-ui/core/components/Menu';
+import { Icons } from '@superset-ui/core/components/Icons';
+import { commands } from 'src/core';
+import ExtensionsManager from 'src/extensions/ExtensionsManager';
+
+export type MenuExtensionProps = {
+  viewId: string;
+} & (
+  | {
+      primary: boolean;
+      secondary?: never;
+      children?: React.ReactNode;
+      defaultItems?: never;
+      compactMode?: boolean;
+    }
+  | {
+      primary?: never;
+      secondary: boolean;
+      children?: never;
+      defaultItems?: MenuItemType[];
+      compactMode?: never;
+    }
+);
+
+const MenuExtension = ({
+  viewId,
+  primary,
+  secondary,
+  defaultItems,
+  children,
+  compactMode,
+}: MenuExtensionProps) => {
+  const theme = useTheme();
+  const iconColor = theme.colorPrimary;
+  const contributions =
+    ExtensionsManager.getInstance().getMenuContributions(viewId);
+
+  const actions = primary ? contributions?.primary : contributions?.secondary;
+  const primaryActions = useMemo(
+    () =>
+      primary
+        ? (actions || []).map(contribution => {
+            const command =
+              ExtensionsManager.getInstance().getCommandContribution(
+                contribution.command,
+              )!;
+            // @ts-ignore
+            const Icon = Icons[command?.icon as IconNameType];
+
+            return (
+              <Button
+                key={contribution.view}
+                onClick={() => commands.executeCommand(command.command)}
+                tooltip={command?.description}
+                icon={<Icon iconSize="m" iconColor={iconColor} />}
+                buttonSize="small"
+              >
+                {!compactMode ? command?.title : undefined}
+              </Button>
+            );
+          })
+        : [],
+    [actions, primary, iconColor, compactMode],
+  );
+  const secondaryActions = useMemo(
+    () =>
+      secondary
+        ? (actions || [])
+            .map(contribution => {
+              const command =
+                ExtensionsManager.getInstance().getCommandContribution(
+                  contribution.command,
+                )!;
+              return {
+                key: command.command,
+                label: command.title,
+                title: command.description,
+                onClick: () => commands.executeCommand(command.command),
+              } as MenuItemType;
+            })
+            .concat(...(defaultItems || []))

Review Comment:
   **Suggestion:** Secondary menu items mapping uses a non-null assertion and 
will crash if `getCommandContribution` returns undefined; also the code uses 
`concat(...(defaultItems || []))` which unnecessarily spreads the array and is 
error-proneβ€”change to check for missing commands and use `concat(defaultItems 
|| [])`. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
                 const command = 
ExtensionsManager.getInstance().getCommandContribution(
                   contribution.command,
                 );
                 if (!command) {
                   return null;
                 }
                 return {
                   key: command.command,
                   label: command.title,
                   title: command.description,
                   onClick: () => commands.executeCommand(command.command),
                 } as MenuItemType;
               })
               .concat(defaultItems || [])
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   This hunk also uses the non-null assertion which can cause the same runtime 
crash if a command isn't found. Additionally, .concat(...(defaultItems || [])) 
is unnecessary and harder to reason about β€” concat(defaultItems || []) is the 
correct, simpler usage. The proposed fix removes the crash vector and 
simplifies concatenation.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/components/MenuExtension/index.tsx
   **Line:** 91:102
   **Comment:**
        *Possible Bug: Secondary menu items mapping uses a non-null assertion 
and will crash if `getCommandContribution` returns undefined; also the code 
uses `concat(...(defaultItems || []))` which unnecessarily spreads the array 
and is error-proneβ€”change to check for missing commands and use 
`concat(defaultItems || [])`.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/components/MenuExtension/index.tsx:
##########
@@ -0,0 +1,147 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useMemo } from 'react';
+import { css, useTheme } from '@apache-superset/core/ui';
+import { Button, Dropdown } from '@superset-ui/core/components';
+import { Menu, MenuItemType } from '@superset-ui/core/components/Menu';
+import { Icons } from '@superset-ui/core/components/Icons';
+import { commands } from 'src/core';
+import ExtensionsManager from 'src/extensions/ExtensionsManager';
+
+export type MenuExtensionProps = {
+  viewId: string;
+} & (
+  | {
+      primary: boolean;
+      secondary?: never;
+      children?: React.ReactNode;
+      defaultItems?: never;
+      compactMode?: boolean;
+    }
+  | {
+      primary?: never;
+      secondary: boolean;
+      children?: never;
+      defaultItems?: MenuItemType[];
+      compactMode?: never;
+    }
+);
+
+const MenuExtension = ({
+  viewId,
+  primary,
+  secondary,
+  defaultItems,
+  children,
+  compactMode,
+}: MenuExtensionProps) => {
+  const theme = useTheme();
+  const iconColor = theme.colorPrimary;
+  const contributions =
+    ExtensionsManager.getInstance().getMenuContributions(viewId);
+
+  const actions = primary ? contributions?.primary : contributions?.secondary;
+  const primaryActions = useMemo(
+    () =>
+      primary
+        ? (actions || []).map(contribution => {
+            const command =
+              ExtensionsManager.getInstance().getCommandContribution(
+                contribution.command,
+              )!;
+            // @ts-ignore
+            const Icon = Icons[command?.icon as IconNameType];
+
+            return (
+              <Button
+                key={contribution.view}
+                onClick={() => commands.executeCommand(command.command)}
+                tooltip={command?.description}
+                icon={<Icon iconSize="m" iconColor={iconColor} />}

Review Comment:
   **Suggestion:** Dynamic icon lookup can yield undefined (or a non-component) 
and rendering `<Icon />` will throw "Element type is invalid"; ensure the 
looked-up `Icon` is a valid component and render a safe fallback or omit the 
icon. [type error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
               const Icon = Icons[command?.icon as any];
   
               return (
                 <Button
                   key={contribution.view}
                   onClick={() => commands.executeCommand(command.command)}
                   tooltip={command?.description}
                   icon={Icon ? <Icon iconSize="m" iconColor={iconColor} /> : 
undefined}
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The Icons lookup can return undefined or a non-component value; rendering 
<Icon /> in that case throws "Element type is invalid". The improved code 
checks Icon and conditionally renders the icon (or omits it), which prevents 
that runtime error and is a minimal, correct safeguard.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/components/MenuExtension/index.tsx
   **Line:** 69:76
   **Comment:**
        *Type Error: Dynamic icon lookup can yield undefined (or a 
non-component) and rendering `<Icon />` will throw "Element type is invalid"; 
ensure the looked-up `Icon` is a valid component and render a safe fallback or 
omit the icon.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/components/ViewExtension/index.tsx:
##########
@@ -16,18 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import ExtensionsManager from 'src/extensions/ExtensionsManager';
+import { useExtensionsContext } from 'src/extensions/ExtensionsContext';
 
-/**
- * Jest configuration for @storybook/test-runner
- *
- * This extends the default test-runner config with custom timeouts
- * to handle slow story rendering in CI environments.
- */
-const { getJestConfig } = require('@storybook/test-runner');
-const testRunnerConfig = getJestConfig();
+export interface ViewExtensionProps {
+  viewId: string;
+}
 
-module.exports = {
-  ...testRunnerConfig,
-  // Increase timeout from default 15s to 60s for CI environments
-  testTimeout: 60000,
+const ViewExtension = ({ viewId }: ViewExtensionProps) => {
+  const contributions =
+    ExtensionsManager.getInstance().getViewContributions(viewId) || [];
+  const { getView } = useExtensionsContext();
+
+  return <>{contributions.map(contribution => getView(contribution.id))}</>;

Review Comment:
   **Suggestion:** Rendering a list of views without stable React keys can 
produce UI bugs and warnings; mapping `contributions` directly to elements 
returns an array of children without keys which can cause incorrect element 
reuse and rendering issues. Wrap each rendered view in an element with a stable 
`key` (e.g., the contribution id) and guard against missing ids. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
     return (
       <>
         {contributions
           .filter(contribution => contribution && typeof contribution.id !== 
'undefined')
           .map(contribution => (
             <span 
key={String(contribution.id)}>{getView(contribution.id)}</span>
           ))}
       </>
     );
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Mapping an array of children without stable keys can produce React warnings 
and incorrect element reuse. The suggested change adds a stable key per item 
which addresses the real issue. Note: the proposed wrapper uses a <span> which 
will add extra DOM; prefer using <React.Fragment key={...}> to avoid altering 
markup or ensure getView already returns keyed elements.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/components/ViewExtension/index.tsx
   **Line:** 31:31
   **Comment:**
        *Possible Bug: Rendering a list of views without stable React keys can 
produce UI bugs and warnings; mapping `contributions` directly to elements 
returns an array of children without keys which can cause incorrect element 
reuse and rendering issues. Wrap each rendered view in an element with a stable 
`key` (e.g., the contribution id) and guard against missing ids.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/components/MenuExtension/MenuExtension.test.tsx:
##########
@@ -0,0 +1,374 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { render, screen, waitFor } from 'spec/helpers/testing-library';
+import userEvent from '@testing-library/user-event';
+import type { contributions, core } from '@apache-superset/core';
+import ExtensionsManager from 'src/extensions/ExtensionsManager';
+import { commands } from 'src/core';
+import MenuExtension from 'src/components/MenuExtension';
+
+jest.mock('src/core', () => ({
+  commands: {
+    executeCommand: jest.fn(),
+  },
+}));
+
+function createMockCommand(
+  command: string,
+  overrides: Partial<contributions.CommandContribution> = {},
+): contributions.CommandContribution {
+  return {
+    command,
+    icon: 'PlusOutlined',
+    title: `${command} Title`,
+    description: `${command} description`,
+    ...overrides,
+  };
+}
+
+function createMockMenuItem(
+  view: string,
+  command: string,
+): contributions.MenuItem {
+  return {
+    view,
+    command,
+  };
+}
+
+function createMockMenu(
+  overrides: Partial<contributions.MenuContribution> = {},
+): contributions.MenuContribution {
+  return {
+    context: [],
+    primary: [],
+    secondary: [],
+    ...overrides,
+  };
+}
+
+function createMockExtension(
+  options: Partial<core.Extension> & {
+    commands?: contributions.CommandContribution[];
+    menus?: Record<string, contributions.MenuContribution>;
+  } = {},
+): core.Extension {
+  const {
+    id = 'test-extension',
+    name = 'Test Extension',
+    commands: cmds = [],
+    menus = {},
+  } = options;
+
+  return {
+    id,
+    name,
+    description: 'A test extension',
+    version: '1.0.0',
+    dependencies: [],
+    remoteEntry: '',
+    exposedModules: [],
+    extensionDependencies: [],
+    contributions: {
+      commands: cmds,
+      menus,
+      views: {},
+    },
+    activate: jest.fn(),
+    deactivate: jest.fn(),
+  };
+}
+
+function setupActivatedExtension(
+  manager: ExtensionsManager,
+  extension: core.Extension,
+) {
+  const context = { disposables: [] };
+  (manager as any).contextIndex.set(extension.id, context);
+  (manager as any).extensionContributions.set(extension.id, {
+    commands: extension.contributions.commands,
+    menus: extension.contributions.menus,
+    views: extension.contributions.views,
+  });
+}
+
+async function createActivatedExtension(
+  manager: ExtensionsManager,
+  extensionOptions: Parameters<typeof createMockExtension>[0] = {},
+): Promise<core.Extension> {
+  const mockExtension = createMockExtension(extensionOptions);
+  await manager.initializeExtension(mockExtension);
+  setupActivatedExtension(manager, mockExtension);
+  return mockExtension;
+}
+
+const TEST_VIEW_ID = 'test.menu';
+
+beforeEach(() => {
+  (ExtensionsManager as any).instance = undefined;
+  jest.clearAllMocks();
+});
+
+afterEach(() => {
+  (ExtensionsManager as any).instance = undefined;
+});
+
+test('renders children when primary mode with no extensions', () => {
+  render(
+    <MenuExtension viewId={TEST_VIEW_ID} primary>
+      <button type="button">Child Button</button>
+    </MenuExtension>,
+  );
+
+  expect(
+    screen.getByRole('button', { name: 'Child Button' }),
+  ).toBeInTheDocument();
+});
+
+test('renders primary actions from extension contributions', async () => {
+  const manager = ExtensionsManager.getInstance();
+
+  await createActivatedExtension(manager, {
+    commands: [createMockCommand('test.action')],
+    menus: {
+      [TEST_VIEW_ID]: createMockMenu({
+        primary: [createMockMenuItem('test-view', 'test.action')],
+      }),
+    },
+  });
+
+  render(<MenuExtension viewId={TEST_VIEW_ID} primary />);
+
+  expect(screen.getByText('test.action Title')).toBeInTheDocument();
+});
+
+test('renders primary actions with children', async () => {
+  const manager = ExtensionsManager.getInstance();
+
+  await createActivatedExtension(manager, {
+    commands: [createMockCommand('test.action')],
+    menus: {
+      [TEST_VIEW_ID]: createMockMenu({
+        primary: [createMockMenuItem('test-view', 'test.action')],
+      }),
+    },
+  });
+
+  render(
+    <MenuExtension viewId={TEST_VIEW_ID} primary>
+      <button type="button">Child Button</button>
+    </MenuExtension>,
+  );
+
+  expect(screen.getByText('test.action Title')).toBeInTheDocument();
+  expect(
+    screen.getByRole('button', { name: 'Child Button' }),
+  ).toBeInTheDocument();
+});
+
+test('hides title in compact mode for primary actions', async () => {
+  const manager = ExtensionsManager.getInstance();
+
+  await createActivatedExtension(manager, {
+    commands: [createMockCommand('test.action')],
+    menus: {
+      [TEST_VIEW_ID]: createMockMenu({
+        primary: [createMockMenuItem('test-view', 'test.action')],
+      }),
+    },
+  });
+
+  render(<MenuExtension viewId={TEST_VIEW_ID} primary compactMode />);
+
+  expect(screen.queryByText('test.action Title')).not.toBeInTheDocument();
+  expect(screen.getByRole('button')).toBeInTheDocument();
+});
+
+test('executes command when primary action button is clicked', async () => {
+  const manager = ExtensionsManager.getInstance();
+
+  await createActivatedExtension(manager, {
+    commands: [createMockCommand('test.action')],
+    menus: {
+      [TEST_VIEW_ID]: createMockMenu({
+        primary: [createMockMenuItem('test-view', 'test.action')],
+      }),
+    },
+  });
+
+  render(<MenuExtension viewId={TEST_VIEW_ID} primary />);
+
+  const button = screen.getByText('test.action Title').closest('button')!;

Review Comment:
   **Suggestion:** Locating the primary action's button via 
`.closest('button')` on a text node can return null and cause a runtime error; 
use a role-based query that finds the actual button by accessible name to avoid 
a potential TypeError and make the test less brittle. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
     const button = screen.getByRole('button', { name: 'test.action Title' });
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The change makes the test more robust and idiomatic: querying by role with 
the accessible name avoids a potential null from .closest() and better reflects 
how users interact with the UI. It's a simple, valid improvement that doesn't 
change behaviour and reduces flakiness.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/components/MenuExtension/MenuExtension.test.tsx
   **Line:** 217:217
   **Comment:**
        *Possible Bug: Locating the primary action's button via 
`.closest('button')` on a text node can return null and cause a runtime error; 
use a role-based query that finds the actual button by accessible name to avoid 
a potential TypeError and make the test less brittle.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.test.ts:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import configureStore from 'redux-mock-store';
+import thunk from 'redux-thunk';
+import { renderHook, act } from '@testing-library/react-hooks';
+import { createWrapper } from 'spec/helpers/testing-library';
+import { initialState, defaultQueryEditor } from 'src/SqlLab/fixtures';
+import * as localStorageHelpers from 'src/utils/localStorageHelpers';
+
+import useDatabaseSelector from './useDatabaseSelector';
+
+const middlewares = [thunk];
+const mockStore = configureStore(middlewares);
+
+const mockDatabase = {
+  id: 1,
+  database_name: 'main',
+  backend: 'mysql',
+};
+
+const mockDatabases = {
+  [mockDatabase.id]: mockDatabase,
+};
+
+const createInitialState = (overrides = {}) => ({
+  ...initialState,
+  sqlLab: {
+    ...initialState.sqlLab,
+    databases: mockDatabases,
+    ...overrides,
+  },
+});
+
+beforeEach(() => {
+  jest.spyOn(localStorageHelpers, 'getItem').mockReturnValue(null);
+  jest.spyOn(localStorageHelpers, 'setItem').mockImplementation(() => {});
+});
+
+afterEach(() => {
+  jest.clearAllMocks();
+  jest.restoreAllMocks();
+});
+
+test('returns initial values from query editor', () => {
+  const store = mockStore(createInitialState());
+  const { result } = renderHook(
+    () => useDatabaseSelector(defaultQueryEditor.id),
+    {
+      wrapper: createWrapper({
+        useRedux: true,
+        store,
+      }),
+    },
+  );
+
+  expect(result.current.catalog).toBe(defaultQueryEditor.catalog);
+  expect(result.current.schema).toBe(defaultQueryEditor.schema);
+  expect(typeof result.current.onDbChange).toBe('function');
+  expect(typeof result.current.onCatalogChange).toBe('function');
+  expect(typeof result.current.onSchemaChange).toBe('function');
+  expect(typeof result.current.getDbList).toBe('function');
+  expect(typeof result.current.handleError).toBe('function');
+});
+
+test('returns database when dbId exists in store', () => {
+  const store = mockStore(
+    createInitialState({
+      unsavedQueryEditor: {
+        id: defaultQueryEditor.id,
+        dbId: mockDatabase.id,
+      },
+    }),
+  );
+
+  const { result, rerender } = renderHook(
+    () => useDatabaseSelector(defaultQueryEditor.id),
+    {
+      wrapper: createWrapper({
+        useRedux: true,
+        store,
+      }),
+    },
+  );
+
+  // Trigger effect by rerendering
+  rerender();
+
+  expect(result.current.db).toEqual(mockDatabase);
+});
+
+test('dispatches QUERY_EDITOR_SETDB action on onDbChange', () => {
+  const store = mockStore(createInitialState());
+  const { result } = renderHook(
+    () => useDatabaseSelector(defaultQueryEditor.id),
+    {
+      wrapper: createWrapper({
+        useRedux: true,
+        store,
+      }),
+    },
+  );
+
+  act(() => {
+    result.current.onDbChange({ id: 2 });
+  });
+
+  const actions = store.getActions();
+  expect(actions).toContainEqual(
+    expect.objectContaining({
+      type: 'QUERY_EDITOR_SETDB',
+      dbId: 2,
+    }),
+  );
+});
+
+test('dispatches queryEditorSetCatalog action on onCatalogChange', () => {
+  const store = mockStore(createInitialState());
+  const { result } = renderHook(
+    () => useDatabaseSelector(defaultQueryEditor.id),
+    {
+      wrapper: createWrapper({
+        useRedux: true,
+        store,
+      }),
+    },
+  );
+
+  act(() => {
+    result.current.onCatalogChange('new_catalog');
+  });
+
+  const actions = store.getActions();
+  expect(actions).toContainEqual(
+    expect.objectContaining({
+      type: 'QUERY_EDITOR_SET_CATALOG',
+    }),
+  );
+});
+
+test('dispatches queryEditorSetSchema action on onSchemaChange', () => {
+  const store = mockStore(createInitialState());
+  const { result } = renderHook(
+    () => useDatabaseSelector(defaultQueryEditor.id),
+    {
+      wrapper: createWrapper({
+        useRedux: true,
+        store,
+      }),
+    },
+  );
+
+  act(() => {
+    result.current.onSchemaChange('new_schema');
+  });
+
+  const actions = store.getActions();
+  expect(actions).toContainEqual(
+    expect.objectContaining({
+      type: 'QUERY_EDITOR_SET_SCHEMA',
+    }),
+  );
+});
+
+test('dispatches setDatabases action on getDbList', () => {
+  const store = mockStore(createInitialState());
+  const { result } = renderHook(
+    () => useDatabaseSelector(defaultQueryEditor.id),
+    {
+      wrapper: createWrapper({
+        useRedux: true,
+        store,
+      }),
+    },
+  );
+
+  const newDatabase = {
+    id: 3,
+    database_name: 'test_db',
+    backend: 'postgresql',
+  };
+
+  act(() => {
+    result.current.getDbList(newDatabase as any);
+  });
+
+  const actions = store.getActions();
+  expect(actions).toContainEqual(
+    expect.objectContaining({
+      type: 'SET_DATABASES',
+    }),
+  );
+});
+
+test('dispatches addDangerToast action on handleError', () => {
+  const store = mockStore(createInitialState());
+  const { result } = renderHook(
+    () => useDatabaseSelector(defaultQueryEditor.id),
+    {
+      wrapper: createWrapper({
+        useRedux: true,
+        store,
+      }),
+    },
+  );
+
+  act(() => {
+    result.current.handleError('Test error message');
+  });
+
+  const actions = store.getActions();
+  expect(actions).toContainEqual(
+    expect.objectContaining({
+      type: 'ADD_TOAST',
+      payload: expect.objectContaining({
+        toastType: 'DANGER_TOAST',
+        text: 'Test error message',
+      }),
+    }),
+  );
+});
+
+test('reads database from localStorage when URL has db param', () => {
+  const localStorageDb = {
+    id: 5,
+    database_name: 'local_storage_db',
+    backend: 'sqlite',
+  };
+
+  jest.spyOn(localStorageHelpers, 'getItem').mockReturnValue(localStorageDb);
+
+  const originalLocation = window.location;
+  Object.defineProperty(window, 'location', {
+    value: { search: '?db=true' },
+    writable: true,
+  });
+
+  const store = mockStore(createInitialState());
+  const { result, rerender } = renderHook(
+    () => useDatabaseSelector(defaultQueryEditor.id),
+    {
+      wrapper: createWrapper({
+        useRedux: true,
+        store,
+      }),
+    },
+  );
+
+  rerender();
+
+  expect(result.current.db).toEqual(localStorageDb);
+  expect(localStorageHelpers.setItem).toHaveBeenCalledWith(
+    localStorageHelpers.LocalStorageKeys.Database,
+    null,
+  );
+
+  Object.defineProperty(window, 'location', {
+    value: originalLocation,
+    writable: true,
+  });

Review Comment:
   **Suggestion:** Test state leak / teardown bug: the test mutates 
`window.location` and restores it only at the end of the test; if an assertion 
throws before the restore, subsequent tests will run with the modified 
location. Wrap the location mutation, render, assertions and restore in a 
try/finally and await rerender with `act` to ensure proper cleanup and to avoid 
flakiness. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
   test('reads database from localStorage when URL has db param', async () => {
     const localStorageDb = {
       id: 5,
       database_name: 'local_storage_db',
       backend: 'sqlite',
     };
   
     jest.spyOn(localStorageHelpers, 'getItem').mockReturnValue(localStorageDb);
   
     const originalLocation = window.location;
     Object.defineProperty(window, 'location', {
       value: { search: '?db=true' },
       writable: true,
     });
   
     try {
       const store = mockStore(createInitialState());
       const { result, rerender } = renderHook(
         () => useDatabaseSelector(defaultQueryEditor.id),
         {
           wrapper: createWrapper({
             useRedux: true,
             store,
           }),
         },
       );
   
       // wait for effect-driven state updates
       await act(async () => {
         rerender();
       });
   
       expect(result.current.db).toEqual(localStorageDb);
       expect(localStorageHelpers.setItem).toHaveBeenCalledWith(
         localStorageHelpers.LocalStorageKeys.Database,
         null,
       );
     } finally {
       // always restore global location
       Object.defineProperty(window, 'location', {
         value: originalLocation,
         writable: true,
       });
     }
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Mutating window.location without guaranteeing restoration risks leaking 
state across tests if an assertion fails. Wrapping the mutation and restoration 
in a try/finally and awaiting effect-driven updates (using act) is a correct 
and practical fix for test reliability.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.test.ts
   **Line:** 238:275
   **Comment:**
        *Possible Bug: Test state leak / teardown bug: the test mutates 
`window.location` and restores it only at the end of the test; if an assertion 
throws before the restore, subsequent tests will run with the modified 
location. Wrap the location mutation, render, assertions and restore in a 
try/finally and await rerender with `act` to ensure proper cleanup and to avoid 
flakiness.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/components/ViewExtension/index.tsx:
##########
@@ -16,18 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import ExtensionsManager from 'src/extensions/ExtensionsManager';
+import { useExtensionsContext } from 'src/extensions/ExtensionsContext';
 
-/**
- * Jest configuration for @storybook/test-runner
- *
- * This extends the default test-runner config with custom timeouts
- * to handle slow story rendering in CI environments.
- */
-const { getJestConfig } = require('@storybook/test-runner');
-const testRunnerConfig = getJestConfig();
+export interface ViewExtensionProps {
+  viewId: string;
+}
 
-module.exports = {
-  ...testRunnerConfig,
-  // Increase timeout from default 15s to 60s for CI environments
-  testTimeout: 60000,
+const ViewExtension = ({ viewId }: ViewExtensionProps) => {
+  const contributions =
+    ExtensionsManager.getInstance().getViewContributions(viewId) || [];

Review Comment:
   **Suggestion:** The call to `getViewContributions(viewId) || []` assumes the 
method returns an array or falsy value; if it returns a non-array truthy value 
the code will break when iterating. Coerce the result to an array (using 
`Array.isArray`) to ensure `contributions` is always an array. [type error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
     const maybeContributions = 
ExtensionsManager.getInstance().getViewContributions(viewId);
     const contributions = Array.isArray(maybeContributions) ? 
maybeContributions : [];
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Coercing the result with Array.isArray prevents runtime errors if 
getViewContributions unexpectedly returns a non-array truthy value. The 
improved code is a small, defensive fix that prevents .map from failing.
   </details>
   <details>
   <summary><b>Prompt for AI Agent πŸ€– </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/components/ViewExtension/index.tsx
   **Line:** 27:28
   **Comment:**
        *Type Error: The call to `getViewContributions(viewId) || []` assumes 
the method returns an array or falsy value; if it returns a non-array truthy 
value the code will break when iterating. Coerce the result to an array (using 
`Array.isArray`) to ensure `contributions` is always an array.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to