diff --git a/package-lock.json b/package-lock.json index e9cea2898..20b41022d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,8 +23,10 @@ }, "devDependencies": { "@eslint/js": "^9.39.1", + "@types/node": "^25.7.0", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", + "@types/react-test-renderer": "^19.1.0", "@typescript-eslint/eslint-plugin": "^8.48.1", "@typescript-eslint/parser": "^8.48.1", "@vitejs/plugin-react": "^6.0.1", @@ -33,6 +35,7 @@ "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", "prettier": "^3.7.4", + "react-test-renderer": "^19.2.1", "sass": "^1.94.2", "typescript": "^5.9.3", "typescript-eslint": "^8.48.1", @@ -72,6 +75,7 @@ "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", @@ -437,6 +441,7 @@ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.8.tgz", "integrity": "sha512-XcE9fcnkHCbWkjeKyi0lllwXmBLtyYb5dt89dJyx23I9+LSh5vZDIuk7OLG4VM1lgrXZQcY6cxyZyk5WVPRv/A==", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/state": "^6.5.0", "crelt": "^1.0.6", @@ -1451,12 +1456,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.7.0.tgz", + "integrity": "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.21.0" + } + }, "node_modules/@types/react": { "version": "19.2.7", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1471,6 +1488,16 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/react-test-renderer": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-19.1.0.tgz", + "integrity": "sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react/node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -1544,6 +1571,7 @@ "integrity": "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.48.1", "@typescript-eslint/types": "8.48.1", @@ -1979,6 +2007,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2429,6 +2458,7 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -2950,6 +2980,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.28.4" }, @@ -3621,6 +3652,7 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -3732,6 +3764,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz", "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -3741,6 +3774,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz", "integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -3775,6 +3809,13 @@ } } }, + "node_modules/react-is": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.6.tgz", + "integrity": "sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==", + "dev": true, + "license": "MIT" + }, "node_modules/react-router": { "version": "7.12.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.12.0.tgz", @@ -3813,6 +3854,20 @@ "react-dom": ">=18" } }, + "node_modules/react-test-renderer": { + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-19.2.1.tgz", + "integrity": "sha512-xsyf515ij+d8Rs/tsDZSJYXn+GdYO/IOw9BVFtjJbrrFR+dL1yZQQN1ChxY6otYAsCeAHhU0XsKyJUzP6omyvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "react-is": "^19.2.1", + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.1" + } + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -4078,6 +4133,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4110,6 +4166,13 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/undici-types": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.21.0.tgz", + "integrity": "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==", + "dev": true, + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.2.2", "dev": true, @@ -4154,6 +4217,7 @@ "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -4398,6 +4462,7 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "license": "ISC", + "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -4425,6 +4490,7 @@ "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 80c7ce20e..4ba870dd7 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,10 @@ }, "devDependencies": { "@eslint/js": "^9.39.1", + "@types/node": "^25.7.0", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", + "@types/react-test-renderer": "^19.1.0", "@typescript-eslint/eslint-plugin": "^8.48.1", "@typescript-eslint/parser": "^8.48.1", "@vitejs/plugin-react": "^6.0.1", @@ -38,6 +40,7 @@ "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", "prettier": "^3.7.4", + "react-test-renderer": "^19.2.1", "sass": "^1.94.2", "typescript": "^5.9.3", "typescript-eslint": "^8.48.1", diff --git a/src/features/authFiles/components/AuthJsonPasteModal.module.scss b/src/features/authFiles/components/AuthJsonPasteModal.module.scss new file mode 100644 index 000000000..173443950 --- /dev/null +++ b/src/features/authFiles/components/AuthJsonPasteModal.module.scss @@ -0,0 +1,60 @@ +@use '../../../styles/variables' as *; + +.authJsonPasteModal { + display: flex; + flex-direction: column; + gap: $spacing-md; +} + +.prefixProxyError { + padding: $spacing-sm $spacing-md; + border-radius: $radius-md; + border: 1px solid var(--danger-color); + background-color: rgba($error-color, 0.1); + color: var(--danger-color); + font-size: 12px; +} + +.formGroup { + display: flex; + flex-direction: column; + gap: $spacing-xs; + margin-top: $spacing-md; + + label { + font-size: 14px; + font-weight: 500; + color: var(--text-primary); + } +} + +.authJsonPasteTextarea { + width: 100%; + min-height: 300px; + box-sizing: border-box; + padding: $spacing-sm $spacing-md; + border: 1px solid var(--border-color); + border-radius: $radius-md; + background-color: var(--bg-secondary); + color: var(--text-primary); + font-family: monospace; + font-size: 12px; + line-height: 1.6; + resize: vertical; + + &:focus { + outline: none; + border-color: var(--primary-color); + } + + &::placeholder { + color: var(--text-tertiary); + } +} + +.authJsonPasteHint { + margin: 0; + color: var(--text-secondary); + font-size: 12px; + line-height: 1.5; +} diff --git a/src/features/authFiles/components/AuthJsonPasteModal.test.tsx b/src/features/authFiles/components/AuthJsonPasteModal.test.tsx new file mode 100644 index 000000000..b8b1e5569 --- /dev/null +++ b/src/features/authFiles/components/AuthJsonPasteModal.test.tsx @@ -0,0 +1,232 @@ +import { act, type ReactNode } from 'react'; +import { create, type ReactTestRenderer } from 'react-test-renderer'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { Button } from '@/components/ui/Button'; +import { Input } from '@/components/ui/Input'; +import { Select } from '@/components/ui/Select'; +import { AuthJsonPasteModal } from './AuthJsonPasteModal'; + +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (key: string) => key, + }), +})); + +vi.mock('@/components/ui/Modal', () => ({ + Modal: (props: { children: ReactNode; footer?: ReactNode }) => ( +
+ {t(type === 'session' ? 'auth_files.paste_session_hint' : 'auth_files.paste_cpa_hint')} +
+