diff --git a/package-lock.json b/package-lock.json index 3c66197..f454c4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,6 +45,7 @@ }, "devDependencies": { "@svgr/webpack": "^8.1.0", + "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/jest": "^30.0.0", @@ -6901,6 +6902,78 @@ "react": "^18 || ^19" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/dom/node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/dom/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, "node_modules/@testing-library/jest-dom": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", @@ -8996,6 +9069,13 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -12155,6 +12235,16 @@ "node": ">= 0.8" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -17301,6 +17391,16 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/lz4js": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/lz4js/-/lz4js-0.2.0.tgz", diff --git a/package.json b/package.json index 10d2766..f889c9b 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "lint": "next lint", "test": "jest", "test:watch": "jest --watch", + "test:coverage": "jest --coverage", "refine": "refine" }, "dependencies": { @@ -52,6 +53,7 @@ }, "devDependencies": { "@svgr/webpack": "^8.1.0", + "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/jest": "^30.0.0", diff --git a/src/components/olmap/BurstPipeAnalysis/ValveIsolation.test.tsx b/src/components/olmap/BurstPipeAnalysis/ValveIsolation.test.tsx new file mode 100644 index 0000000..0389aef --- /dev/null +++ b/src/components/olmap/BurstPipeAnalysis/ValveIsolation.test.tsx @@ -0,0 +1,98 @@ +import React from "react"; +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import ValveIsolation from "./ValveIsolation"; +import axios from "axios"; +import "@testing-library/jest-dom"; + +// Mock dependencies +jest.mock("axios"); +const mockedAxios = axios as jest.Mocked; + +jest.mock("@refinedev/core", () => ({ + useNotification: () => ({ + open: jest.fn(), + }), +})); + +// Mock config +jest.mock("@config/config", () => ({ + config: { + BACKEND_URL: "http://test-api.com", + }, + NETWORK_NAME: "test-network", + // If config is a default export or named export, adjust accordingly. + // Based on usage: import { config, NETWORK_NAME } from '@config/config'; + // The mock above covers named exports. +})); + +describe("ValveIsolation Component", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test("renders input and analyze button", () => { + render(); + expect(screen.getByLabelText(/爆管管段ID/i)).toBeInTheDocument(); + expect(screen.getByText(/开始分析/i)).toBeInTheDocument(); + }); + + test("calls API with correct parameters when analyze is clicked", async () => { + mockedAxios.get.mockResolvedValueOnce({ + data: { + accident_element: "pipe1", + accident_type: "Burst", + affected_nodes: ["node1", "node2"], + must_close_valves: ["valve1"], + optional_valves: [], + isolatable: true, + }, + }); + + render(); + + const input = screen.getByLabelText(/爆管管段ID/i); + fireEvent.change(input, { target: { value: "pipe1" } }); + + const button = screen.getByText(/开始分析/i); + fireEvent.click(button); + + await waitFor(() => { + expect(mockedAxios.get).toHaveBeenCalledWith( + "http://test-api.com/api/v1/valve_isolation_analysis", + { + params: { + network: "test-network", + accident_element: "pipe1", + }, + }, + ); + }); + }); + + test("displays results after successful analysis", async () => { + mockedAxios.get.mockResolvedValueOnce({ + data: { + accident_element: "pipe1", + accident_type: "Burst", + affected_nodes: ["nodeA"], + must_close_valves: ["valveA", "valveB"], + optional_valves: [], + isolatable: true, + }, + }); + + render(); + + fireEvent.change(screen.getByLabelText(/爆管管段ID/i), { + target: { value: "pipe1" }, + }); + fireEvent.click(screen.getByText(/开始分析/i)); + + await waitFor(() => { + expect(screen.getByText("可隔离")).toBeInTheDocument(); + expect(screen.getByText("valveA")).toBeInTheDocument(); + expect(screen.getByText("valveB")).toBeInTheDocument(); + expect(screen.getByText("nodeA")).toBeInTheDocument(); + }); + }); +}); diff --git a/src/utils/parseColor.test.js b/src/utils/parseColor.test.js new file mode 100644 index 0000000..3d95a87 --- /dev/null +++ b/src/utils/parseColor.test.js @@ -0,0 +1,21 @@ +const { parseColor } = require('./parseColor'); + +describe('parseColor', () => { + it('should parse hex color', () => { + expect(parseColor('#FF0000')).toEqual({ r: 255, g: 0, b: 0 }); + expect(parseColor('00FF00')).toEqual({ r: 0, g: 255, b: 0 }); + }); + + it('should parse rgb color', () => { + expect(parseColor('rgb(0, 0, 255)')).toEqual({ r: 0, g: 0, b: 255, a: 1 }); + }); + + it('should parse rgba color', () => { + expect(parseColor('rgba(0, 0, 255, 0.5)')).toEqual({ r: 0, g: 0, b: 255, a: 0.5 }); + }); + + it('should default alpha to 1 if not provided in rgba-like pattern', () => { + // The regex supports optional alpha + expect(parseColor('rgba(0, 0, 255)')).toEqual({ r: 0, g: 0, b: 255, a: 1 }); + }); +});