test(decnet_web): MSW-based test foundation for UI refactor
Phase 0 of the decnet_web refactor: stand up an MSW server, fixtures,
and a router-aware render helper so the upcoming god-component splits
(AttackerDetail first) can land with same-commit test coverage.
- msw devDep + setupServer wired into src/test/setup.ts
- src/test/server.ts re-exports server, http, HttpResponse, apiUrl()
- src/test/fixtures/{attacker,decky,canary,topology}.ts factories
- src/test/renderWithRouter.tsx wraps MemoryRouter + ToastProvider
- baseline coverage thresholds (0%) in vite.config.ts; raise per PR
- coverage/ added to decnet_web/.gitignore
Existing Orchestrator/AttackerDetail/ThemeLab tests stay on vi.mock
and continue to pass; new tests use MSW.
This commit is contained in:
1
decnet_web/.gitignore
vendored
1
decnet_web/.gitignore
vendored
@@ -10,6 +10,7 @@ lerna-debug.log*
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
|
||||
510
decnet_web/package-lock.json
generated
510
decnet_web/package-lock.json
generated
@@ -31,6 +31,7 @@
|
||||
"eslint-plugin-react-refresh": "^0.5.2",
|
||||
"globals": "^17.4.0",
|
||||
"jsdom": "^29.1.1",
|
||||
"msw": "^2.14.5",
|
||||
"typescript": "~6.0.2",
|
||||
"typescript-eslint": "^8.58.0",
|
||||
"vite": "^8.0.4",
|
||||
@@ -768,6 +769,93 @@
|
||||
"url": "https://github.com/sponsors/nzakas"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/ansi": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.5.tgz",
|
||||
"integrity": "sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/confirm": {
|
||||
"version": "6.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.0.12.tgz",
|
||||
"integrity": "sha512-h9FgGun3QwVYNj5TWIZZ+slii73bMoBFjPfVIGtnFuL4t8gBiNDV9PcSfIzkuxvgquJKt9nr1QzszpBzTbH8Og==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^11.1.9",
|
||||
"@inquirer/type": "^4.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/core": {
|
||||
"version": "11.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.1.9.tgz",
|
||||
"integrity": "sha512-BDE4fG22uYh1bGSifcj7JSx119TVYNViMhMu85usp4Fswrzh6M0DV3yld64jA98uOAa2GSQ4Bg4bZRm2d2cwSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@inquirer/ansi": "^2.0.5",
|
||||
"@inquirer/figures": "^2.0.5",
|
||||
"@inquirer/type": "^4.0.5",
|
||||
"cli-width": "^4.1.0",
|
||||
"fast-wrap-ansi": "^0.2.0",
|
||||
"mute-stream": "^3.0.0",
|
||||
"signal-exit": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/figures": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.5.tgz",
|
||||
"integrity": "sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/type": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.5.tgz",
|
||||
"integrity": "sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||
@@ -818,6 +906,31 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@mswjs/interceptors": {
|
||||
"version": "0.41.8",
|
||||
"resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.8.tgz",
|
||||
"integrity": "sha512-pRLMNKTSGRoLq+KnEB/7OY5vijw1XmcheAAOiv6pj7W1FG32kAGqj1C/RK/cqxRGr1Fh+zBi8sDur8kj3EQv6A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@open-draft/deferred-promise": "^2.2.0",
|
||||
"@open-draft/logger": "^0.3.0",
|
||||
"@open-draft/until": "^2.0.0",
|
||||
"is-node-process": "^1.2.0",
|
||||
"outvariant": "^1.4.3",
|
||||
"strict-event-emitter": "^0.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@mswjs/interceptors/node_modules/@open-draft/deferred-promise": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz",
|
||||
"integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz",
|
||||
@@ -837,6 +950,31 @@
|
||||
"@emnapi/runtime": "^1.7.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@open-draft/deferred-promise": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-3.0.0.tgz",
|
||||
"integrity": "sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@open-draft/logger": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz",
|
||||
"integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-node-process": "^1.2.0",
|
||||
"outvariant": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@open-draft/until": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz",
|
||||
"integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@oxc-project/types": {
|
||||
"version": "0.123.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.123.0.tgz",
|
||||
@@ -1423,6 +1561,23 @@
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/set-cookie-parser": {
|
||||
"version": "2.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz",
|
||||
"integrity": "sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/statuses": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz",
|
||||
"integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/use-sync-external-store": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
||||
@@ -1940,7 +2095,6 @@
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -2181,6 +2335,31 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-width": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
|
||||
"integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
@@ -2522,6 +2701,13 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz",
|
||||
@@ -2851,6 +3037,33 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-string-truncated-width": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz",
|
||||
"integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-string-width": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz",
|
||||
"integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-string-truncated-width": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-wrap-ansi": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.0.tgz",
|
||||
"integrity": "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-string-width": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
@@ -2990,6 +3203,16 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
@@ -3065,6 +3288,16 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/graphql": {
|
||||
"version": "16.14.0",
|
||||
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.0.tgz",
|
||||
"integrity": "sha512-BBvQ/406p+4CZbTpCbVPSxfzrZrbnuWSP1ELYgyS6B+hNeKzgrdB4JczCa5VZUBQrDa9hUngm0KnexY6pJRN5Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -3114,6 +3347,24 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/headers-polyfill": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-5.0.1.tgz",
|
||||
"integrity": "sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/set-cookie-parser": "^2.4.10",
|
||||
"set-cookie-parser": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/headers-polyfill/node_modules/set-cookie-parser": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz",
|
||||
"integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/hermes-estree": {
|
||||
"version": "0.25.1",
|
||||
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
|
||||
@@ -3227,6 +3478,16 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
@@ -3240,6 +3501,13 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-node-process": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz",
|
||||
"integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-potential-custom-element-name": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
|
||||
@@ -3867,6 +4135,61 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/msw": {
|
||||
"version": "2.14.5",
|
||||
"resolved": "https://registry.npmjs.org/msw/-/msw-2.14.5.tgz",
|
||||
"integrity": "sha512-X6G05oX4x0e+CNI55KMdhMmwHCBKf2iwazGr+iwsdoJ94JA1ED7wSXb6V+lLPdqFkmIlPiGYvayqnaNcOzobDA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@inquirer/confirm": "^6.0.11",
|
||||
"@mswjs/interceptors": "^0.41.3",
|
||||
"@open-draft/deferred-promise": "^3.0.0",
|
||||
"@types/statuses": "^2.0.6",
|
||||
"cookie": "^1.1.1",
|
||||
"graphql": "^16.13.2",
|
||||
"headers-polyfill": "^5.0.1",
|
||||
"is-node-process": "^1.2.0",
|
||||
"outvariant": "^1.4.3",
|
||||
"path-to-regexp": "^6.3.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"rettime": "^0.11.11",
|
||||
"statuses": "^2.0.2",
|
||||
"strict-event-emitter": "^0.5.1",
|
||||
"tough-cookie": "^6.0.1",
|
||||
"type-fest": "^5.5.0",
|
||||
"until-async": "^3.0.2",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"bin": {
|
||||
"msw": "cli/index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/mswjs"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">= 4.8.x"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/mute-stream": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz",
|
||||
"integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "^20.17.0 || >=22.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
@@ -3929,6 +4252,13 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/outvariant": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz",
|
||||
"integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||
@@ -4007,6 +4337,13 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
|
||||
"integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
||||
@@ -4278,6 +4615,16 @@
|
||||
"redux": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
@@ -4304,6 +4651,13 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/rettime": {
|
||||
"version": "0.11.11",
|
||||
"resolved": "https://registry.npmjs.org/rettime/-/rettime-0.11.11.tgz",
|
||||
"integrity": "sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/rolldown": {
|
||||
"version": "1.0.0-rc.13",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.13.tgz",
|
||||
@@ -4431,6 +4785,19 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/solid-js": {
|
||||
"version": "1.9.12",
|
||||
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.12.tgz",
|
||||
@@ -4476,6 +4843,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/std-env": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
|
||||
@@ -4483,6 +4860,41 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/strict-event-emitter": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz",
|
||||
"integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-indent": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
|
||||
@@ -4529,6 +4941,19 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tagged-tag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",
|
||||
"integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/tiny-invariant": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
@@ -4659,6 +5084,22 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.6.0.tgz",
|
||||
"integrity": "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==",
|
||||
"dev": true,
|
||||
"license": "(MIT OR CC0-1.0)",
|
||||
"dependencies": {
|
||||
"tagged-tag": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
|
||||
@@ -4714,6 +5155,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/until-async": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/until-async/-/until-async-3.0.2.tgz",
|
||||
"integrity": "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kettanaito"
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
||||
@@ -5045,6 +5496,24 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/xml-name-validator": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
|
||||
@@ -5062,6 +5531,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
@@ -5069,6 +5548,35 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs-parser": {
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"eslint-plugin-react-refresh": "^0.5.2",
|
||||
"globals": "^17.4.0",
|
||||
"jsdom": "^29.1.1",
|
||||
"msw": "^2.14.5",
|
||||
"typescript": "~6.0.2",
|
||||
"typescript-eslint": "^8.58.0",
|
||||
"vite": "^8.0.4",
|
||||
|
||||
58
decnet_web/src/test/fixtures/attacker.ts
vendored
Normal file
58
decnet_web/src/test/fixtures/attacker.ts
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
export interface AttackerFixture {
|
||||
uuid: string;
|
||||
ip: string;
|
||||
identity_id: string | null;
|
||||
first_seen: string;
|
||||
last_seen: string;
|
||||
event_count: number;
|
||||
service_count: number;
|
||||
decky_count: number;
|
||||
services: string[];
|
||||
deckies: string[];
|
||||
traversal_path: string | null;
|
||||
is_traversal: boolean;
|
||||
bounty_count: number;
|
||||
credential_count: number;
|
||||
fingerprints: unknown[];
|
||||
commands: { service: string; decky: string; command: string; timestamp: string }[];
|
||||
country_code: string | null;
|
||||
country_source: string | null;
|
||||
asn: number | null;
|
||||
as_name: string | null;
|
||||
asn_source: string | null;
|
||||
ptr_record: string | null;
|
||||
updated_at: string;
|
||||
behavior: null;
|
||||
service_activity: { interacted: string[]; scanned: string[] };
|
||||
observations: never[];
|
||||
}
|
||||
|
||||
export const makeAttacker = (overrides: Partial<AttackerFixture> = {}): AttackerFixture => ({
|
||||
uuid: '11111111-1111-1111-1111-111111111111',
|
||||
ip: '198.51.100.10',
|
||||
identity_id: null,
|
||||
first_seen: '2026-05-01T10:00:00Z',
|
||||
last_seen: '2026-05-09T11:00:00Z',
|
||||
event_count: 12,
|
||||
service_count: 2,
|
||||
decky_count: 1,
|
||||
services: ['ssh', 'http'],
|
||||
deckies: ['decoy-01'],
|
||||
traversal_path: null,
|
||||
is_traversal: false,
|
||||
bounty_count: 0,
|
||||
credential_count: 0,
|
||||
fingerprints: [],
|
||||
commands: [],
|
||||
country_code: 'US',
|
||||
country_source: 'maxmind',
|
||||
asn: 64500,
|
||||
as_name: 'EXAMPLE-AS',
|
||||
asn_source: 'maxmind',
|
||||
ptr_record: null,
|
||||
updated_at: '2026-05-09T11:00:00Z',
|
||||
behavior: null,
|
||||
service_activity: { interacted: ['ssh'], scanned: ['http'] },
|
||||
observations: [],
|
||||
...overrides,
|
||||
});
|
||||
63
decnet_web/src/test/fixtures/canary.ts
vendored
Normal file
63
decnet_web/src/test/fixtures/canary.ts
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
export interface CanaryTokenFixture {
|
||||
uuid: string;
|
||||
kind: 'http' | 'dns' | 'aws_passive';
|
||||
decky_name: string;
|
||||
topology_id: string | null;
|
||||
blob_uuid: string | null;
|
||||
instrumenter: string | null;
|
||||
generator: string | null;
|
||||
placement_path: string;
|
||||
callback_token: string;
|
||||
placed_at: string;
|
||||
last_triggered_at: string | null;
|
||||
trigger_count: number;
|
||||
created_by: string;
|
||||
state: 'planted' | 'revoked' | 'failed';
|
||||
last_error: string | null;
|
||||
}
|
||||
|
||||
export interface CanaryBlobFixture {
|
||||
uuid: string;
|
||||
sha256: string;
|
||||
filename: string;
|
||||
content_type: string;
|
||||
size_bytes: number;
|
||||
uploaded_by: string;
|
||||
uploaded_at: string;
|
||||
token_count: number;
|
||||
}
|
||||
|
||||
export const makeCanaryToken = (
|
||||
overrides: Partial<CanaryTokenFixture> = {},
|
||||
): CanaryTokenFixture => ({
|
||||
uuid: '22222222-2222-2222-2222-222222222222',
|
||||
kind: 'http',
|
||||
decky_name: 'decoy-01',
|
||||
topology_id: null,
|
||||
blob_uuid: null,
|
||||
instrumenter: 'git_config',
|
||||
generator: 'git_config',
|
||||
placement_path: '/etc/.git/config',
|
||||
callback_token: 'abc123token',
|
||||
placed_at: '2026-05-01T10:00:00Z',
|
||||
last_triggered_at: null,
|
||||
trigger_count: 0,
|
||||
created_by: 'admin',
|
||||
state: 'planted',
|
||||
last_error: null,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
export const makeCanaryBlob = (
|
||||
overrides: Partial<CanaryBlobFixture> = {},
|
||||
): CanaryBlobFixture => ({
|
||||
uuid: '33333333-3333-3333-3333-333333333333',
|
||||
sha256: 'a'.repeat(64),
|
||||
filename: 'creds.json',
|
||||
content_type: 'application/json',
|
||||
size_bytes: 1024,
|
||||
uploaded_by: 'admin',
|
||||
uploaded_at: '2026-05-01T10:00:00Z',
|
||||
token_count: 0,
|
||||
...overrides,
|
||||
});
|
||||
31
decnet_web/src/test/fixtures/decky.ts
vendored
Normal file
31
decnet_web/src/test/fixtures/decky.ts
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
export interface DeckyFixture {
|
||||
name: string;
|
||||
ip: string;
|
||||
services: string[];
|
||||
distro: string;
|
||||
hostname: string;
|
||||
archetype: string | null;
|
||||
service_config: Record<string, Record<string, unknown>>;
|
||||
mutate_interval: number | null;
|
||||
last_mutated: number;
|
||||
swarm?: {
|
||||
host_uuid: string;
|
||||
host_name: string;
|
||||
state: string;
|
||||
last_error: string | null;
|
||||
last_seen: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
export const makeDecky = (overrides: Partial<DeckyFixture> = {}): DeckyFixture => ({
|
||||
name: 'decoy-01',
|
||||
ip: '10.10.10.10',
|
||||
services: ['ssh', 'http'],
|
||||
distro: 'debian-12',
|
||||
hostname: 'fileserver-01',
|
||||
archetype: 'workstation',
|
||||
service_config: {},
|
||||
mutate_interval: null,
|
||||
last_mutated: 0,
|
||||
...overrides,
|
||||
});
|
||||
9
decnet_web/src/test/fixtures/index.ts
vendored
Normal file
9
decnet_web/src/test/fixtures/index.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
export { makeAttacker, type AttackerFixture } from './attacker';
|
||||
export { makeDecky, type DeckyFixture } from './decky';
|
||||
export {
|
||||
makeCanaryToken,
|
||||
makeCanaryBlob,
|
||||
type CanaryTokenFixture,
|
||||
type CanaryBlobFixture,
|
||||
} from './canary';
|
||||
export { makeTopology, type TopologyFixture } from './topology';
|
||||
18
decnet_web/src/test/fixtures/topology.ts
vendored
Normal file
18
decnet_web/src/test/fixtures/topology.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
export interface TopologyFixture {
|
||||
id: string;
|
||||
name: string;
|
||||
mode: string;
|
||||
target_host_uuid: string | null;
|
||||
status: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
export const makeTopology = (overrides: Partial<TopologyFixture> = {}): TopologyFixture => ({
|
||||
id: '44444444-4444-4444-4444-444444444444',
|
||||
name: 'corp-net-01',
|
||||
mode: 'flat',
|
||||
target_host_uuid: null,
|
||||
status: 'active',
|
||||
version: 1,
|
||||
...overrides,
|
||||
});
|
||||
34
decnet_web/src/test/renderWithRouter.tsx
Normal file
34
decnet_web/src/test/renderWithRouter.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import { render, type RenderOptions, type RenderResult } from '@testing-library/react';
|
||||
import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||
import { ToastProvider } from '../components/Toasts/ToastProvider';
|
||||
|
||||
export interface RenderWithRouterOptions extends Omit<RenderOptions, 'wrapper'> {
|
||||
/** Initial URL the MemoryRouter starts at. */
|
||||
initialEntries?: string[];
|
||||
/** When set, the rendered UI is mounted at this route path so `useParams` resolves. */
|
||||
path?: string;
|
||||
}
|
||||
|
||||
const Wrap = ({ children }: { children: ReactNode }) => (
|
||||
<ToastProvider>{children}</ToastProvider>
|
||||
);
|
||||
|
||||
export const renderWithRouter = (
|
||||
ui: ReactElement,
|
||||
{ initialEntries = ['/'], path, ...rest }: RenderWithRouterOptions = {},
|
||||
): RenderResult => {
|
||||
const tree = path ? (
|
||||
<Routes>
|
||||
<Route path={path} element={ui} />
|
||||
</Routes>
|
||||
) : (
|
||||
ui
|
||||
);
|
||||
return render(
|
||||
<MemoryRouter initialEntries={initialEntries}>
|
||||
<Wrap>{tree}</Wrap>
|
||||
</MemoryRouter>,
|
||||
rest,
|
||||
);
|
||||
};
|
||||
13
decnet_web/src/test/server.ts
Normal file
13
decnet_web/src/test/server.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { setupServer } from 'msw/node';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
|
||||
export const API_BASE = 'http://localhost:8000/api/v1';
|
||||
|
||||
export const server = setupServer();
|
||||
|
||||
export { http, HttpResponse };
|
||||
|
||||
export const apiUrl = (path: string): string => {
|
||||
const trimmed = path.startsWith('/') ? path : `/${path}`;
|
||||
return `${API_BASE}${trimmed}`;
|
||||
};
|
||||
@@ -1,7 +1,13 @@
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
import { afterEach } from 'vitest';
|
||||
import { afterAll, afterEach, beforeAll } from 'vitest';
|
||||
import { cleanup } from '@testing-library/react';
|
||||
import { server } from './server';
|
||||
|
||||
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
server.resetHandlers();
|
||||
});
|
||||
|
||||
afterAll(() => server.close());
|
||||
|
||||
@@ -14,6 +14,13 @@ export default defineConfig({
|
||||
reporter: ['text', 'html'],
|
||||
include: ['src/**/*.{ts,tsx}'],
|
||||
exclude: ['src/**/*.d.ts', 'src/test/**', 'src/main.tsx'],
|
||||
// Baseline floors. Each refactor PR raises these; never lower.
|
||||
thresholds: {
|
||||
lines: 0,
|
||||
functions: 0,
|
||||
branches: 0,
|
||||
statements: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
server: {
|
||||
|
||||
Reference in New Issue
Block a user