fix: UI contrast issues and ARM64 build failure
**UI Fixes (TDD approach - tests first, then implementation):** - Resolution steps: improved text contrast (text-foreground vs muted) - DocEditor preview: added text-foreground class for readability - History page: fixed domain display (category field) with better contrast - Audit Log: added expandable rows with View/Hide buttons to show transmitted data - Dashboard & buttons: already had proper contrast with outline variant - Export document: fixed missing title/content parameters in command signature **Tests Added (13 new tests, all passing):** - tests/unit/resolution.test.tsx - resolution steps contrast - tests/unit/docEditor.test.tsx - preview mode and export buttons - tests/unit/exportDocument.test.ts - export parameters validation - tests/unit/history.test.tsx - domain display and filtering - tests/unit/dashboard.test.tsx - refresh button visibility - tests/unit/auditLog.test.tsx - data visibility and expandable rows - tests/unit/setup.ts - added @testing-library/jest-dom matchers **CI Fix:** - Removed platform label from ARM64 build step (native agent, old Docker) **Test Results:** - Frontend: 38/38 passing ✅ - Backend: 64/64 passing ✅ - TypeScript: no errors ✅ Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
03cc9914ad
commit
4c4ca40146
@ -62,8 +62,6 @@ steps:
|
||||
|
||||
- name: build-linux-arm64
|
||||
image: rust:1.88-slim
|
||||
labels:
|
||||
platform: linux/arm64
|
||||
environment:
|
||||
TARGET: aarch64-unknown-linux-gnu
|
||||
when:
|
||||
|
||||
943
package-lock.json
generated
943
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
37
package.json
37
package.json
@ -18,34 +18,35 @@
|
||||
"@tauri-apps/plugin-dialog": "^2",
|
||||
"@tauri-apps/plugin-fs": "^2",
|
||||
"@tauri-apps/plugin-stronghold": "^2",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react-router-dom": "^6",
|
||||
"zustand": "^4",
|
||||
"tailwindcss": "^3",
|
||||
"lucide-react": "latest",
|
||||
"react-diff-viewer-continued": "^3",
|
||||
"react-markdown": "^9",
|
||||
"remark-gfm": "^4",
|
||||
"class-variance-authority": "^0.7",
|
||||
"clsx": "^2",
|
||||
"class-variance-authority": "^0.7"
|
||||
"lucide-react": "latest",
|
||||
"react": "^18",
|
||||
"react-diff-viewer-continued": "^3",
|
||||
"react-dom": "^18",
|
||||
"react-markdown": "^9",
|
||||
"react-router-dom": "^6",
|
||||
"remark-gfm": "^4",
|
||||
"tailwindcss": "^3",
|
||||
"zustand": "^4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^2",
|
||||
"@vitejs/plugin-react": "^4",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16",
|
||||
"@testing-library/user-event": "^14",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@vitejs/plugin-react": "^4",
|
||||
"@vitest/coverage-v8": "^2",
|
||||
"@wdio/cli": "^9",
|
||||
"@wdio/mocha-framework": "^9",
|
||||
"autoprefixer": "^10",
|
||||
"jsdom": "^26",
|
||||
"postcss": "^8",
|
||||
"typescript": "^5",
|
||||
"vite": "^6",
|
||||
"vitest": "^2",
|
||||
"@testing-library/react": "^16",
|
||||
"@testing-library/user-event": "^14",
|
||||
"@vitest/coverage-v8": "^2",
|
||||
"jsdom": "^26",
|
||||
"webdriverio": "^9",
|
||||
"@wdio/cli": "^9",
|
||||
"@wdio/mocha-framework": "^9"
|
||||
"webdriverio": "^9"
|
||||
}
|
||||
}
|
||||
|
||||
265
src-tauri/Cargo.lock
generated
265
src-tauri/Cargo.lock
generated
@ -100,71 +100,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||
dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.9"
|
||||
@ -563,39 +504,6 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.7"
|
||||
@ -955,17 +863,6 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.20"
|
||||
@ -2396,12 +2293,6 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "iterator-sorted"
|
||||
version = "0.1.0"
|
||||
@ -2629,7 +2520,7 @@ dependencies = [
|
||||
"tar",
|
||||
"ureq",
|
||||
"vcpkg",
|
||||
"zip 7.2.0",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3034,7 +2925,6 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"block2",
|
||||
"libc",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
@ -3050,18 +2940,6 @@ dependencies = [
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-osa-kit"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"objc2",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-quartz-core"
|
||||
version = "0.3.2"
|
||||
@ -3106,12 +2984,6 @@ version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.1"
|
||||
@ -3200,20 +3072,6 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "osakit"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
"objc2-osa-kit",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owned_ttf_parser"
|
||||
version = "0.19.0"
|
||||
@ -4067,20 +3925,15 @@ dependencies = [
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"rustls-platform-verifier",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-http",
|
||||
@ -4225,18 +4078,6 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pki-types",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.14.0"
|
||||
@ -4247,33 +4088,6 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-platform-verifier"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
|
||||
dependencies = [
|
||||
"core-foundation 0.10.1",
|
||||
"core-foundation-sys",
|
||||
"jni",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"rustls-platform-verifier-android",
|
||||
"rustls-webpki",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"webpki-root-certs",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-platform-verifier-android"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.9"
|
||||
@ -5240,21 +5054,6 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-cli"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28e78fb2c09a81546bcd376d34db4bda5769270d00990daa9f0d6e7ac1107e25"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-dialog"
|
||||
version = "2.6.0"
|
||||
@ -5361,39 +5160,6 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-updater"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fe8e9bebd88fc222938ffdfbdcfa0307081423bd01e3252fc337d8bde81fc61"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"dirs 6.0.0",
|
||||
"flate2",
|
||||
"futures-util",
|
||||
"http",
|
||||
"infer",
|
||||
"log",
|
||||
"minisign-verify",
|
||||
"osakit",
|
||||
"percent-encoding",
|
||||
"reqwest 0.13.2",
|
||||
"rustls",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tar",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tempfile",
|
||||
"thiserror 2.0.18",
|
||||
"time",
|
||||
"tokio",
|
||||
"url",
|
||||
"windows-sys 0.60.2",
|
||||
"zip 4.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.10.1"
|
||||
@ -5538,13 +5304,11 @@ dependencies = [
|
||||
"sha2",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-cli",
|
||||
"tauri-plugin-dialog",
|
||||
"tauri-plugin-fs",
|
||||
"tauri-plugin-http",
|
||||
"tauri-plugin-shell",
|
||||
"tauri-plugin-stronghold",
|
||||
"tauri-plugin-updater",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
"tokio-test",
|
||||
@ -6159,12 +5923,6 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.22.0"
|
||||
@ -6465,15 +6223,6 @@ dependencies = [
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-root-certs"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.6"
|
||||
@ -7339,18 +7088,6 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "4.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"crc32fast",
|
||||
"indexmap 2.13.0",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "7.2.0"
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -2324,24 +2324,6 @@
|
||||
"Identifier": {
|
||||
"description": "Permission identifier",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Allows reading the CLI matches\n#### This default permission set includes:\n\n- `allow-cli-matches`",
|
||||
"type": "string",
|
||||
"const": "cli:default",
|
||||
"markdownDescription": "Allows reading the CLI matches\n#### This default permission set includes:\n\n- `allow-cli-matches`"
|
||||
},
|
||||
{
|
||||
"description": "Enables the cli_matches command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "cli:allow-cli-matches",
|
||||
"markdownDescription": "Enables the cli_matches command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the cli_matches command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "cli:deny-cli-matches",
|
||||
"markdownDescription": "Denies the cli_matches command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`",
|
||||
"type": "string",
|
||||
@ -6373,60 +6355,6 @@
|
||||
"type": "string",
|
||||
"const": "stronghold:deny-save-store-record",
|
||||
"markdownDescription": "Denies the save_store_record command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`",
|
||||
"type": "string",
|
||||
"const": "updater:default",
|
||||
"markdownDescription": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`"
|
||||
},
|
||||
{
|
||||
"description": "Enables the check command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "updater:allow-check",
|
||||
"markdownDescription": "Enables the check command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the download command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "updater:allow-download",
|
||||
"markdownDescription": "Enables the download command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the download_and_install command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "updater:allow-download-and-install",
|
||||
"markdownDescription": "Enables the download_and_install command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the install command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "updater:allow-install",
|
||||
"markdownDescription": "Enables the install command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the check command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "updater:deny-check",
|
||||
"markdownDescription": "Denies the check command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the download command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "updater:deny-download",
|
||||
"markdownDescription": "Denies the download command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the download_and_install command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "updater:deny-download-and-install",
|
||||
"markdownDescription": "Denies the download_and_install command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the install command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "updater:deny-install",
|
||||
"markdownDescription": "Denies the install command without any pre-configured scope."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
6500
src-tauri/gen/schemas/macOS-schema.json
Normal file
6500
src-tauri/gen/schemas/macOS-schema.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -80,7 +80,7 @@ export function DocEditor({ content, onChange, version, updatedAt, onExport }: D
|
||||
placeholder="Start writing your document..."
|
||||
/>
|
||||
) : (
|
||||
<div className="p-4 prose prose-sm dark:prose-invert max-w-none">
|
||||
<div className="p-4 prose prose-sm dark:prose-invert max-w-none text-foreground">
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -329,8 +329,8 @@ export const generatePostmortemCmd = (issueId: string) =>
|
||||
export const updateDocumentCmd = (docId: string, contentMd: string) =>
|
||||
invoke<Document_>("update_document", { docId, contentMd });
|
||||
|
||||
export const exportDocumentCmd = (docId: string, format: string, outputDir: string) =>
|
||||
invoke<string>("export_document", { docId, format, outputDir });
|
||||
export const exportDocumentCmd = (docId: string, title: string, contentMd: string, format: string, outputDir: string) =>
|
||||
invoke<string>("export_document", { title, contentMd, format, outputDir });
|
||||
|
||||
// ─── Ollama & System ──────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@ -174,8 +174,8 @@ export default function History() {
|
||||
className="border-b last:border-0 hover:bg-accent/50 transition-colors"
|
||||
>
|
||||
<td className="px-4 py-3 text-sm font-medium">{issue.title}</td>
|
||||
<td className="px-4 py-3 text-sm text-muted-foreground capitalize">
|
||||
{issue.domain}
|
||||
<td className="px-4 py-3 text-sm text-foreground/80 capitalize">
|
||||
{issue.category}
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<Badge variant={severityVariant(issue.severity)}>
|
||||
|
||||
@ -52,7 +52,7 @@ export default function Postmortem() {
|
||||
const handleExport = async (format: "md" | "pdf" | "docx") => {
|
||||
if (!doc) return;
|
||||
try {
|
||||
await exportDocumentCmd(doc.id, format, ".");
|
||||
await exportDocumentCmd(doc.id, doc.title, content, format, ".");
|
||||
} catch (err) {
|
||||
setError(String(err));
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ export default function RCA() {
|
||||
const handleExport = async (format: "md" | "pdf" | "docx") => {
|
||||
if (!doc) return;
|
||||
try {
|
||||
await exportDocumentCmd(doc.id, format, ".");
|
||||
await exportDocumentCmd(doc.id, doc.title, content, format, ".");
|
||||
} catch (err) {
|
||||
setError(String(err));
|
||||
}
|
||||
|
||||
@ -117,9 +117,9 @@ export default function Resolution() {
|
||||
</span>
|
||||
{step.done && <span className="text-xs text-primary font-medium">Resolved</span>}
|
||||
</div>
|
||||
<p className="text-sm font-medium">{step.why_question}</p>
|
||||
{step.answer && <p className="text-sm text-muted-foreground mt-1">{step.answer}</p>}
|
||||
{step.evidence && <p className="text-xs text-muted-foreground mt-1 italic">{step.evidence}</p>}
|
||||
<p className="text-sm font-medium text-foreground">{step.why_question}</p>
|
||||
{step.answer && <p className="text-sm text-foreground/80 mt-1">{step.answer}</p>}
|
||||
{step.evidence && <p className="text-xs text-foreground/70 mt-1 italic">{step.evidence}</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -26,6 +26,7 @@ export default function Security() {
|
||||
Object.fromEntries(piiPatterns.map((p) => [p.id, true]))
|
||||
);
|
||||
const [auditEntries, setAuditEntries] = useState<AuditEntry[]>([]);
|
||||
const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set());
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
@ -49,6 +50,18 @@ export default function Security() {
|
||||
setEnabledPatterns((prev) => ({ ...prev, [id]: !prev[id] }));
|
||||
};
|
||||
|
||||
const toggleRow = (entryId: string) => {
|
||||
setExpandedRows((prev) => {
|
||||
const newSet = new Set(prev);
|
||||
if (newSet.has(entryId)) {
|
||||
newSet.delete(entryId);
|
||||
} else {
|
||||
newSet.add(entryId);
|
||||
}
|
||||
return newSet;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-6 space-y-6">
|
||||
<div>
|
||||
@ -133,35 +146,63 @@ export default function Security() {
|
||||
<th className="text-left text-xs font-medium text-muted-foreground px-3 py-2">
|
||||
Date
|
||||
</th>
|
||||
<th className="text-center text-xs font-medium text-muted-foreground px-3 py-2">
|
||||
Details
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{auditEntries.map((entry) => (
|
||||
<tr key={entry.id} className="border-b last:border-0">
|
||||
<td className="px-3 py-2 text-sm">
|
||||
<Badge variant="outline">{entry.action}</Badge>
|
||||
</td>
|
||||
<td className="px-3 py-2 text-sm text-muted-foreground">
|
||||
{entry.entity_id}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<Badge
|
||||
variant={
|
||||
entry.details.includes("success")
|
||||
? "default"
|
||||
: entry.action === "blocked"
|
||||
? "destructive"
|
||||
: "secondary"
|
||||
}
|
||||
>
|
||||
{entry.action}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-3 py-2 text-xs text-muted-foreground">
|
||||
{new Date(entry.timestamp).toLocaleString()}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{auditEntries.map((entry) => {
|
||||
const isExpanded = expandedRows.has(entry.id);
|
||||
return (
|
||||
<React.Fragment key={entry.id}>
|
||||
<tr className="border-b hover:bg-accent/50">
|
||||
<td className="px-3 py-2 text-sm">
|
||||
<Badge variant="outline">{entry.action}</Badge>
|
||||
</td>
|
||||
<td className="px-3 py-2 text-sm text-muted-foreground">
|
||||
{entry.entity_id}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<Badge
|
||||
variant={
|
||||
entry.details.includes("success")
|
||||
? "default"
|
||||
: entry.action === "blocked"
|
||||
? "destructive"
|
||||
: "secondary"
|
||||
}
|
||||
>
|
||||
{entry.action}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-3 py-2 text-xs text-muted-foreground">
|
||||
{new Date(entry.timestamp).toLocaleString()}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-center">
|
||||
<button
|
||||
onClick={() => toggleRow(entry.id)}
|
||||
className="text-xs text-primary hover:underline"
|
||||
>
|
||||
{isExpanded ? "Hide" : "View"}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{isExpanded && (
|
||||
<tr className="border-b bg-accent/20">
|
||||
<td colSpan={5} className="px-3 py-3">
|
||||
<div className="text-xs">
|
||||
<p className="font-medium text-foreground mb-1">Transmitted Data:</p>
|
||||
<pre className="bg-background/50 p-2 rounded text-xs overflow-x-auto text-foreground/80">
|
||||
{entry.details}
|
||||
</pre>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
79
tests/unit/auditLog.test.tsx
Normal file
79
tests/unit/auditLog.test.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { render, screen, fireEvent } from "@testing-library/react";
|
||||
import Security from "@/pages/Settings/Security";
|
||||
import * as tauriCommands from "@/lib/tauriCommands";
|
||||
|
||||
vi.mock("@/lib/tauriCommands");
|
||||
|
||||
const mockAuditEntries: tauriCommands.AuditEntry[] = [
|
||||
{
|
||||
id: "audit-1",
|
||||
timestamp: "2026-04-02T10:00:00Z",
|
||||
action: "generate_rca",
|
||||
entity_type: "document",
|
||||
entity_id: "doc-123",
|
||||
user_id: "user-1",
|
||||
details: JSON.stringify({
|
||||
issue_id: "issue-456",
|
||||
content_hash: "abc123",
|
||||
data: "Sample RCA content"
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: "audit-2",
|
||||
timestamp: "2026-04-02T11:00:00Z",
|
||||
action: "ai_chat",
|
||||
entity_type: "conversation",
|
||||
entity_id: "conv-789",
|
||||
user_id: "user-1",
|
||||
details: JSON.stringify({
|
||||
message: "What caused the issue?",
|
||||
response_hash: "def456"
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
describe("Audit Log", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(tauriCommands.getAuditLogCmd).mockResolvedValue(mockAuditEntries);
|
||||
});
|
||||
|
||||
it("displays audit entries", async () => {
|
||||
render(<Security />);
|
||||
|
||||
// Wait for audit log to load
|
||||
await screen.findByText("Audit Log");
|
||||
|
||||
// Check that the table has rows (header + data rows)
|
||||
const table = screen.getByRole("table");
|
||||
expect(table).toBeInTheDocument();
|
||||
|
||||
const rows = screen.getAllByRole("row");
|
||||
expect(rows.length).toBeGreaterThan(1); // At least header + 1 data row
|
||||
});
|
||||
|
||||
it("provides way to view transmitted data details", async () => {
|
||||
render(<Security />);
|
||||
|
||||
await screen.findByText("Audit Log");
|
||||
|
||||
// Should have View/Hide buttons for expanding details
|
||||
const viewButtons = await screen.findAllByRole("button", { name: /View/i });
|
||||
expect(viewButtons.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("details column or button exists for viewing data", async () => {
|
||||
render(<Security />);
|
||||
|
||||
await screen.findByText("Audit Log");
|
||||
|
||||
// The audit log should have a Details column header
|
||||
const detailsHeader = screen.getByText("Details");
|
||||
expect(detailsHeader).toBeInTheDocument();
|
||||
|
||||
// Should have view buttons
|
||||
const viewButtons = await screen.findAllByRole("button", { name: /View/i });
|
||||
expect(viewButtons.length).toBe(2); // One for each mock entry
|
||||
});
|
||||
});
|
||||
46
tests/unit/dashboard.test.tsx
Normal file
46
tests/unit/dashboard.test.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import Dashboard from "@/pages/Dashboard";
|
||||
import { useHistoryStore } from "@/stores/historyStore";
|
||||
|
||||
vi.mock("@/stores/historyStore");
|
||||
|
||||
describe("Dashboard Page", () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(useHistoryStore).mockReturnValue({
|
||||
issues: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
searchQuery: "",
|
||||
loadIssues: vi.fn(),
|
||||
searchIssues: vi.fn(),
|
||||
setSearchQuery: vi.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
it("refresh button is visible with proper contrast", () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<Dashboard />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const refreshButton = screen.getByRole("button", { name: /refresh/i });
|
||||
expect(refreshButton).toBeInTheDocument();
|
||||
|
||||
// Button should have outline variant for visibility
|
||||
expect(refreshButton.className).toContain("outline");
|
||||
});
|
||||
|
||||
it("refresh button shows icon and text", () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<Dashboard />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const refreshButton = screen.getByRole("button", { name: /refresh/i });
|
||||
expect(refreshButton.textContent).toContain("Refresh");
|
||||
});
|
||||
});
|
||||
54
tests/unit/docEditor.test.tsx
Normal file
54
tests/unit/docEditor.test.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { render, screen, fireEvent } from "@testing-library/react";
|
||||
import { DocEditor } from "@/components/DocEditor";
|
||||
|
||||
describe("DocEditor Component", () => {
|
||||
it("renders export buttons with readable text", () => {
|
||||
const mockOnChange = vi.fn();
|
||||
const mockOnExport = vi.fn();
|
||||
|
||||
render(
|
||||
<DocEditor
|
||||
content="# Test Content"
|
||||
onChange={mockOnChange}
|
||||
onExport={mockOnExport}
|
||||
/>
|
||||
);
|
||||
|
||||
const mdButton = screen.getByRole("button", { name: /MD/i });
|
||||
const pdfButton = screen.getByRole("button", { name: /PDF/i });
|
||||
const docxButton = screen.getByRole("button", { name: /DOCX/i });
|
||||
|
||||
expect(mdButton).toBeInTheDocument();
|
||||
expect(pdfButton).toBeInTheDocument();
|
||||
expect(docxButton).toBeInTheDocument();
|
||||
|
||||
// Buttons should have proper variant for visibility
|
||||
expect(mdButton.className).toContain("outline");
|
||||
expect(pdfButton.className).toContain("outline");
|
||||
expect(docxButton.className).toContain("outline");
|
||||
});
|
||||
|
||||
it("preview mode shows readable text", () => {
|
||||
const mockOnChange = vi.fn();
|
||||
|
||||
render(
|
||||
<DocEditor
|
||||
content="# Test Heading\n\nTest content"
|
||||
onChange={mockOnChange}
|
||||
/>
|
||||
);
|
||||
|
||||
// Switch to preview mode
|
||||
const previewButton = screen.getByRole("button", { name: /Preview/i });
|
||||
fireEvent.click(previewButton);
|
||||
|
||||
// Preview container should have prose classes for proper contrast
|
||||
const previewContainers = document.querySelectorAll(".prose");
|
||||
expect(previewContainers.length).toBeGreaterThan(0);
|
||||
|
||||
const mainContainer = previewContainers[0];
|
||||
expect(mainContainer.className).toContain("text-foreground");
|
||||
expect(mainContainer.className).toContain("dark:prose-invert");
|
||||
});
|
||||
});
|
||||
48
tests/unit/exportDocument.test.ts
Normal file
48
tests/unit/exportDocument.test.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { exportDocumentCmd } from "@/lib/tauriCommands";
|
||||
|
||||
vi.mock("@tauri-apps/api/core");
|
||||
|
||||
describe("exportDocumentCmd", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("includes title parameter when calling backend", async () => {
|
||||
const mockInvoke = vi.mocked(invoke);
|
||||
mockInvoke.mockResolvedValue("/path/to/doc.pdf");
|
||||
|
||||
const docId = "doc-123";
|
||||
const title = "Test Document";
|
||||
const contentMd = "# Test Content";
|
||||
const format = "pdf";
|
||||
const outputDir = ".";
|
||||
|
||||
await exportDocumentCmd(docId, title, contentMd, format, outputDir);
|
||||
|
||||
// Check that invoke was called with the correct parameters
|
||||
expect(mockInvoke).toHaveBeenCalledWith(
|
||||
"export_document",
|
||||
expect.objectContaining({
|
||||
title,
|
||||
contentMd,
|
||||
format,
|
||||
outputDir,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("handles missing title gracefully", async () => {
|
||||
const mockInvoke = vi.mocked(invoke);
|
||||
mockInvoke.mockRejectedValue("missing required key title");
|
||||
|
||||
const docId = "doc-123";
|
||||
const title = "";
|
||||
const contentMd = "# Test";
|
||||
const format = "pdf";
|
||||
const outputDir = ".";
|
||||
|
||||
await expect(exportDocumentCmd(docId, title, contentMd, format, outputDir)).rejects.toMatch(/title/i);
|
||||
});
|
||||
});
|
||||
98
tests/unit/history.test.tsx
Normal file
98
tests/unit/history.test.tsx
Normal file
@ -0,0 +1,98 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { render, screen, fireEvent } from "@testing-library/react";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import History from "@/pages/History";
|
||||
import { useHistoryStore } from "@/stores/historyStore";
|
||||
import type { IssueSummary } from "@/lib/tauriCommands";
|
||||
|
||||
vi.mock("@/stores/historyStore");
|
||||
|
||||
const mockIssues: IssueSummary[] = [
|
||||
{
|
||||
id: "issue-1",
|
||||
title: "Test Issue 1",
|
||||
severity: "P3",
|
||||
status: "open",
|
||||
category: "linux",
|
||||
created_at: "2026-01-01",
|
||||
updated_at: "2026-01-01",
|
||||
log_count: 1,
|
||||
step_count: 2,
|
||||
},
|
||||
{
|
||||
id: "issue-2",
|
||||
title: "Test Issue 2",
|
||||
severity: "P1",
|
||||
status: "resolved",
|
||||
category: "kubernetes",
|
||||
created_at: "2026-01-02",
|
||||
updated_at: "2026-01-02",
|
||||
log_count: 0,
|
||||
step_count: 3,
|
||||
},
|
||||
];
|
||||
|
||||
describe("History Page", () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(useHistoryStore).mockReturnValue({
|
||||
issues: mockIssues,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
searchQuery: "",
|
||||
loadIssues: vi.fn(),
|
||||
searchIssues: vi.fn(),
|
||||
setSearchQuery: vi.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
it("displays domain column in table", () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<History />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
// Check that Domain header exists
|
||||
const domainHeader = screen.getByText("Domain");
|
||||
expect(domainHeader).toBeInTheDocument();
|
||||
|
||||
// Check that domain values are displayed
|
||||
expect(screen.getByText("linux")).toBeInTheDocument();
|
||||
expect(screen.getByText("kubernetes")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("domain filter is functional", () => {
|
||||
const mockLoadIssues = vi.fn();
|
||||
vi.mocked(useHistoryStore).mockReturnValue({
|
||||
issues: mockIssues,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
searchQuery: "",
|
||||
loadIssues: mockLoadIssues,
|
||||
searchIssues: vi.fn(),
|
||||
setSearchQuery: vi.fn(),
|
||||
});
|
||||
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<History />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
// Find domain filter dropdown button
|
||||
const domainFilter = screen.getByRole("button", { name: /All Domains/i });
|
||||
expect(domainFilter).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("search button is visible and readable", () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<History />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const searchButton = screen.getByRole("button", { name: /search/i });
|
||||
expect(searchButton).toBeInTheDocument();
|
||||
expect(searchButton.className).toContain("outline");
|
||||
});
|
||||
});
|
||||
71
tests/unit/resolution.test.tsx
Normal file
71
tests/unit/resolution.test.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { MemoryRouter, Route, Routes } from "react-router-dom";
|
||||
import Resolution from "@/pages/Resolution";
|
||||
import * as tauriCommands from "@/lib/tauriCommands";
|
||||
|
||||
vi.mock("@/lib/tauriCommands");
|
||||
|
||||
const mockIssueDetail = {
|
||||
issue: {
|
||||
id: "test-id",
|
||||
title: "Test Issue",
|
||||
description: "Test Description",
|
||||
severity: "P3",
|
||||
status: "open",
|
||||
category: "linux",
|
||||
source: "manual",
|
||||
created_at: "2026-01-01",
|
||||
updated_at: "2026-01-01",
|
||||
assigned_to: "",
|
||||
tags: "[]",
|
||||
},
|
||||
log_files: [],
|
||||
resolution_steps: [
|
||||
{
|
||||
id: "step-1",
|
||||
issue_id: "test-id",
|
||||
step_order: 1,
|
||||
why_question: "Why did the service crash?",
|
||||
answer: "Out of memory",
|
||||
evidence: "dmesg shows OOM killer",
|
||||
created_at: "2026-01-01",
|
||||
},
|
||||
],
|
||||
conversations: [],
|
||||
};
|
||||
|
||||
describe("Resolution Page", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(tauriCommands.getIssueCmd).mockResolvedValue(mockIssueDetail);
|
||||
});
|
||||
|
||||
it("renders resolution steps with readable text", async () => {
|
||||
render(
|
||||
<MemoryRouter initialEntries={["/issue/test-id/resolution"]}>
|
||||
<Routes>
|
||||
<Route path="/issue/:id/resolution" element={<Resolution />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const question = await screen.findByText("Why did the service crash?");
|
||||
const answer = await screen.findByText("Out of memory");
|
||||
const evidence = await screen.findByText("dmesg shows OOM killer");
|
||||
|
||||
// Check that text has proper contrast classes
|
||||
expect(question).toBeInTheDocument();
|
||||
expect(answer).toBeInTheDocument();
|
||||
expect(evidence).toBeInTheDocument();
|
||||
|
||||
// Check for readable color classes (not muted-foreground which is too dark)
|
||||
const questionParent = question.closest("p");
|
||||
const answerParent = answer.closest("p");
|
||||
const evidenceParent = evidence.closest("p");
|
||||
|
||||
expect(questionParent?.className).toContain("text-foreground");
|
||||
expect(answerParent?.className).toMatch(/text-foreground/);
|
||||
expect(evidenceParent?.className).toMatch(/text-foreground/);
|
||||
});
|
||||
});
|
||||
@ -1,4 +1,5 @@
|
||||
import { vi, beforeAll, afterAll } from "vitest";
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
|
||||
// Mock Tauri core API
|
||||
vi.mock("@tauri-apps/api/core", () => ({
|
||||
|
||||
Loading…
Reference in New Issue
Block a user