codeant-ai-for-open-source[bot] commented on code in PR #33002:
URL: https://github.com/apache/superset/pull/33002#discussion_r2844751175
##########
superset-frontend/src/components/ListView/ListView.test.jsx:
##########
@@ -26,6 +26,115 @@ import fetchMock from 'fetch-mock';
// Only import components that are directly referenced in tests
import { ListView } from './ListView';
+jest.mock('@superset-ui/core/components', () => ({
+ Alert: ({ children, message, ...props }) => (
+ <div {...props}>{message || children}</div>
+ ),
+ DropdownButton: ({ children, onClick, 'data-test': dataTest, ...props }) => (
+ <button type="button" onClick={onClick} data-test={dataTest} {...props}>
+ {children}
+ </button>
+ ),
+ Icons: {
+ AppstoreOutlined: props => (
+ <span {...props} role="img" aria-label="appstore" />
+ ),
+ UnorderedListOutlined: props => (
+ <span {...props} role="img" aria-label="unordered-list" />
+ ),
+ DeleteOutlined: props => <span {...props} role="img" aria-label="delete"
/>,
+ },
+ EmptyState: ({ children, ...props }) => <div {...props}>{children}</div>,
+ Checkbox: props => <input type="checkbox" {...props} />,
+ Loading: () => <div>Loading...</div>,
+ Flex: ({ children, ...props }) => <div {...props}>{children}</div>,
+ Menu: {
+ Item: ({ children, ...props }) => <div {...props}>{children}</div>,
+ },
Review Comment:
**Suggestion:** The Jest mock for `Menu` returns a plain object instead of a
React component, so when `ListView` renders `<Menu>` in tests it will crash
with an "element type is invalid" error. [type error]
<details>
<summary><b>Severity Level:</b> Critical 🚨</summary>
```mdx
- ❌ ListView test suite crashes before any assertions run.
- ⚠️ CI cannot validate bulk select/certify ListView behavior.
```
</details>
```suggestion
Menu: Object.assign(
({ children, onClick, ...props }) => (
<div onClick={onClick} {...props}>
{children}
</div>
),
{
Item: ({ children, ...props }) => <div {...props}>{children}</div>,
},
),
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Run the Jest suite that includes `ListView.test.jsx`
(`superset-frontend/src/components/ListView/ListView.test.jsx`), which
mounts `<ListView
/>` in `beforeEach` via `factory()` (see `describe('ListView', () => { ...
}` block around
lines 140–210).
2. During render, `ListView` (in
`superset-frontend/src/components/ListView/ListView.tsx`)
imports `Menu` from `@superset-ui/core/components` and uses it as a
component, e.g.
`<Menu>` with nested `<Menu.Item>` for bulk action menus.
3. The Jest mock at lines 27–73 in `ListView.test.jsx` defines `Menu` as a
plain object `{
Item: (...) => <div>... }` instead of a function/component, because it omits
a callable
`Menu` component and only exposes `Item`.
4. When React reconciles `<Menu>` inside `ListView`, it receives an object
instead of a
valid element type and throws the runtime error "Element type is invalid:
expected a
string (for built-in components) or a class/function but got: object",
causing every test
in this file to error before assertions execute.
```
</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/ListView/ListView.test.jsx
**Line:** 51:53
**Comment:**
*Type Error: The Jest mock for `Menu` returns a plain object instead of
a React component, so when `ListView` renders `<Menu>` in tests it will crash
with an "element type is invalid" error.
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>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F33002&comment_hash=8be8cb01d97c3c0e1907ed05c38b7f33bfd2d56b0c46757255d2b05222dc79d7&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F33002&comment_hash=8be8cb01d97c3c0e1907ed05c38b7f33bfd2d56b0c46757255d2b05222dc79d7&reaction=dislike'>👎</a>
##########
superset-frontend/src/features/bulkUpdate/BulkCertifyModal.tsx:
##########
@@ -0,0 +1,159 @@
+/**
+ * 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. 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 { useState, FC } from 'react';
+
+import { t, SupersetClient } from '@superset-ui/core';
+import Chart from 'src/types/Chart';
+import { Dashboard } from 'src/pages/DashboardList';
+import {
+ Input,
+ Modal,
+ Button,
+ FormLabel,
+ Col,
+ Row,
+} from '@superset-ui/core/components';
+
+interface BulkCertifyModalProps {
+ onHide: () => void;
+ refreshData: () => void;
+ addSuccessToast: (msg: string) => void;
+ addDangerToast: (msg: string) => void;
+ show: boolean;
+ resourceName: 'chart' | 'dashboard';
+ resourceLabel: string;
+ selected: Chart[] | Dashboard[];
+}
+
+const BulkCertifyModal: FC<BulkCertifyModalProps> = ({
+ show,
+ selected = [],
+ resourceName,
+ resourceLabel,
+ onHide,
+ refreshData,
+ addSuccessToast,
+ addDangerToast,
+}) => {
+ const [certifiedBy, setCertifiedBy] = useState<string>('');
+ const [certificationDetails, setCertificationDetails] = useState<string>('');
+
+ const resourceLabelPlural = t(
+ '%s',
+ selected.length > 1 ? `${resourceLabel}s` : resourceLabel,
+ );
+
+ const onSave = async () => {
+ if (!certifiedBy) {
+ addDangerToast(t('Please enter who certified these items'));
+ return;
+ }
+
+ try {
+ await Promise.all(
+ selected.map(item => {
+ const url = `/api/v1/${resourceName}/${item.id}`;
+ const payload = {
+ certified_by: certifiedBy,
+ certification_details: certificationDetails,
+ };
+
+ return SupersetClient.put({
+ url,
+ headers: { 'Content-Type': 'application/json' },
+ jsonPayload: payload,
+ });
+ }),
+ );
+
+ addSuccessToast(t('Successfully certified %s', resourceLabelPlural));
+ refreshData();
+ onHide();
+ setCertifiedBy('');
+ setCertificationDetails('');
+ } catch (error) {
+ addDangerToast(t('Failed to certify %s', resourceLabelPlural));
+ }
+ };
+
+ return (
+ <Modal
+ title={<h4>{t('Bulk certify %s', resourceLabelPlural)}</h4>}
+ show={show}
+ onHide={() => {
+ setCertifiedBy('');
+ setCertificationDetails('');
+ onHide();
+ }}
+ footer={
+ <div>
+ <Button
+ data-test="modal-cancel-certify-button"
+ buttonStyle="secondary"
+ onClick={onHide}
Review Comment:
**Suggestion:** The Cancel button calls the parent hide handler directly and
does not clear the modal's internal certification fields, while the modal's own
onHide handler does clear them, leading to inconsistent state where cancelling
via the button may reopen the modal with stale values if the component remains
mounted. Unify both code paths to use the same hide handler that clears local
state and then invokes the parent onHide. [logic error]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ⚠️ Bulk certify modal reopens with stale certification fields.
- ⚠️ Inconsistent behavior between Cancel and modal close actions.
- ⚠️ Potential confusion when certifying multiple resource batches.
```
</details>
```suggestion
const handleHide = () => {
setCertifiedBy('');
setCertificationDetails('');
onHide();
};
return (
<Modal
title={<h4>{t('Bulk certify %s', resourceLabelPlural)}</h4>}
show={show}
onHide={handleHide}
footer={
<div>
<Button
data-test="modal-cancel-certify-button"
buttonStyle="secondary"
onClick={handleHide}
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Open the bulk certification UI (charts or dashboards list) and trigger
the bulk certify
action, which mounts `BulkCertifyModal` from
`superset-frontend/src/features/bulkUpdate/BulkCertifyModal.tsx:31` with
`show={true}`.
2. In the modal, type a non-empty value into the "Certified by" input and
optional text
into "Certification details" (state stored in `certifiedBy` and
`certificationDetails` at
lines 51–52).
3. Close the modal by clicking the "Cancel" button defined at lines 103–107,
which calls
the parent `onHide` directly and does *not* execute the local reset logic
present in the
`Modal`'s `onHide` handler at lines 96–100.
4. Reopen the same modal instance by triggering bulk certify again while the
parent reuses
the mounted component and only toggles the `show` prop from `false` back to
`true`;
observe that the inputs still contain the previous values because local
state was never
cleared when Cancel was clicked, whereas closing via the modal's close
behavior
(ESC/header X/overlay, which invokes `Modal`'s `onHide`) would have reset
them.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset-frontend/src/features/bulkUpdate/BulkCertifyModal.tsx
**Line:** 92:106
**Comment:**
*Logic Error: The Cancel button calls the parent hide handler directly
and does not clear the modal's internal certification fields, while the modal's
own onHide handler does clear them, leading to inconsistent state where
cancelling via the button may reopen the modal with stale values if the
component remains mounted. Unify both code paths to use the same hide handler
that clears local state and then invokes the parent onHide.
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>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F33002&comment_hash=555badb6564e9264feb0e2f43cd3bb8afda2575085ab72a4abab014413e4536e&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F33002&comment_hash=555badb6564e9264feb0e2f43cd3bb8afda2575085ab72a4abab014413e4536e&reaction=dislike'>👎</a>
##########
superset-frontend/cypress-base/cypress/e2e/dashboard_list/list.test.ts:
##########
@@ -53,12 +52,22 @@ function confirmDelete(bulk = false) {
cy.wrap($input).type('DELETE');
});
cy.getBySel('modal-confirm-button').should('be.visible').click();
+ cy.wait('@delete');
+}
+
+function confirmBulkDelete() {
+ interceptBulkDelete();
- if (bulk) {
- cy.wait('@bulkDelete');
- } else {
- cy.wait('@delete');
- }
+ // Wait for modal dialog to be present and visible
+ cy.get('[role="dialog")[aria-modal="true"]').should('be.visible');
Review Comment:
**Suggestion:** The CSS selector used to locate the delete confirmation
dialog is malformed (`[role="dialog")[aria-modal="true"]`), so Cypress will
fail to find the modal and the bulk delete test will hang or error instead of
interacting with the dialog. [logic error]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ❌ Bulk delete dashboard e2e test fails consistently.
- ⚠️ Bulk delete feature lacks reliable automated coverage.
```
</details>
```suggestion
cy.get('[role="dialog"][aria-modal="true"]').should('be.visible');
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Run the Cypress spec
`superset-frontend/cypress-base/cypress/e2e/dashboard_list/list.test.ts`
(e.g., `yarn
cypress run --spec cypress/e2e/dashboard_list/list.test.ts`).
2. During execution of the "common actions" suite, the test `it('should bulk
delete
correctly', ...)` (around line 137 in the diff) is executed and calls
`toggleBulkSelect()`, selects dashboards, then triggers the bulk delete UI.
3. After clicking `cy.getBySel('bulk-select-action').trigger('mouseenter');
cy.contains('Delete').click();`, the test calls `confirmBulkDelete()`
defined around line
58 in the same file.
4. Inside `confirmBulkDelete()`, Cypress executes
`cy.get('[role="dialog")[aria-modal="true"]').should('be.visible');` which
uses an invalid
CSS selector, causing Cypress to fail to parse or match the selector and the
test to error
or time out instead of interacting with the confirmation modal.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:**
superset-frontend/cypress-base/cypress/e2e/dashboard_list/list.test.ts
**Line:** 62:62
**Comment:**
*Logic Error: The CSS selector used to locate the delete confirmation
dialog is malformed (`[role="dialog")[aria-modal="true"]`), so Cypress will
fail to find the modal and the bulk delete test will hang or error instead of
interacting with the dialog.
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>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F33002&comment_hash=51d223f5b0c151ed902e536c23b8831cb6342273d34fec818e2bb58c2bbb87a0&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F33002&comment_hash=51d223f5b0c151ed902e536c23b8831cb6342273d34fec818e2bb58c2bbb87a0&reaction=dislike'>👎</a>
--
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]