From 8d5ff67d05e8abd5b5affa21445ac9e731acee8a Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Mon, 10 Aug 2026 14:42:36 +0800 Subject: [PATCH] fix(web): name external API card actions (#40304) --- .../external-api/external-api-panel/index.tsx | 3 +- .../__tests__/index.spec.tsx | 92 ++++++++++++------- .../external-knowledge-api-card/index.tsx | 18 +++- 3 files changed, 77 insertions(+), 36 deletions(-) diff --git a/web/app/components/datasets/external-api/external-api-panel/index.tsx b/web/app/components/datasets/external-api/external-api-panel/index.tsx index 6d7fc4dbce0..f121e60296a 100644 --- a/web/app/components/datasets/external-api/external-api-panel/index.tsx +++ b/web/app/components/datasets/external-api/external-api-panel/index.tsx @@ -97,11 +97,12 @@ const ExternalAPIPanel: React.FC = ({ {isLoading ? ( ) : ( - externalKnowledgeApiList.map((api) => ( + externalKnowledgeApiList.map((api, index) => ( )) )} diff --git a/web/app/components/datasets/external-api/external-knowledge-api-card/__tests__/index.spec.tsx b/web/app/components/datasets/external-api/external-knowledge-api-card/__tests__/index.spec.tsx index 5a3bc766f24..475c4d1a10f 100644 --- a/web/app/components/datasets/external-api/external-knowledge-api-card/__tests__/index.spec.tsx +++ b/web/app/components/datasets/external-api/external-knowledge-api-card/__tests__/index.spec.tsx @@ -70,8 +70,14 @@ describe('ExternalKnowledgeAPICard', () => { const defaultProps = { api: mockApi, canManageExternalKnowledgeApi: true, + position: 1, } + const editButtonName = + 'common.operation.edit Test External API https://api.example.com/knowledge 1' + const deleteButtonName = + 'common.operation.delete Test External API https://api.example.com/knowledge 1' + beforeEach(() => { vi.clearAllMocks() }) @@ -88,17 +94,41 @@ describe('ExternalKnowledgeAPICard', () => { }) it('should render edit and delete buttons', () => { - const { container } = render() - const buttons = container.querySelectorAll('button') - expect(buttons.length).toBe(2) + const duplicateApi = { + ...mockApi, + id: 'api-456', + } + + render( + <> + + + , + ) + + expect(screen.getByRole('button', { name: editButtonName })).toBeInTheDocument() + expect(screen.getByRole('button', { name: deleteButtonName })).toBeInTheDocument() + expect( + screen.getByRole('button', { + name: 'common.operation.edit Test External API https://api.example.com/knowledge 2', + }), + ).toBeInTheDocument() + expect( + screen.getByRole('button', { + name: 'common.operation.delete Test External API https://api.example.com/knowledge 2', + }), + ).toBeInTheDocument() }) it('should hide edit and delete buttons when external knowledge API management is unavailable', () => { - const { container } = render( - , - ) + render() - expect(container.querySelectorAll('button').length).toBe(0) + expect(screen.queryByRole('button', { name: editButtonName })).not.toBeInTheDocument() + expect(screen.queryByRole('button', { name: deleteButtonName })).not.toBeInTheDocument() }) it('should render API connection icon', () => { @@ -125,9 +155,8 @@ describe('ExternalKnowledgeAPICard', () => { } vi.mocked(fetchExternalAPI).mockResolvedValue(mockResponse) - const { container } = render() - const buttons = container.querySelectorAll('button') - const editButton = buttons[0] + render() + const editButton = screen.getByRole('button', { name: editButtonName }) fireEvent.click(editButton!) @@ -153,9 +182,8 @@ describe('ExternalKnowledgeAPICard', () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) vi.mocked(fetchExternalAPI).mockRejectedValue(new Error('Fetch failed')) - const { container } = render() - const buttons = container.querySelectorAll('button') - const editButton = buttons[0] + render() + const editButton = screen.getByRole('button', { name: editButtonName }) fireEvent.click(editButton!) @@ -185,8 +213,8 @@ describe('ExternalKnowledgeAPICard', () => { } vi.mocked(fetchExternalAPI).mockResolvedValue(mockResponse) - const { container } = render() - const editButton = container.querySelectorAll('button')[0] + render() + const editButton = screen.getByRole('button', { name: editButtonName }) fireEvent.click(editButton!) @@ -234,8 +262,8 @@ describe('ExternalKnowledgeAPICard', () => { } vi.mocked(fetchExternalAPI).mockResolvedValue(mockResponse) - const { container } = render() - const editButton = container.querySelectorAll('button')[0] + render() + const editButton = screen.getByRole('button', { name: editButtonName }) fireEvent.click(editButton!) @@ -254,9 +282,8 @@ describe('ExternalKnowledgeAPICard', () => { it('should check usage and show confirm dialog when delete button is clicked', async () => { vi.mocked(checkUsageExternalAPI).mockResolvedValue({ is_using: false, count: 0 }) - const { container } = render() - const buttons = container.querySelectorAll('button') - const deleteButton = buttons[1] + render() + const deleteButton = screen.getByRole('button', { name: deleteButtonName }) fireEvent.click(deleteButton!) @@ -273,8 +300,8 @@ describe('ExternalKnowledgeAPICard', () => { it('should show usage count in confirm dialog when API is in use', async () => { vi.mocked(checkUsageExternalAPI).mockResolvedValue({ is_using: true, count: 3 }) - const { container } = render() - const deleteButton = container.querySelectorAll('button')[1] + render() + const deleteButton = screen.getByRole('button', { name: deleteButtonName }) fireEvent.click(deleteButton!) @@ -287,8 +314,8 @@ describe('ExternalKnowledgeAPICard', () => { vi.mocked(checkUsageExternalAPI).mockResolvedValue({ is_using: false, count: 0 }) vi.mocked(deleteExternalAPI).mockResolvedValue({ result: 'success' }) - const { container } = render() - const deleteButton = container.querySelectorAll('button')[1] + render() + const deleteButton = screen.getByRole('button', { name: deleteButtonName }) fireEvent.click(deleteButton!) @@ -310,8 +337,8 @@ describe('ExternalKnowledgeAPICard', () => { it('should close confirm dialog when cancel is clicked', async () => { vi.mocked(checkUsageExternalAPI).mockResolvedValue({ is_using: false, count: 0 }) - const { container } = render() - const deleteButton = container.querySelectorAll('button')[1] + render() + const deleteButton = screen.getByRole('button', { name: deleteButtonName }) fireEvent.click(deleteButton!) @@ -332,8 +359,8 @@ describe('ExternalKnowledgeAPICard', () => { vi.mocked(checkUsageExternalAPI).mockResolvedValue({ is_using: false, count: 0 }) vi.mocked(deleteExternalAPI).mockRejectedValue(new Error('Delete failed')) - const { container } = render() - const deleteButton = container.querySelectorAll('button')[1] + render() + const deleteButton = screen.getByRole('button', { name: deleteButtonName }) fireEvent.click(deleteButton!) @@ -358,8 +385,8 @@ describe('ExternalKnowledgeAPICard', () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) vi.mocked(checkUsageExternalAPI).mockRejectedValue(new Error('Check failed')) - const { container } = render() - const deleteButton = container.querySelectorAll('button')[1] + render() + const deleteButton = screen.getByRole('button', { name: deleteButtonName }) fireEvent.click(deleteButton!) @@ -384,6 +411,7 @@ describe('ExternalKnowledgeAPICard', () => { , ) expect(screen.getByText('Test External API'))!.toBeInTheDocument() @@ -394,8 +422,8 @@ describe('ExternalKnowledgeAPICard', () => { vi.mocked(checkUsageExternalAPI).mockResolvedValue({ is_using: false, count: 0 }) vi.mocked(deleteExternalAPI).mockResolvedValue({ result: 'error' }) - const { container } = render() - const deleteButton = container.querySelectorAll('button')[1] + render() + const deleteButton = screen.getByRole('button', { name: deleteButtonName }) fireEvent.click(deleteButton!) diff --git a/web/app/components/datasets/external-api/external-knowledge-api-card/index.tsx b/web/app/components/datasets/external-api/external-knowledge-api-card/index.tsx index 54b100e9361..24184b24702 100644 --- a/web/app/components/datasets/external-api/external-knowledge-api-card/index.tsx +++ b/web/app/components/datasets/external-api/external-knowledge-api-card/index.tsx @@ -28,11 +28,13 @@ import { type ExternalKnowledgeAPICardProps = { api: ExternalKnowledgeApiResponse canManageExternalKnowledgeApi: boolean + position: number } const ExternalKnowledgeAPICard: React.FC = ({ api, canManageExternalKnowledgeApi, + position, }) => { const { setShowExternalKnowledgeAPIModal } = useModalContext() const [showConfirm, setShowConfirm] = useState(false) @@ -132,16 +134,26 @@ const ExternalKnowledgeAPICard: React.FC = ({ {canManageExternalKnowledgeApi && (
- - + $['operation.edit'], { ns: 'common' })} ${api.name} ${endpoint} ${position}`} + onClick={handleEditClick} + > + $['operation.delete'], { ns: 'common' })} ${api.name} ${endpoint} ${position}`} className="hover:bg-state-destructive-hover" onClick={handleDeleteClick} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} > - +
)}