mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-09 14:57:59 -05:00
fix(frontend): clean up console warnings in test suite (#12004)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: amanape <83104063+amanape@users.noreply.github.com>
This commit is contained in:
@@ -23,6 +23,11 @@ describe("ConversationPanel", () => {
|
||||
Component: () => <ConversationPanel onClose={onCloseMock} />,
|
||||
path: "/",
|
||||
},
|
||||
{
|
||||
// Add route to prevent "No routes matched location" warning
|
||||
Component: () => null,
|
||||
path: "/conversations/:conversationId",
|
||||
},
|
||||
]);
|
||||
|
||||
const renderConversationPanel = () => renderWithProviders(<RouterStub />);
|
||||
|
||||
@@ -48,9 +48,12 @@ describe("MaintenanceBanner", () => {
|
||||
expect(button).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// maintenance-banner
|
||||
|
||||
it("handles invalid date gracefully", () => {
|
||||
// Suppress expected console.warn for invalid date parsing
|
||||
const consoleWarnSpy = vi
|
||||
.spyOn(console, "warn")
|
||||
.mockImplementation(() => {});
|
||||
|
||||
const invalidTime = "invalid-date";
|
||||
|
||||
render(
|
||||
@@ -62,6 +65,9 @@ describe("MaintenanceBanner", () => {
|
||||
// Check if the banner is rendered
|
||||
const banner = screen.queryByTestId("maintenance-banner");
|
||||
expect(banner).not.toBeInTheDocument();
|
||||
|
||||
// Restore console.warn
|
||||
consoleWarnSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("click on dismiss button removes banner", () => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
beforeEach,
|
||||
afterAll,
|
||||
afterEach,
|
||||
vi,
|
||||
} from "vitest";
|
||||
import { screen, waitFor, render, cleanup } from "@testing-library/react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
@@ -141,6 +142,11 @@ describe("Conversation WebSocket Handler", () => {
|
||||
});
|
||||
|
||||
it("should handle malformed/invalid event data gracefully", async () => {
|
||||
// Suppress expected console.warn for invalid JSON parsing
|
||||
const consoleWarnSpy = vi
|
||||
.spyOn(console, "warn")
|
||||
.mockImplementation(() => {});
|
||||
|
||||
// Set up MSW to send various invalid events when connection is established
|
||||
mswServer.use(
|
||||
wsLink.addEventListener("connection", ({ client, server }) => {
|
||||
@@ -203,6 +209,9 @@ describe("Conversation WebSocket Handler", () => {
|
||||
"valid-event-123",
|
||||
);
|
||||
expect(screen.getByTestId("ui-events-count")).toHaveTextContent("1");
|
||||
|
||||
// Restore console.warn
|
||||
consoleWarnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -34,7 +34,11 @@ describe("useWebSocket", () => {
|
||||
}),
|
||||
);
|
||||
|
||||
beforeAll(() => mswServer.listen());
|
||||
beforeAll(() =>
|
||||
mswServer.listen({
|
||||
onUnhandledRequest: "warn",
|
||||
}),
|
||||
);
|
||||
afterEach(() => mswServer.resetHandlers());
|
||||
afterAll(() => mswServer.close());
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useShouldShowUserFeatures } from "../src/hooks/use-should-show-user-fea
|
||||
vi.mock("../src/hooks/use-should-show-user-features");
|
||||
vi.mock("#/api/suggestions-service/suggestions-service.api", () => ({
|
||||
SuggestionsService: {
|
||||
getSuggestedTasks: vi.fn(),
|
||||
getSuggestedTasks: vi.fn().mockResolvedValue([]),
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
44
frontend/package-lock.json
generated
44
frontend/package-lock.json
generated
@@ -192,7 +192,6 @@
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@@ -732,7 +731,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -779,7 +777,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -2331,7 +2328,6 @@
|
||||
"version": "2.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/system/-/system-2.4.24.tgz",
|
||||
"integrity": "sha512-9GKQgUc91otQfwmq6TLE72QKxtB341aK5NpBHS3gRoWYEuNN714Zl3OXwIZNvdXPJpsTaUo1ID1ibJU9tfgwdg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@heroui/react-utils": "2.1.14",
|
||||
"@heroui/system-rsc": "2.3.21",
|
||||
@@ -2411,7 +2407,6 @@
|
||||
"version": "2.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/theme/-/theme-2.4.24.tgz",
|
||||
"integrity": "sha512-lL+anmY4GGWwKyTbJ2PEBZE4talIZ3hu4yGpku9TktCVG2nC2YTwiWQFJ+Jcbf8Cf9vuLzI1sla5bz2jUqiBRA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@heroui/shared-utils": "2.1.12",
|
||||
"color": "^4.2.3",
|
||||
@@ -5127,7 +5122,6 @@
|
||||
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.21.3",
|
||||
"@svgr/babel-preset": "8.1.0",
|
||||
@@ -5588,7 +5582,6 @@
|
||||
"integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
"@babel/runtime": "^7.12.5",
|
||||
@@ -5766,7 +5759,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
|
||||
"integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
|
||||
"devOptional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
@@ -5782,7 +5774,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
||||
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
@@ -5793,7 +5784,6 @@
|
||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
@@ -5834,7 +5824,6 @@
|
||||
"integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "7.18.0",
|
||||
@@ -5892,7 +5881,6 @@
|
||||
"integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "7.18.0",
|
||||
"@typescript-eslint/types": "7.18.0",
|
||||
@@ -6406,8 +6394,7 @@
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
||||
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
@@ -6435,7 +6422,6 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -6972,7 +6958,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
@@ -7661,8 +7646,7 @@
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/damerau-levenshtein": {
|
||||
"version": "1.0.8",
|
||||
@@ -8380,7 +8364,6 @@
|
||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
@@ -8504,7 +8487,6 @@
|
||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
},
|
||||
@@ -8585,7 +8567,6 @@
|
||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.9",
|
||||
@@ -8677,7 +8658,6 @@
|
||||
"integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"aria-query": "^5.3.2",
|
||||
"array-includes": "^3.1.8",
|
||||
@@ -8773,7 +8753,6 @@
|
||||
"integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"array-includes": "^3.1.8",
|
||||
"array.prototype.findlast": "^1.2.5",
|
||||
@@ -8807,7 +8786,6 @@
|
||||
"integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -9076,7 +9054,6 @@
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
|
||||
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
@@ -9407,7 +9384,6 @@
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.26.tgz",
|
||||
"integrity": "sha512-cPcIhgR42xBn1Uj+PzOyheMtZ73H927+uWPDVhUMqxy8UHt6Okavb6xIz9J/phFUHUj0OncR6UvMfJTXoc/LKA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.23.23",
|
||||
"motion-utils": "^12.23.6",
|
||||
@@ -10075,7 +10051,6 @@
|
||||
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
|
||||
}
|
||||
],
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4"
|
||||
},
|
||||
@@ -10853,7 +10828,6 @@
|
||||
"integrity": "sha512-GtldT42B8+jefDUC4yUKAvsaOrH7PDHmZxZXNgF2xMmymjUbRYJvpAybZAKEmXDGTM0mCsz8duOa4vTm5AY2Kg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@acemir/cssom": "^0.9.28",
|
||||
"@asamuzakjp/dom-selector": "^6.7.6",
|
||||
@@ -12555,7 +12529,6 @@
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
|
||||
"integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"dompurify": "3.2.7",
|
||||
"marked": "14.0.0"
|
||||
@@ -12650,7 +12623,6 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@inquirer/confirm": "^5.0.0",
|
||||
"@mswjs/interceptors": "^0.40.0",
|
||||
@@ -13375,7 +13347,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -13437,7 +13408,6 @@
|
||||
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
@@ -13634,7 +13604,6 @@
|
||||
"version": "19.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
||||
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -13683,7 +13652,6 @@
|
||||
"version": "19.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
|
||||
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -13796,7 +13764,6 @@
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.11.0.tgz",
|
||||
"integrity": "sha512-uI4JkMmjbWCZc01WVP2cH7ZfSzH91JAZUDd7/nIprDgWxBV1TkkmLToFh7EbMTcMak8URFRa2YoBL/W8GWnCTQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cookie": "^1.0.1",
|
||||
"set-cookie-parser": "^2.6.0"
|
||||
@@ -14158,7 +14125,6 @@
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
|
||||
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.8"
|
||||
},
|
||||
@@ -15154,7 +15120,6 @@
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
|
||||
"integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/dcastil"
|
||||
@@ -15281,7 +15246,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -15583,7 +15547,6 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -15889,7 +15852,6 @@
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz",
|
||||
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -16059,7 +16021,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -16072,7 +16033,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.16.tgz",
|
||||
"integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vitest/expect": "4.0.16",
|
||||
"@vitest/mocker": "4.0.16",
|
||||
|
||||
@@ -47,18 +47,20 @@ export function ConversationCardContextMenu({
|
||||
const ref = useClickOutsideElement<HTMLUListElement>(onClose);
|
||||
|
||||
const generateSection = useCallback(
|
||||
(items: React.ReactNode[], isLast?: boolean) => {
|
||||
(items: React.ReactNode[], sectionKey: string, isLast?: boolean) => {
|
||||
const filteredItems = items.filter((i) => i != null);
|
||||
|
||||
if (filteredItems.length > 0) {
|
||||
return !isLast
|
||||
? [
|
||||
...filteredItems,
|
||||
<Divider key="conversation-card-context-menu-divider" />,
|
||||
]
|
||||
: filteredItems;
|
||||
return !isLast ? (
|
||||
<React.Fragment key={sectionKey}>
|
||||
{filteredItems}
|
||||
<Divider />
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<React.Fragment key={sectionKey}>{filteredItems}</React.Fragment>
|
||||
);
|
||||
}
|
||||
return [];
|
||||
return null;
|
||||
},
|
||||
[],
|
||||
);
|
||||
@@ -71,88 +73,104 @@ export function ConversationCardContextMenu({
|
||||
alignment="right"
|
||||
className="mt-0"
|
||||
>
|
||||
{generateSection([
|
||||
onEdit && (
|
||||
<ContextMenuListItem
|
||||
testId="edit-button"
|
||||
onClick={onEdit}
|
||||
className={contextMenuListItemClassName}
|
||||
>
|
||||
<ConversationNameContextMenuIconText
|
||||
icon={<EditIcon width={16} height={16} />}
|
||||
text={t(I18nKey.BUTTON$RENAME)}
|
||||
/>
|
||||
</ContextMenuListItem>
|
||||
),
|
||||
])}
|
||||
{generateSection([
|
||||
onShowAgentTools && (
|
||||
<ContextMenuListItem
|
||||
testId="show-agent-tools-button"
|
||||
onClick={onShowAgentTools}
|
||||
className={contextMenuListItemClassName}
|
||||
>
|
||||
<ConversationNameContextMenuIconText
|
||||
icon={<ToolsIcon width={16} height={16} />}
|
||||
text={t(I18nKey.BUTTON$SHOW_AGENT_TOOLS_AND_METADATA)}
|
||||
/>
|
||||
</ContextMenuListItem>
|
||||
),
|
||||
onShowSkills && (
|
||||
<ContextMenuListItem
|
||||
testId="show-skills-button"
|
||||
onClick={onShowSkills}
|
||||
className={contextMenuListItemClassName}
|
||||
>
|
||||
<ConversationNameContextMenuIconText
|
||||
icon={<RobotIcon width={16} height={16} />}
|
||||
text={t(I18nKey.CONVERSATION$SHOW_SKILLS)}
|
||||
/>
|
||||
</ContextMenuListItem>
|
||||
),
|
||||
])}
|
||||
{generateSection([
|
||||
onStop && (
|
||||
<ContextMenuListItem
|
||||
testId="stop-button"
|
||||
onClick={onStop}
|
||||
className={contextMenuListItemClassName}
|
||||
>
|
||||
<ConversationNameContextMenuIconText
|
||||
icon={<CloseIcon width={16} height={16} />}
|
||||
text={t(I18nKey.COMMON$CLOSE_CONVERSATION_STOP_RUNTIME)}
|
||||
/>
|
||||
</ContextMenuListItem>
|
||||
),
|
||||
onDownloadViaVSCode && (
|
||||
<ContextMenuListItem
|
||||
testId="download-vscode-button"
|
||||
onClick={onDownloadViaVSCode}
|
||||
className={contextMenuListItemClassName}
|
||||
>
|
||||
<ConversationNameContextMenuIconText
|
||||
icon={<DownloadIcon width={16} height={16} />}
|
||||
text={t(I18nKey.BUTTON$DOWNLOAD_VIA_VSCODE)}
|
||||
/>
|
||||
</ContextMenuListItem>
|
||||
),
|
||||
onDownloadConversation && (
|
||||
<ContextMenuListItem
|
||||
testId="download-trajectory-button"
|
||||
onClick={onDownloadConversation}
|
||||
className={contextMenuListItemClassName}
|
||||
>
|
||||
<ConversationNameContextMenuIconText
|
||||
icon={<DownloadIcon width={16} height={16} />}
|
||||
text={t(I18nKey.BUTTON$EXPORT_CONVERSATION)}
|
||||
/>
|
||||
</ContextMenuListItem>
|
||||
),
|
||||
])}
|
||||
{generateSection(
|
||||
[
|
||||
onEdit && (
|
||||
<ContextMenuListItem
|
||||
key="edit-button"
|
||||
testId="edit-button"
|
||||
onClick={onEdit}
|
||||
className={contextMenuListItemClassName}
|
||||
>
|
||||
<ConversationNameContextMenuIconText
|
||||
icon={<EditIcon width={16} height={16} />}
|
||||
text={t(I18nKey.BUTTON$RENAME)}
|
||||
/>
|
||||
</ContextMenuListItem>
|
||||
),
|
||||
],
|
||||
"edit-section",
|
||||
)}
|
||||
{generateSection(
|
||||
[
|
||||
onShowAgentTools && (
|
||||
<ContextMenuListItem
|
||||
key="show-agent-tools-button"
|
||||
testId="show-agent-tools-button"
|
||||
onClick={onShowAgentTools}
|
||||
className={contextMenuListItemClassName}
|
||||
>
|
||||
<ConversationNameContextMenuIconText
|
||||
icon={<ToolsIcon width={16} height={16} />}
|
||||
text={t(I18nKey.BUTTON$SHOW_AGENT_TOOLS_AND_METADATA)}
|
||||
/>
|
||||
</ContextMenuListItem>
|
||||
),
|
||||
onShowSkills && (
|
||||
<ContextMenuListItem
|
||||
key="show-skills-button"
|
||||
testId="show-skills-button"
|
||||
onClick={onShowSkills}
|
||||
className={contextMenuListItemClassName}
|
||||
>
|
||||
<ConversationNameContextMenuIconText
|
||||
icon={<RobotIcon width={16} height={16} />}
|
||||
text={t(I18nKey.CONVERSATION$SHOW_SKILLS)}
|
||||
/>
|
||||
</ContextMenuListItem>
|
||||
),
|
||||
],
|
||||
"tools-section",
|
||||
)}
|
||||
{generateSection(
|
||||
[
|
||||
onStop && (
|
||||
<ContextMenuListItem
|
||||
key="stop-button"
|
||||
testId="stop-button"
|
||||
onClick={onStop}
|
||||
className={contextMenuListItemClassName}
|
||||
>
|
||||
<ConversationNameContextMenuIconText
|
||||
icon={<CloseIcon width={16} height={16} />}
|
||||
text={t(I18nKey.COMMON$CLOSE_CONVERSATION_STOP_RUNTIME)}
|
||||
/>
|
||||
</ContextMenuListItem>
|
||||
),
|
||||
onDownloadViaVSCode && (
|
||||
<ContextMenuListItem
|
||||
key="download-vscode-button"
|
||||
testId="download-vscode-button"
|
||||
onClick={onDownloadViaVSCode}
|
||||
className={contextMenuListItemClassName}
|
||||
>
|
||||
<ConversationNameContextMenuIconText
|
||||
icon={<DownloadIcon width={16} height={16} />}
|
||||
text={t(I18nKey.BUTTON$DOWNLOAD_VIA_VSCODE)}
|
||||
/>
|
||||
</ContextMenuListItem>
|
||||
),
|
||||
onDownloadConversation && (
|
||||
<ContextMenuListItem
|
||||
key="download-trajectory-button"
|
||||
testId="download-trajectory-button"
|
||||
onClick={onDownloadConversation}
|
||||
className={contextMenuListItemClassName}
|
||||
>
|
||||
<ConversationNameContextMenuIconText
|
||||
icon={<DownloadIcon width={16} height={16} />}
|
||||
text={t(I18nKey.BUTTON$EXPORT_CONVERSATION)}
|
||||
/>
|
||||
</ContextMenuListItem>
|
||||
),
|
||||
],
|
||||
"control-section",
|
||||
)}
|
||||
{generateSection(
|
||||
[
|
||||
onDisplayCost && (
|
||||
<ContextMenuListItem
|
||||
key="display-cost-button"
|
||||
testId="display-cost-button"
|
||||
onClick={onDisplayCost}
|
||||
className={contextMenuListItemClassName}
|
||||
@@ -165,6 +183,7 @@ export function ConversationCardContextMenu({
|
||||
),
|
||||
onDelete && (
|
||||
<ContextMenuListItem
|
||||
key="delete-button"
|
||||
testId="delete-button"
|
||||
onClick={onDelete}
|
||||
className={contextMenuListItemClassName}
|
||||
@@ -172,10 +191,11 @@ export function ConversationCardContextMenu({
|
||||
<ConversationNameContextMenuIconText
|
||||
icon={<DeleteIcon width={16} height={16} />}
|
||||
text={t(I18nKey.COMMON$DELETE_CONVERSATION)}
|
||||
/>{" "}
|
||||
/>
|
||||
</ContextMenuListItem>
|
||||
),
|
||||
],
|
||||
"info-section",
|
||||
true,
|
||||
)}
|
||||
</ContextMenu>
|
||||
|
||||
@@ -79,6 +79,7 @@ export function BranchDropdownMenu({
|
||||
menuRef={menuRef}
|
||||
renderItem={renderItem}
|
||||
renderEmptyState={renderEmptyState}
|
||||
itemKey={(branch) => branch.name}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -211,6 +211,7 @@ export function GitProviderDropdown({
|
||||
getItemProps={getItemProps}
|
||||
renderItem={renderItem}
|
||||
renderEmptyState={renderEmptyState}
|
||||
itemKey={(provider) => provider}
|
||||
/>
|
||||
|
||||
<ErrorMessage isError={!!errorMessage} message={errorMessage} />
|
||||
|
||||
@@ -369,6 +369,7 @@ export function GitRepoDropdown({
|
||||
stickyFooterItem={stickyFooterItem}
|
||||
testId="git-repo-dropdown-menu"
|
||||
numberOfRecentItems={recentRepositories.length}
|
||||
itemKey={(repo) => repo.id}
|
||||
/>
|
||||
|
||||
<ErrorMessage isError={isError} />
|
||||
|
||||
@@ -39,6 +39,7 @@ export function ConversationStatusIndicator({
|
||||
ariaLabel={statusLabel}
|
||||
placement="right"
|
||||
showArrow
|
||||
asSpan
|
||||
className="p-0 border-0 bg-transparent hover:opacity-100"
|
||||
tooltipClassName="bg-[#1a1a1a] text-white text-xs shadow-lg"
|
||||
>
|
||||
|
||||
@@ -20,63 +20,59 @@ export function RecentConversation({ conversation }: RecentConversationProps) {
|
||||
conversation.selected_repository && conversation.selected_branch;
|
||||
|
||||
return (
|
||||
<Link to={`/conversations/${conversation.conversation_id}`}>
|
||||
<button
|
||||
type="button"
|
||||
className="flex flex-col gap-1 p-[14px] cursor-pointer w-full rounded-lg hover:bg-[#5C5D62] transition-all duration-300 text-left"
|
||||
>
|
||||
<div className="flex items-center gap-2 pl-1">
|
||||
<ConversationStatusIndicator
|
||||
conversationStatus={conversation.status}
|
||||
/>
|
||||
<span className="text-xs text-white leading-6 font-normal">
|
||||
{conversation.title}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-xs text-[#A3A3A3] leading-4 font-normal">
|
||||
<div className="flex items-center gap-3">
|
||||
{hasRepository ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<GitProviderIcon
|
||||
gitProvider={conversation.git_provider as Provider}
|
||||
/>
|
||||
<span
|
||||
className="max-w-[124px] truncate"
|
||||
title={conversation.selected_repository || ""}
|
||||
>
|
||||
{conversation.selected_repository}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-1">
|
||||
<RepoForkedIcon width={12} height={12} color="#A3A3A3" />
|
||||
<span className="max-w-[124px] truncate">
|
||||
{t(I18nKey.COMMON$NO_REPOSITORY)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{hasRepository ? (
|
||||
<div className="flex items-center gap-1">
|
||||
<CodeBranchIcon width={12} height={12} color="#A3A3A3" />
|
||||
<span
|
||||
className="max-w-[124px] truncate"
|
||||
title={conversation.selected_branch || ""}
|
||||
>
|
||||
{conversation.selected_branch}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{(conversation.created_at || conversation.last_updated_at) && (
|
||||
<span>
|
||||
{formatTimeDelta(
|
||||
conversation.created_at || conversation.last_updated_at,
|
||||
)}{" "}
|
||||
{t(I18nKey.CONVERSATION$AGO)}
|
||||
</span>
|
||||
<Link
|
||||
to={`/conversations/${conversation.conversation_id}`}
|
||||
className="flex flex-col gap-1 p-[14px] cursor-pointer w-full rounded-lg hover:bg-[#5C5D62] transition-all duration-300 text-left"
|
||||
>
|
||||
<div className="flex items-center gap-2 pl-1">
|
||||
<ConversationStatusIndicator conversationStatus={conversation.status} />
|
||||
<span className="text-xs text-white leading-6 font-normal">
|
||||
{conversation.title}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-xs text-[#A3A3A3] leading-4 font-normal">
|
||||
<div className="flex items-center gap-3">
|
||||
{hasRepository ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<GitProviderIcon
|
||||
gitProvider={conversation.git_provider as Provider}
|
||||
/>
|
||||
<span
|
||||
className="max-w-[124px] truncate"
|
||||
title={conversation.selected_repository || ""}
|
||||
>
|
||||
{conversation.selected_repository}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-1">
|
||||
<RepoForkedIcon width={12} height={12} color="#A3A3A3" />
|
||||
<span className="max-w-[124px] truncate">
|
||||
{t(I18nKey.COMMON$NO_REPOSITORY)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{hasRepository ? (
|
||||
<div className="flex items-center gap-1">
|
||||
<CodeBranchIcon width={12} height={12} color="#A3A3A3" />
|
||||
<span
|
||||
className="max-w-[124px] truncate"
|
||||
title={conversation.selected_branch || ""}
|
||||
>
|
||||
{conversation.selected_branch}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</button>
|
||||
{(conversation.created_at || conversation.last_updated_at) && (
|
||||
<span>
|
||||
{formatTimeDelta(
|
||||
conversation.created_at || conversation.last_updated_at,
|
||||
)}{" "}
|
||||
{t(I18nKey.CONVERSATION$AGO)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface GenericDropdownMenuProps<T> {
|
||||
stickyFooterItem?: React.ReactNode;
|
||||
testId?: string;
|
||||
numberOfRecentItems?: number;
|
||||
itemKey: (item: T) => string | number;
|
||||
}
|
||||
|
||||
export function GenericDropdownMenu<T>({
|
||||
@@ -51,12 +52,28 @@ export function GenericDropdownMenu<T>({
|
||||
stickyFooterItem,
|
||||
testId,
|
||||
numberOfRecentItems = 0,
|
||||
itemKey,
|
||||
}: GenericDropdownMenuProps<T>) {
|
||||
if (!isOpen) return null;
|
||||
|
||||
const hasItems = filteredItems.length > 0;
|
||||
const showEmptyState = !hasItems && !stickyTopItem && !stickyFooterItem;
|
||||
|
||||
// Always render the menu container (even when closed) so getMenuProps is always called
|
||||
// This prevents the downshift warning about forgetting to call getMenuProps
|
||||
if (!isOpen) {
|
||||
return (
|
||||
<div className="relative">
|
||||
<ul
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...getMenuProps({
|
||||
ref: menuRef,
|
||||
className: "hidden",
|
||||
"data-testid": testId,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div
|
||||
@@ -85,21 +102,24 @@ export function GenericDropdownMenu<T>({
|
||||
) : (
|
||||
<>
|
||||
{stickyTopItem}
|
||||
{filteredItems.map((item, index) => (
|
||||
<>
|
||||
{renderItem(
|
||||
item,
|
||||
index,
|
||||
highlightedIndex,
|
||||
selectedItem,
|
||||
getItemProps,
|
||||
)}
|
||||
{numberOfRecentItems > 0 &&
|
||||
index === numberOfRecentItems - 1 && (
|
||||
<div className="border-b border-[#727987] bg-[#454545] pb-1 mb-1 h-[1px]" />
|
||||
{filteredItems.map((item, index) => {
|
||||
const key = itemKey(item);
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
{renderItem(
|
||||
item,
|
||||
index,
|
||||
highlightedIndex,
|
||||
selectedItem,
|
||||
getItemProps,
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
{numberOfRecentItems > 0 &&
|
||||
index === numberOfRecentItems - 1 && (
|
||||
<div className="border-b border-[#727987] bg-[#454545] pb-1 mb-1 h-[1px]" />
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</ul>
|
||||
|
||||
@@ -17,9 +17,10 @@ export function MicroagentManagementAccordionTitle({
|
||||
<TooltipButton
|
||||
tooltip={repository.full_name}
|
||||
ariaLabel={repository.full_name}
|
||||
className="text-white text-base font-normal bg-transparent p-0 min-w-0 h-auto cursor-pointer truncate max-w-[194px] translate-y-[-1px]"
|
||||
testId="repository-name-tooltip"
|
||||
placement="bottom"
|
||||
asSpan
|
||||
className="text-white text-base font-normal bg-transparent p-0 min-w-0 h-auto cursor-pointer truncate max-w-[194px] translate-y-[-1px]"
|
||||
>
|
||||
<span>{repository.full_name}</span>
|
||||
</TooltipButton>
|
||||
|
||||
@@ -18,22 +18,32 @@ export function MicroagentManagementAddMicroagentButton({
|
||||
setSelectedRepository,
|
||||
} = useMicroagentManagementStore();
|
||||
|
||||
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
const handleClick = (e: React.MouseEvent<HTMLSpanElement>) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
setAddMicroagentModalVisible(!addMicroagentModalVisible);
|
||||
setSelectedRepository(repository);
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLSpanElement>) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
setAddMicroagentModalVisible(!addMicroagentModalVisible);
|
||||
setSelectedRepository(repository);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
<span
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={handleClick}
|
||||
className="translate-y-[-1px]"
|
||||
onKeyDown={handleKeyDown}
|
||||
className="translate-y-[-1px] text-sm font-normal leading-5 text-[#8480FF] cursor-pointer hover:text-[#6C63FF] transition-colors duration-200"
|
||||
data-testid="add-microagent-button"
|
||||
>
|
||||
<span className="text-sm font-normal leading-5 text-[#8480FF] cursor-pointer hover:text-[#6C63FF] transition-colors duration-200">
|
||||
{t(I18nKey.COMMON$ADD_MICROAGENT)}
|
||||
</span>
|
||||
</button>
|
||||
{t(I18nKey.COMMON$ADD_MICROAGENT)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface TooltipButtonProps {
|
||||
disabled?: boolean;
|
||||
placement?: TooltipProps["placement"];
|
||||
showArrow?: boolean;
|
||||
asSpan?: boolean;
|
||||
}
|
||||
|
||||
export function TooltipButton({
|
||||
@@ -31,6 +32,7 @@ export function TooltipButton({
|
||||
disabled = false,
|
||||
placement = "right",
|
||||
showArrow = false,
|
||||
asSpan = false,
|
||||
}: TooltipButtonProps) {
|
||||
const handleClick = (e: React.MouseEvent) => {
|
||||
if (onClick && !disabled) {
|
||||
@@ -39,22 +41,67 @@ export function TooltipButton({
|
||||
}
|
||||
};
|
||||
|
||||
const buttonContent = (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={ariaLabel}
|
||||
data-testid={testId}
|
||||
onClick={handleClick}
|
||||
className={cn(
|
||||
"hover:opacity-80",
|
||||
disabled && "opacity-50 cursor-not-allowed",
|
||||
className,
|
||||
)}
|
||||
disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
const isClickable = !!onClick && !disabled;
|
||||
let buttonContent: React.ReactNode;
|
||||
if (asSpan) {
|
||||
if (isClickable) {
|
||||
buttonContent = (
|
||||
<span
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={ariaLabel}
|
||||
data-testid={testId}
|
||||
onClick={handleClick}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
onClick();
|
||||
e.preventDefault();
|
||||
}
|
||||
}}
|
||||
className={cn(
|
||||
"hover:opacity-80",
|
||||
disabled && "opacity-50 cursor-not-allowed",
|
||||
className,
|
||||
)}
|
||||
aria-disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
buttonContent = (
|
||||
<span
|
||||
aria-label={ariaLabel}
|
||||
data-testid={testId}
|
||||
className={cn(
|
||||
"hover:opacity-80",
|
||||
disabled && "opacity-50 cursor-not-allowed",
|
||||
className,
|
||||
)}
|
||||
aria-disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
buttonContent = (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={ariaLabel}
|
||||
data-testid={testId}
|
||||
onClick={handleClick}
|
||||
className={cn(
|
||||
"hover:opacity-80",
|
||||
disabled && "opacity-50 cursor-not-allowed",
|
||||
className,
|
||||
)}
|
||||
disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
let content;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user