@ripla/godd-mcp 1.0.4-canary.9 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (348) hide show
  1. package/README.md +89 -94
  2. package/dist/godd.cjs +349 -233
  3. package/dist/godd.js +2193 -838
  4. package/dist/index.js +152 -122
  5. package/{notes-api → notes/api}/.env.example +6 -1
  6. package/{notes-api → notes/api}/README.md +6 -5
  7. package/{notes-api → notes/api}/app/main.py +29 -2
  8. package/{notes-api → notes/api}/app/routers/auth.py +6 -1
  9. package/{notes-api → notes/api}/app/routers/issues.py +157 -1
  10. package/{notes-api → notes/api}/app/routers/pr.py +2 -2
  11. package/{notes-api → notes/api}/app/services/github_app_token.py +32 -1
  12. package/{notes-api → notes/api}/app/services/github_issues.py +101 -11
  13. package/{notes-api → notes/api}/app/services/github_pr.py +6 -1
  14. package/{notes-api → notes/api}/tests/test_github_app_token.py +49 -1
  15. package/{notes-api → notes/api}/tests/test_github_service.py +42 -0
  16. package/notes/api/tests/test_health.py +52 -0
  17. package/{notes-api → notes/api}/tests/test_issues.py +216 -0
  18. package/{notes-api → notes/api}/tests/test_pr.py +24 -0
  19. package/{notes-api → notes/api}/tests/test_pr_chain.py +19 -0
  20. package/{notes-api → notes/api}/uv.lock +178 -2
  21. package/notes/app/DESIGN.md +273 -0
  22. package/{notes-app → notes/app}/README.md +4 -1
  23. package/notes/app/docker/Dockerfile.e2e +19 -0
  24. package/notes/app/docker/Dockerfile.prod +15 -0
  25. package/notes/app/docker/Dockerfile.test +17 -0
  26. package/notes/app/e2e/file-tree.spec.ts +26 -0
  27. package/notes/app/e2e/file-view.spec.ts +25 -0
  28. package/notes/app/e2e/fixtures.ts +28 -0
  29. package/{notes-app → notes/app}/e2e/global-setup.ts +5 -7
  30. package/notes/app/e2e/login.spec.ts +25 -0
  31. package/notes/app/e2e/pages/login-page.ts +29 -0
  32. package/notes/app/e2e/pages/main-page.ts +29 -0
  33. package/notes/app/e2e/panel-resize-handle.spec.ts +213 -0
  34. package/{notes-app → notes/app}/eslint.config.js +9 -0
  35. package/{notes-app → notes/app}/package.json +0 -3
  36. package/{notes-app → notes/app}/pnpm-workspace.yaml +1 -0
  37. package/notes/app/src/components/CommentPanel.test.tsx +194 -0
  38. package/{notes-app → notes/app}/src/components/CommentPanel.tsx +119 -10
  39. package/notes/app/src/components/CommentSelectionPopover.tsx +26 -0
  40. package/notes/app/src/components/EditToolbar.test.tsx +102 -0
  41. package/{notes-app → notes/app}/src/components/EditToolbar.tsx +5 -54
  42. package/notes/app/src/components/FileInfoPanel.branch-tab.test.tsx +250 -0
  43. package/notes/app/src/components/FileInfoPanel.history-tab.test.tsx +146 -0
  44. package/notes/app/src/components/FileInfoPanel.test.tsx +101 -0
  45. package/{notes-app → notes/app}/src/components/FileInfoPanel.tsx +135 -20
  46. package/notes/app/src/components/IssueCommentThread.test.tsx +93 -0
  47. package/notes/app/src/components/IssueCommentThread.tsx +156 -0
  48. package/notes/app/src/components/IssueDetailView.test.tsx +1118 -0
  49. package/notes/app/src/components/IssueDetailView.tsx +476 -0
  50. package/notes/app/src/components/IssueList.test.tsx +545 -0
  51. package/notes/app/src/components/IssueList.tsx +374 -0
  52. package/{notes-app → notes/app}/src/components/LabelSelector.test.tsx +94 -8
  53. package/notes/app/src/components/LabelSelector.tsx +452 -0
  54. package/{notes-app → notes/app}/src/components/MarkdownEditor.tsx +140 -2
  55. package/notes/app/src/components/PanelResizeHandle.test.tsx +481 -0
  56. package/notes/app/src/components/PanelResizeHandle.tsx +96 -0
  57. package/{notes-app → notes/app}/src/hooks/useEditSession.test.ts +171 -23
  58. package/{notes-app → notes/app}/src/hooks/useEditSession.ts +49 -62
  59. package/notes/app/src/hooks/useMdUpLayout.test.ts +10 -0
  60. package/notes/app/src/hooks/useMdUpLayout.ts +27 -0
  61. package/notes/app/src/hooks/usePeriodicSync.test.ts +244 -0
  62. package/notes/app/src/hooks/usePeriodicSync.ts +83 -0
  63. package/notes/app/src/hooks/useResizablePanelWidth.test.ts +251 -0
  64. package/notes/app/src/hooks/useResizablePanelWidth.ts +167 -0
  65. package/{notes-app → notes/app}/src/lib/api.test.ts +81 -1
  66. package/{notes-app → notes/app}/src/lib/api.ts +98 -4
  67. package/notes/app/src/lib/commentAnchor.test.ts +22 -0
  68. package/notes/app/src/lib/commentAnchor.ts +28 -0
  69. package/notes/app/src/lib/commentHighlight.test.ts +110 -0
  70. package/notes/app/src/lib/commentHighlight.ts +44 -0
  71. package/notes/app/src/lib/github-sync.test.ts +126 -0
  72. package/notes/app/src/lib/github-sync.ts +89 -0
  73. package/notes/app/src/lib/issue-list-utils.test.ts +275 -0
  74. package/notes/app/src/lib/issue-list-utils.ts +135 -0
  75. package/notes/app/src/lib/issue-mobile-layout.test.ts +17 -0
  76. package/notes/app/src/lib/issue-mobile-layout.ts +20 -0
  77. package/notes/app/src/lib/issue-url-sync.test.ts +73 -0
  78. package/notes/app/src/lib/issue-url-sync.ts +43 -0
  79. package/notes/app/src/lib/main-page-default-view.test.ts +37 -0
  80. package/notes/app/src/lib/main-page-default-view.ts +15 -0
  81. package/notes/app/src/lib/resizable-panel.test.ts +199 -0
  82. package/notes/app/src/lib/resizable-panel.ts +136 -0
  83. package/notes/app/src/pages/LoginPage.test.tsx +17 -0
  84. package/notes/app/src/pages/MainPage.branch-modal.test.tsx +226 -0
  85. package/notes/app/src/pages/MainPage.issue-sync.test.tsx +145 -0
  86. package/notes/app/src/pages/MainPage.sidebar.test.tsx +361 -0
  87. package/{notes-app → notes/app}/src/pages/MainPage.tsx +406 -61
  88. package/{notes-app → notes/app}/src/pages/SettingsPage.test.tsx +32 -23
  89. package/{notes-app → notes/app}/src/pages/UserManagementPage.test.tsx +8 -18
  90. package/notes/app/src/test/mocks.ts +156 -0
  91. package/notes/app/src/test/render.tsx +27 -0
  92. package/package.json +3 -5
  93. package/notes-api/tests/test_health.py +0 -11
  94. package/notes-app/docker/Dockerfile.e2e +0 -14
  95. package/notes-app/docker/Dockerfile.prod +0 -12
  96. package/notes-app/docker/Dockerfile.test +0 -12
  97. package/notes-app/e2e/file-tree.spec.ts +0 -29
  98. package/notes-app/e2e/file-view.spec.ts +0 -24
  99. package/notes-app/e2e/login.spec.ts +0 -32
  100. package/notes-app/src/components/IssueDetailView.test.tsx +0 -201
  101. package/notes-app/src/components/IssueDetailView.tsx +0 -318
  102. package/notes-app/src/components/IssueList.test.tsx +0 -93
  103. package/notes-app/src/components/IssueList.tsx +0 -169
  104. package/notes-app/src/components/LabelSelector.tsx +0 -276
  105. package/notes-app/src/pages/LoginPage.test.tsx +0 -29
  106. package/stacks/django-vue.yaml +0 -33
  107. package/stacks/fastapi-react.yaml +0 -160
  108. package/stacks/flutter-firebase.yaml +0 -58
  109. package/stacks/nextjs-prisma.yaml +0 -33
  110. package/stacks/schema.json +0 -144
  111. package/templates/agents/architect.hbs +0 -183
  112. package/templates/agents/auto-documenter.hbs +0 -36
  113. package/templates/agents/backend-developer.hbs +0 -175
  114. package/templates/agents/code-reviewer.hbs +0 -29
  115. package/templates/agents/database-developer.hbs +0 -52
  116. package/templates/agents/debug-specialist.hbs +0 -39
  117. package/templates/agents/environment-setup.hbs +0 -41
  118. package/templates/agents/frontend-developer.hbs +0 -45
  119. package/templates/agents/integration-qa.hbs +0 -26
  120. package/templates/agents/performance-qa.hbs +0 -24
  121. package/templates/agents/pr-writer.hbs +0 -53
  122. package/templates/agents/quality-lead.hbs +0 -26
  123. package/templates/agents/requirements-analyst.hbs +0 -26
  124. package/templates/agents/security-analyst.hbs +0 -21
  125. package/templates/agents/security-reviewer.hbs +0 -20
  126. package/templates/agents/ssot-updater.hbs +0 -45
  127. package/templates/agents/technical-writer.hbs +0 -28
  128. package/templates/agents/unit-test-engineer.hbs +0 -19
  129. package/templates/agents/user-clone.hbs +0 -49
  130. package/templates/github-actions/aws/deploy-notes.yml.hbs +0 -117
  131. package/templates/github-actions/azure/deploy-notes.yml.hbs +0 -78
  132. package/templates/github-actions/gcp/deploy-notes.yml.hbs +0 -76
  133. package/templates/github-actions/vercel-railway/deploy-notes.yml.hbs +0 -49
  134. package/templates/mindsets/agent-stdio.hbs +0 -51
  135. package/templates/mindsets/dart.hbs +0 -39
  136. package/templates/mindsets/ddd-clean-architecture.hbs +0 -52
  137. package/templates/mindsets/electron.hbs +0 -41
  138. package/templates/mindsets/fastapi.hbs +0 -40
  139. package/templates/mindsets/flutter.hbs +0 -56
  140. package/templates/mindsets/kotlin.hbs +0 -46
  141. package/templates/mindsets/postgresql.hbs +0 -39
  142. package/templates/mindsets/python.hbs +0 -39
  143. package/templates/mindsets/react.hbs +0 -51
  144. package/templates/mindsets/swift.hbs +0 -46
  145. package/templates/mindsets/terraform.hbs +0 -53
  146. package/templates/mindsets/typescript.hbs +0 -34
  147. package/templates/mindsets/vite.hbs +0 -37
  148. package/templates/notes-compose.yml +0 -82
  149. package/templates/partials/cogito-geo.hbs +0 -84
  150. package/templates/partials/cogito-godd.hbs +0 -22
  151. package/templates/partials/cogito-grammar.hbs +0 -105
  152. package/templates/partials/cogito-particle.hbs +0 -76
  153. package/templates/partials/cogito-root-ai.hbs +0 -79
  154. package/templates/partials/cogito-root-arch.hbs +0 -75
  155. package/templates/partials/cogito-root-core.hbs +0 -75
  156. package/templates/partials/cogito-root-daily.hbs +0 -58
  157. package/templates/partials/cogito-root-dev.hbs +0 -71
  158. package/templates/partials/cogito-root-devops.hbs +0 -71
  159. package/templates/partials/cogito-root-framework.hbs +0 -80
  160. package/templates/partials/cogito-root-lang.hbs +0 -74
  161. package/templates/partials/godd-core.hbs +0 -50
  162. package/templates/partials/project-context.hbs +0 -52
  163. package/templates/partials/ssot-header.hbs +0 -40
  164. package/templates/partials/ssot-rules.hbs +0 -30
  165. package/templates/prompts/adr.hbs +0 -39
  166. package/templates/prompts/agent-dev-workflow.hbs +0 -34
  167. package/templates/prompts/analyze-report.hbs +0 -36
  168. package/templates/prompts/auto-documentation.hbs +0 -37
  169. package/templates/prompts/check.hbs +0 -52
  170. package/templates/prompts/commit.hbs +0 -96
  171. package/templates/prompts/dev.hbs +0 -127
  172. package/templates/prompts/docs-init.hbs +0 -101
  173. package/templates/prompts/docs-update.hbs +0 -51
  174. package/templates/prompts/git-workflow.hbs +0 -125
  175. package/templates/prompts/impact-analysis.hbs +0 -43
  176. package/templates/prompts/install.hbs +0 -40
  177. package/templates/prompts/new-branch.hbs +0 -61
  178. package/templates/prompts/notes-deploy.hbs +0 -138
  179. package/templates/prompts/pr-analyze.hbs +0 -44
  180. package/templates/prompts/pr-create.hbs +0 -125
  181. package/templates/prompts/push-execute.hbs +0 -83
  182. package/templates/prompts/push.hbs +0 -24
  183. package/templates/prompts/release-notes.hbs +0 -38
  184. package/templates/prompts/requirements-to-business-flow.hbs +0 -29
  185. package/templates/prompts/requirements-to-tickets.hbs +0 -26
  186. package/templates/prompts/review.hbs +0 -126
  187. package/templates/prompts/setup.hbs +0 -247
  188. package/templates/prompts/spec.hbs +0 -35
  189. package/templates/prompts/specification-to-tickets.hbs +0 -24
  190. package/templates/prompts/submit.hbs +0 -143
  191. package/templates/prompts/sync.hbs +0 -63
  192. package/templates/prompts/test-plan.hbs +0 -56
  193. package/templates/scripts/aws/terraform-backend-setup.sh.hbs +0 -149
  194. package/templates/terraform/aws/alb.tf.hbs +0 -58
  195. package/templates/terraform/aws/backend.tf.hbs +0 -10
  196. package/templates/terraform/aws/cloudfront.tf.hbs +0 -96
  197. package/templates/terraform/aws/ecr.tf.hbs +0 -30
  198. package/templates/terraform/aws/ecs.tf.hbs +0 -142
  199. package/templates/terraform/aws/iam.tf.hbs +0 -88
  200. package/templates/terraform/aws/local.tf.hbs +0 -34
  201. package/templates/terraform/aws/output.tf.hbs +0 -49
  202. package/templates/terraform/aws/provider.tf.hbs +0 -26
  203. package/templates/terraform/aws/rds.tf.hbs +0 -38
  204. package/templates/terraform/aws/s3.tf.hbs +0 -34
  205. package/templates/terraform/aws/secrets.tf.hbs +0 -53
  206. package/templates/terraform/aws/security_groups.tf.hbs +0 -81
  207. package/templates/terraform/aws/vpc.tf.hbs +0 -63
  208. package/templates/terraform/azure/backend.tf.hbs +0 -8
  209. package/templates/terraform/azure/iam.tf.hbs +0 -36
  210. package/templates/terraform/azure/main.tf.hbs +0 -203
  211. package/templates/terraform/azure/output.tf.hbs +0 -19
  212. package/templates/terraform/azure/provider.tf.hbs +0 -19
  213. package/templates/terraform/gcp/backend.tf.hbs +0 -6
  214. package/templates/terraform/gcp/iam.tf.hbs +0 -47
  215. package/templates/terraform/gcp/main.tf.hbs +0 -175
  216. package/templates/terraform/gcp/output.tf.hbs +0 -15
  217. package/templates/terraform/gcp/provider.tf.hbs +0 -19
  218. package/templates/terraform/vercel-railway/main.tf.hbs +0 -64
  219. package/templates/terraform/vercel-railway/output.tf.hbs +0 -11
  220. /package/{notes-api → notes/api}/.dockerignore +0 -0
  221. /package/{notes-api → notes/api}/alembic/README +0 -0
  222. /package/{notes-api → notes/api}/alembic/env.py +0 -0
  223. /package/{notes-api → notes/api}/alembic/script.py.mako +0 -0
  224. /package/{notes-api → notes/api}/alembic/versions/001_initial_notes_tables.py +0 -0
  225. /package/{notes-api → notes/api}/alembic/versions/002_add_performance_indexes.py +0 -0
  226. /package/{notes-api → notes/api}/alembic/versions/003_add_yjs_documents.py +0 -0
  227. /package/{notes-api → notes/api}/alembic/versions/004_add_comments.py +0 -0
  228. /package/{notes-api → notes/api}/alembic/versions/005_add_github_username.py +0 -0
  229. /package/{notes-api → notes/api}/alembic/versions/006_add_check_constraints.py +0 -0
  230. /package/{notes-api → notes/api}/alembic.ini +0 -0
  231. /package/{notes-api → notes/api}/app/__init__.py +0 -0
  232. /package/{notes-api → notes/api}/app/config.py +0 -0
  233. /package/{notes-api → notes/api}/app/database.py +0 -0
  234. /package/{notes-api → notes/api}/app/http_client.py +0 -0
  235. /package/{notes-api → notes/api}/app/models/__init__.py +0 -0
  236. /package/{notes-api → notes/api}/app/models/audit.py +0 -0
  237. /package/{notes-api → notes/api}/app/models/base.py +0 -0
  238. /package/{notes-api → notes/api}/app/models/collab.py +0 -0
  239. /package/{notes-api → notes/api}/app/models/comment.py +0 -0
  240. /package/{notes-api → notes/api}/app/models/session.py +0 -0
  241. /package/{notes-api → notes/api}/app/models/setting.py +0 -0
  242. /package/{notes-api → notes/api}/app/models/user.py +0 -0
  243. /package/{notes-api → notes/api}/app/path_validation.py +0 -0
  244. /package/{notes-api → notes/api}/app/routers/__init__.py +0 -0
  245. /package/{notes-api → notes/api}/app/routers/collab.py +0 -0
  246. /package/{notes-api → notes/api}/app/routers/comments.py +0 -0
  247. /package/{notes-api → notes/api}/app/routers/files.py +0 -0
  248. /package/{notes-api → notes/api}/app/routers/settings.py +0 -0
  249. /package/{notes-api → notes/api}/app/routers/tree.py +0 -0
  250. /package/{notes-api → notes/api}/app/routers/upload.py +0 -0
  251. /package/{notes-api → notes/api}/app/routers/users.py +0 -0
  252. /package/{notes-api → notes/api}/app/schemas/__init__.py +0 -0
  253. /package/{notes-api → notes/api}/app/schemas/auth.py +0 -0
  254. /package/{notes-api → notes/api}/app/schemas/user.py +0 -0
  255. /package/{notes-api → notes/api}/app/security.py +0 -0
  256. /package/{notes-api → notes/api}/app/services/__init__.py +0 -0
  257. /package/{notes-api → notes/api}/app/services/audit.py +0 -0
  258. /package/{notes-api → notes/api}/app/services/business_date.py +0 -0
  259. /package/{notes-api → notes/api}/app/services/collab.py +0 -0
  260. /package/{notes-api → notes/api}/app/services/github.py +0 -0
  261. /package/{notes-api → notes/api}/app/services/pr_chain.py +0 -0
  262. /package/{notes-api → notes/api}/app/startup.py +0 -0
  263. /package/{notes-api → notes/api}/docker/Dockerfile +0 -0
  264. /package/{notes-api → notes/api}/docker/Dockerfile.test +0 -0
  265. /package/{notes-api → notes/api}/pyproject.toml +0 -0
  266. /package/{notes-api → notes/api}/tests/conftest.py +0 -0
  267. /package/{notes-api → notes/api}/tests/test_audit.py +0 -0
  268. /package/{notes-api → notes/api}/tests/test_auth.py +0 -0
  269. /package/{notes-api → notes/api}/tests/test_business_date.py +0 -0
  270. /package/{notes-api → notes/api}/tests/test_collab.py +0 -0
  271. /package/{notes-api → notes/api}/tests/test_comments.py +0 -0
  272. /package/{notes-api → notes/api}/tests/test_config_ssl.py +0 -0
  273. /package/{notes-api → notes/api}/tests/test_config_warnings.py +0 -0
  274. /package/{notes-api → notes/api}/tests/test_files.py +0 -0
  275. /package/{notes-api → notes/api}/tests/test_path_validation.py +0 -0
  276. /package/{notes-api → notes/api}/tests/test_security.py +0 -0
  277. /package/{notes-api → notes/api}/tests/test_settings.py +0 -0
  278. /package/{notes-api → notes/api}/tests/test_tree.py +0 -0
  279. /package/{notes-api → notes/api}/tests/test_upload.py +0 -0
  280. /package/{notes-api → notes/api}/tests/test_users.py +0 -0
  281. /package/{notes-app → notes/app}/.dockerignore +0 -0
  282. /package/{notes-app → notes/app}/index.html +0 -0
  283. /package/{notes-app → notes/app}/nginx.conf +0 -0
  284. /package/{notes-app → notes/app}/package-lock.json +0 -0
  285. /package/{notes-app → notes/app}/playwright.config.ts +0 -0
  286. /package/{notes-app → notes/app}/pnpm-lock.yaml +0 -0
  287. /package/{notes-app → notes/app}/src/App.tsx +0 -0
  288. /package/{notes-app → notes/app}/src/components/BranchSelectorModal.tsx +0 -0
  289. /package/{notes-app → notes/app}/src/components/CodeBlockView.test.tsx +0 -0
  290. /package/{notes-app → notes/app}/src/components/CodeBlockView.tsx +0 -0
  291. /package/{notes-app → notes/app}/src/components/CollabStatus.tsx +0 -0
  292. /package/{notes-app → notes/app}/src/components/DiffPreview.tsx +0 -0
  293. /package/{notes-app → notes/app}/src/components/DrawioEditor.tsx +0 -0
  294. /package/{notes-app → notes/app}/src/components/DrawioViewer.tsx +0 -0
  295. /package/{notes-app → notes/app}/src/components/ErrorBoundary.test.tsx +0 -0
  296. /package/{notes-app → notes/app}/src/components/ErrorBoundary.tsx +0 -0
  297. /package/{notes-app → notes/app}/src/components/FileContentView.tsx +0 -0
  298. /package/{notes-app → notes/app}/src/components/FileContextMenu.test.tsx +0 -0
  299. /package/{notes-app → notes/app}/src/components/FileContextMenu.tsx +0 -0
  300. /package/{notes-app → notes/app}/src/components/FileDialogs.test.tsx +0 -0
  301. /package/{notes-app → notes/app}/src/components/FileDialogs.tsx +0 -0
  302. /package/{notes-app → notes/app}/src/components/SaveStatusIndicator.test.tsx +0 -0
  303. /package/{notes-app → notes/app}/src/components/SaveStatusIndicator.tsx +0 -0
  304. /package/{notes-app → notes/app}/src/components/Skeleton.tsx +0 -0
  305. /package/{notes-app → notes/app}/src/components/SpreadsheetEditor.test.tsx +0 -0
  306. /package/{notes-app → notes/app}/src/components/SpreadsheetEditor.tsx +0 -0
  307. /package/{notes-app → notes/app}/src/components/TextEditor.tsx +0 -0
  308. /package/{notes-app → notes/app}/src/components/ToastContainer.tsx +0 -0
  309. /package/{notes-app → notes/app}/src/components/TreeNode.tsx +0 -0
  310. /package/{notes-app → notes/app}/src/contexts/AuthContext.test.tsx +0 -0
  311. /package/{notes-app → notes/app}/src/contexts/AuthContext.tsx +0 -0
  312. /package/{notes-app → notes/app}/src/contexts/ToastContext.test.ts +0 -0
  313. /package/{notes-app → notes/app}/src/contexts/ToastContext.tsx +0 -0
  314. /package/{notes-app → notes/app}/src/hooks/useAutoSave.test.ts +0 -0
  315. /package/{notes-app → notes/app}/src/hooks/useAutoSave.ts +0 -0
  316. /package/{notes-app → notes/app}/src/hooks/useCollaboration.ts +0 -0
  317. /package/{notes-app → notes/app}/src/hooks/useComments.ts +0 -0
  318. /package/{notes-app → notes/app}/src/hooks/useYText.ts +0 -0
  319. /package/{notes-app → notes/app}/src/lib/branch-diff.test.ts +0 -0
  320. /package/{notes-app → notes/app}/src/lib/branch-diff.ts +0 -0
  321. /package/{notes-app → notes/app}/src/lib/created-file.test.ts +0 -0
  322. /package/{notes-app → notes/app}/src/lib/created-file.ts +0 -0
  323. /package/{notes-app → notes/app}/src/lib/csv-utils.test.ts +0 -0
  324. /package/{notes-app → notes/app}/src/lib/csv-utils.ts +0 -0
  325. /package/{notes-app → notes/app}/src/lib/deepLinkPathToSearch.test.ts +0 -0
  326. /package/{notes-app → notes/app}/src/lib/deepLinkPathToSearch.ts +0 -0
  327. /package/{notes-app → notes/app}/src/lib/draft-storage.test.ts +0 -0
  328. /package/{notes-app → notes/app}/src/lib/draft-storage.ts +0 -0
  329. /package/{notes-app → notes/app}/src/lib/mermaid-render.ts +0 -0
  330. /package/{notes-app → notes/app}/src/lib/postLoginRedirect.test.ts +0 -0
  331. /package/{notes-app → notes/app}/src/lib/postLoginRedirect.ts +0 -0
  332. /package/{notes-app → notes/app}/src/lib/tree-utils.ts +0 -0
  333. /package/{notes-app → notes/app}/src/lib/user-colors.ts +0 -0
  334. /package/{notes-app → notes/app}/src/lib/xlsx-utils.test.ts +0 -0
  335. /package/{notes-app → notes/app}/src/lib/xlsx-utils.ts +0 -0
  336. /package/{notes-app → notes/app}/src/main.tsx +0 -0
  337. /package/{notes-app → notes/app}/src/pages/LoginPage.tsx +0 -0
  338. /package/{notes-app → notes/app}/src/pages/MainPage.insertChildEntry.test.ts +0 -0
  339. /package/{notes-app → notes/app}/src/pages/SettingsPage.tsx +0 -0
  340. /package/{notes-app → notes/app}/src/pages/UserManagementPage.tsx +0 -0
  341. /package/{notes-app → notes/app}/src/styles/globals.css +0 -0
  342. /package/{notes-app → notes/app}/src/types.ts +0 -0
  343. /package/{notes-app → notes/app}/src/vite-env.d.ts +0 -0
  344. /package/{notes-app → notes/app}/tsconfig.json +0 -0
  345. /package/{notes-app → notes/app}/tsconfig.node.json +0 -0
  346. /package/{notes-app → notes/app}/vite.config.js +0 -0
  347. /package/{notes-app → notes/app}/vite.config.ts +0 -0
  348. /package/{notes-app → notes/app}/vitest.config.ts +0 -0
@@ -1,201 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import { render, screen, fireEvent, waitFor, act } from "@testing-library/react";
3
-
4
- vi.mock("@/lib/api", () => ({
5
- createIssueApi: vi.fn(),
6
- updateIssueApi: vi.fn(),
7
- getIssueApi: vi.fn(),
8
- getErrorMessage: vi.fn((e: unknown) => String(e)),
9
- isAbortError: vi.fn(() => false),
10
- }));
11
-
12
- vi.mock("@/contexts/AuthContext", () => ({
13
- useAuth: () => ({ canEdit: true }),
14
- }));
15
-
16
- const mockShowToast = vi.hoisted(() => vi.fn());
17
- vi.mock("@/contexts/ToastContext", () => ({
18
- useToast: () => ({ showToast: mockShowToast }),
19
- }));
20
-
21
- vi.mock("@/components/FileContentView", () => ({
22
- default: ({ content }: { content: string }) => (
23
- <div data-testid="file-content-view">{content}</div>
24
- ),
25
- }));
26
-
27
- import { createIssueApi, updateIssueApi, getIssueApi } from "@/lib/api";
28
- import IssueDetailView from "./IssueDetailView";
29
-
30
- const mockCreateIssue = vi.mocked(createIssueApi);
31
- const mockUpdateIssue = vi.mocked(updateIssueApi);
32
- const mockGetIssue = vi.mocked(getIssueApi);
33
-
34
- const MOCK_ISSUE = {
35
- number: 123,
36
- title: "テスト Issue",
37
- body: "本文テスト",
38
- state: "open" as const,
39
- author: "testuser",
40
- assignees: [],
41
- comments: 0,
42
- createdAt: "2026-05-19T00:00:00Z",
43
- updatedAt: "2026-05-19T00:00:00Z",
44
- url: "https://github.com/test/repo/issues/123",
45
- labels: [],
46
- };
47
-
48
- describe("IssueDetailView — 新規 Issue 作成", () => {
49
- beforeEach(() => {
50
- vi.clearAllMocks();
51
- });
52
-
53
- it("作成フォームが表示される(isNew=true)", () => {
54
- render(
55
- <IssueDetailView issueNumber="new" onCreated={vi.fn()} onClose={vi.fn()} />,
56
- );
57
- expect(screen.getByText("新規 Issue 作成")).toBeTruthy();
58
- expect(screen.getByPlaceholderText("Issue のタイトル")).toBeTruthy();
59
- expect(screen.getByText("作成")).toBeTruthy();
60
- });
61
-
62
- it("タイトル未入力時は作成ボタンが disabled", () => {
63
- render(
64
- <IssueDetailView issueNumber="new" onCreated={vi.fn()} onClose={vi.fn()} />,
65
- );
66
- const btn = screen.getByText("作成");
67
- expect((btn as HTMLButtonElement).disabled).toBe(true);
68
- });
69
-
70
- it("作成成功後にプレビュー画面へ遷移し、onCreated が呼ばれる", async () => {
71
- mockCreateIssue.mockResolvedValue(MOCK_ISSUE);
72
-
73
- const onCreated = vi.fn();
74
- render(
75
- <IssueDetailView issueNumber="new" onCreated={onCreated} onClose={vi.fn()} />,
76
- );
77
-
78
- const titleInput = screen.getByPlaceholderText("Issue のタイトル");
79
- fireEvent.change(titleInput, { target: { value: "テスト Issue" } });
80
-
81
- await act(async () => {
82
- fireEvent.click(screen.getByText("作成"));
83
- });
84
-
85
- // プレビュー画面に遷移(編集フォームが消える)
86
- await waitFor(() => {
87
- expect(screen.queryByText("新規 Issue 作成")).toBeNull();
88
- });
89
-
90
- // Issue タイトルがプレビュー表示される
91
- expect(screen.getByText("テスト Issue")).toBeTruthy();
92
-
93
- // onCreated コールバックが作成された Issue 番号で呼ばれる
94
- expect(onCreated).toHaveBeenCalledWith(123);
95
- });
96
-
97
- it("キャンセルボタンで onClose が呼ばれる", () => {
98
- const onClose = vi.fn();
99
- render(
100
- <IssueDetailView issueNumber="new" onCreated={vi.fn()} onClose={onClose} />,
101
- );
102
- fireEvent.click(screen.getByText("キャンセル"));
103
- expect(onClose).toHaveBeenCalledOnce();
104
- });
105
- });
106
-
107
- describe("IssueDetailView — 既存 Issue 更新", () => {
108
- beforeEach(() => {
109
- vi.clearAllMocks();
110
- mockGetIssue.mockResolvedValue(MOCK_ISSUE);
111
- });
112
-
113
- it("更新成功後にプレビュー画面へ遷移する(既存の更新と同じ挙動)", async () => {
114
- const updatedIssue = { ...MOCK_ISSUE, title: "更新後タイトル" };
115
- mockUpdateIssue.mockResolvedValue(updatedIssue);
116
-
117
- render(
118
- <IssueDetailView issueNumber={123} onCreated={vi.fn()} onClose={vi.fn()} />,
119
- );
120
-
121
- await waitFor(() => {
122
- expect(screen.getByText("テスト Issue")).toBeTruthy();
123
- });
124
-
125
- fireEvent.click(screen.getByText("編集"));
126
-
127
- const titleInput = screen.getByDisplayValue("テスト Issue");
128
- fireEvent.change(titleInput, { target: { value: "更新後タイトル" } });
129
-
130
- await act(async () => {
131
- fireEvent.click(screen.getByText("保存"));
132
- });
133
-
134
- await waitFor(() => {
135
- expect(screen.queryByText("保存")).toBeNull();
136
- });
137
-
138
- expect(screen.getByText("更新後タイトル")).toBeTruthy();
139
- });
140
- });
141
-
142
- // #708 固有シナリオ
143
- describe("IssueDetailView — #708 「新規 Issue」ボタンのリセット挙動", () => {
144
- beforeEach(() => {
145
- vi.clearAllMocks();
146
- mockGetIssue.mockResolvedValue(MOCK_ISSUE);
147
- });
148
-
149
- it("既存 Issue 編集・保存後に 'new' でリマウントすると空のフォームが表示される", async () => {
150
- const updatedIssue = { ...MOCK_ISSUE, title: "更新後" };
151
- mockUpdateIssue.mockResolvedValue(updatedIssue);
152
-
153
- const { unmount } = render(
154
- <IssueDetailView issueNumber={123} onCreated={vi.fn()} onClose={vi.fn()} />,
155
- );
156
-
157
- await waitFor(() => {
158
- expect(screen.getByText("テスト Issue")).toBeTruthy();
159
- });
160
-
161
- fireEvent.click(screen.getByText("編集"));
162
- const titleInput = screen.getByDisplayValue("テスト Issue");
163
- fireEvent.change(titleInput, { target: { value: "更新後" } });
164
- await act(async () => {
165
- fireEvent.click(screen.getByText("保存"));
166
- });
167
- await waitFor(() => {
168
- expect(screen.queryByText("保存")).toBeNull();
169
- });
170
-
171
- // 親コンポーネントが key を変えることでリマウント(「新規 Issue」ボタン相当)
172
- unmount();
173
- render(
174
- <IssueDetailView issueNumber="new" onCreated={vi.fn()} onClose={vi.fn()} />,
175
- );
176
-
177
- expect(screen.getByText("新規 Issue 作成")).toBeTruthy();
178
- const newTitleInput = screen.getByPlaceholderText("Issue のタイトル") as HTMLInputElement;
179
- expect(newTitleInput.value).toBe("");
180
- });
181
-
182
- it("新規 Issue フォーム入力途中にリマウントすると空のフォームにリセットされる", () => {
183
- const { unmount } = render(
184
- <IssueDetailView issueNumber="new" onCreated={vi.fn()} onClose={vi.fn()} />,
185
- );
186
-
187
- const titleInput = screen.getByPlaceholderText("Issue のタイトル") as HTMLInputElement;
188
- fireEvent.change(titleInput, { target: { value: "途中入力" } });
189
- expect(titleInput.value).toBe("途中入力");
190
-
191
- // 「新規 Issue」ボタン再クリック相当(newIssueCounter インクリメント → リマウント)
192
- unmount();
193
- render(
194
- <IssueDetailView issueNumber="new" onCreated={vi.fn()} onClose={vi.fn()} />,
195
- );
196
-
197
- const freshInput = screen.getByPlaceholderText("Issue のタイトル") as HTMLInputElement;
198
- expect(freshInput.value).toBe("");
199
- expect(screen.getByText("新規 Issue 作成")).toBeTruthy();
200
- });
201
- });
@@ -1,318 +0,0 @@
1
- import { useState, useEffect, useCallback, lazy, Suspense } from "react";
2
- import {
3
- getIssueApi,
4
- createIssueApi,
5
- updateIssueApi,
6
- getErrorMessage,
7
- isAbortError,
8
- } from "@/lib/api";
9
- import type { IssueDetail } from "@/lib/api";
10
- import { useAuth } from "@/contexts/AuthContext";
11
- import { useToast } from "@/contexts/ToastContext";
12
- import {
13
- CircleDot,
14
- CircleCheck,
15
- ExternalLink,
16
- Save,
17
- X,
18
- Pencil,
19
- Plus,
20
- } from "lucide-react";
21
- import { ContentSkeleton, LoadingCircle } from "@/components/Skeleton";
22
- import LabelSelector from "@/components/LabelSelector";
23
-
24
- const FileContentView = lazy(() => import("@/components/FileContentView"));
25
-
26
- type IssueDetailViewProps = {
27
- issueNumber: number | "new";
28
- onCreated?: (number: number) => void;
29
- onClose?: () => void;
30
- };
31
-
32
- export default function IssueDetailView({
33
- issueNumber,
34
- onCreated,
35
- onClose,
36
- }: IssueDetailViewProps) {
37
- const { canEdit } = useAuth();
38
- const { showToast } = useToast();
39
-
40
- const isNew = issueNumber === "new";
41
- const [issue, setIssue] = useState<IssueDetail | null>(null);
42
- const [loading, setLoading] = useState(!isNew);
43
- const [editing, setEditing] = useState(isNew);
44
- const [saving, setSaving] = useState(false);
45
-
46
- const [title, setTitle] = useState("");
47
- const [body, setBody] = useState("");
48
- const [selectedLabels, setSelectedLabels] = useState<string[]>([]);
49
-
50
- const loadIssue = useCallback(() => {
51
- if (isNew) return;
52
- setIssue(null);
53
- setLoading(true);
54
- getIssueApi(issueNumber as number)
55
- .then((data) => {
56
- setIssue(data);
57
- setTitle(data.title);
58
- setBody(data.body);
59
- setSelectedLabels(data.labels.map((l) => l.name));
60
- })
61
- .catch((e) => {
62
- if (isAbortError(e)) return;
63
- setIssue(null);
64
- showToast(`Issue の読み込みに失敗しました: ${getErrorMessage(e)}`, "error");
65
- })
66
- .finally(() => setLoading(false));
67
- }, [issueNumber, isNew, showToast]);
68
-
69
- useEffect(() => {
70
- loadIssue();
71
- }, [loadIssue]);
72
-
73
- const handleSave = useCallback(async () => {
74
- if (!title.trim()) {
75
- showToast("タイトルを入力してください", "error");
76
- return;
77
- }
78
- setSaving(true);
79
- try {
80
- if (isNew) {
81
- const created = await createIssueApi({
82
- title: title.trim(),
83
- body,
84
- labels: selectedLabels.length > 0 ? selectedLabels : undefined,
85
- });
86
- showToast(`Issue #${created.number} を作成しました`, "success");
87
- setIssue(created);
88
- setEditing(false);
89
- onCreated?.(created.number);
90
- } else {
91
- const updated = await updateIssueApi(issueNumber as number, {
92
- title: title.trim(),
93
- body,
94
- labels: selectedLabels,
95
- });
96
- setIssue(updated);
97
- setEditing(false);
98
- showToast("Issue を更新しました", "success");
99
- }
100
- } catch (e) {
101
- showToast(`保存に失敗しました: ${getErrorMessage(e)}`, "error");
102
- } finally {
103
- setSaving(false);
104
- }
105
- }, [title, body, selectedLabels, isNew, issueNumber, showToast, onCreated]);
106
-
107
- const handleToggleState = useCallback(async () => {
108
- if (!issue) return;
109
- const newState = issue.state === "open" ? "closed" : "open";
110
- setSaving(true);
111
- try {
112
- const updated = await updateIssueApi(issue.number, { state: newState });
113
- setIssue(updated);
114
- showToast(
115
- newState === "closed" ? "Issue をクローズしました" : "Issue を再オープンしました",
116
- "success",
117
- );
118
- } catch (e) {
119
- showToast(`状態変更に失敗しました: ${getErrorMessage(e)}`, "error");
120
- } finally {
121
- setSaving(false);
122
- }
123
- }, [issue, showToast]);
124
-
125
- if (loading) {
126
- return <LoadingCircle message="Issue を読み込み中…" />;
127
- }
128
-
129
- if (editing) {
130
- return (
131
- <div className="p-4 sm:p-6 max-w-3xl mx-auto">
132
- <h2 className="text-lg font-bold text-gray-200 mb-4 flex items-center gap-2">
133
- {isNew ? (
134
- <>
135
- <Plus className="w-5 h-5 text-green-400" />
136
- 新規 Issue 作成
137
- </>
138
- ) : (
139
- <>
140
- <Pencil className="w-5 h-5 text-blue-400" />
141
- Issue #{issueNumber} 編集
142
- </>
143
- )}
144
- </h2>
145
-
146
- <div className="space-y-4">
147
- <div>
148
- <label className="block text-xs text-gray-400 mb-1">タイトル</label>
149
- <input
150
- type="text"
151
- value={title}
152
- onChange={(e) => setTitle(e.target.value)}
153
- placeholder="Issue のタイトル"
154
- className="w-full px-3 py-2 rounded bg-gray-900 border border-gray-600 text-gray-200 placeholder-gray-500 outline-none focus:border-blue-500 text-sm"
155
- autoFocus
156
- />
157
- </div>
158
-
159
- <LabelSelector
160
- selected={selectedLabels}
161
- onChange={setSelectedLabels}
162
- disabled={saving}
163
- />
164
-
165
- <div>
166
- <label className="block text-xs text-gray-400 mb-1">本文(Markdown)</label>
167
- <textarea
168
- value={body}
169
- onChange={(e) => setBody(e.target.value)}
170
- placeholder="Issue の詳細を Markdown で記述…"
171
- rows={12}
172
- className="w-full px-3 py-2 rounded bg-gray-900 border border-gray-600 text-gray-200 placeholder-gray-500 outline-none focus:border-blue-500 text-sm font-mono resize-y"
173
- />
174
- </div>
175
-
176
- <div className="flex items-center gap-2">
177
- <button
178
- type="button"
179
- onClick={handleSave}
180
- disabled={saving || !title.trim()}
181
- className="px-4 py-2 rounded text-sm bg-blue-600 hover:bg-blue-700 text-white transition-colors disabled:opacity-50 flex items-center gap-1.5"
182
- >
183
- <Save className="w-4 h-4" />
184
- {saving ? "保存中…" : isNew ? "作成" : "保存"}
185
- </button>
186
- <button
187
- type="button"
188
- onClick={() => {
189
- if (isNew) {
190
- onClose?.();
191
- } else {
192
- setTitle(issue?.title ?? "");
193
- setBody(issue?.body ?? "");
194
- setSelectedLabels(issue?.labels.map((l) => l.name) ?? []);
195
- setEditing(false);
196
- }
197
- }}
198
- className="px-4 py-2 rounded text-sm text-gray-400 hover:text-gray-200 hover:bg-gray-700 transition-colors flex items-center gap-1.5"
199
- >
200
- <X className="w-4 h-4" />
201
- キャンセル
202
- </button>
203
- </div>
204
- </div>
205
- </div>
206
- );
207
- }
208
-
209
- if (!issue) {
210
- return (
211
- <div className="flex items-center justify-center h-full p-6">
212
- <p className="text-gray-500">Issue が見つかりませんでした</p>
213
- </div>
214
- );
215
- }
216
-
217
- return (
218
- <div className="p-4 sm:p-6 max-w-3xl mx-auto">
219
- {/* Header */}
220
- <div className="mb-4">
221
- <h1 className="text-xl font-bold text-gray-100 flex items-start gap-2">
222
- {issue.state === "open" ? (
223
- <CircleDot className="w-5 h-5 text-green-400 mt-1 shrink-0" />
224
- ) : (
225
- <CircleCheck className="w-5 h-5 text-purple-400 mt-1 shrink-0" />
226
- )}
227
- <span>{issue.title}</span>
228
- <span className="text-gray-500 font-normal">#{issue.number}</span>
229
- </h1>
230
-
231
- <div className="flex items-center gap-3 mt-2 text-xs text-gray-400">
232
- <span>{issue.author}</span>
233
- <span>
234
- {new Date(issue.createdAt).toLocaleDateString("ja-JP")}
235
- </span>
236
- {issue.url && (
237
- <a
238
- href={issue.url}
239
- target="_blank"
240
- rel="noopener noreferrer"
241
- className="flex items-center gap-1 text-blue-400 hover:text-blue-300"
242
- >
243
- <ExternalLink className="w-3 h-3" />
244
- GitHub
245
- </a>
246
- )}
247
- </div>
248
-
249
- {issue.labels.length > 0 && (
250
- <div className="flex flex-wrap gap-1 mt-2">
251
- {issue.labels.map((l) => (
252
- <span
253
- key={l.name}
254
- className="px-2 py-0.5 rounded-full text-xs"
255
- style={{
256
- backgroundColor: `#${l.color}22`,
257
- color: `#${l.color}`,
258
- border: `1px solid #${l.color}44`,
259
- }}
260
- >
261
- {l.name}
262
- </span>
263
- ))}
264
- </div>
265
- )}
266
-
267
- {/* Action buttons */}
268
- {canEdit && (
269
- <div className="flex items-center gap-2 mt-3">
270
- <button
271
- type="button"
272
- onClick={() => setEditing(true)}
273
- className="px-3 py-1 rounded text-sm bg-gray-700 hover:bg-gray-600 text-gray-200 transition-colors flex items-center gap-1.5"
274
- >
275
- <Pencil className="w-3.5 h-3.5" />
276
- 編集
277
- </button>
278
- <button
279
- type="button"
280
- onClick={handleToggleState}
281
- disabled={saving}
282
- className={`px-3 py-1 rounded text-sm transition-colors flex items-center gap-1.5 ${
283
- issue.state === "open"
284
- ? "bg-purple-700/30 text-purple-300 hover:bg-purple-700/50"
285
- : "bg-green-700/30 text-green-300 hover:bg-green-700/50"
286
- }`}
287
- >
288
- {issue.state === "open" ? (
289
- <>
290
- <CircleCheck className="w-3.5 h-3.5" />
291
- クローズ
292
- </>
293
- ) : (
294
- <>
295
- <CircleDot className="w-3.5 h-3.5" />
296
- 再オープン
297
- </>
298
- )}
299
- </button>
300
- </div>
301
- )}
302
- </div>
303
-
304
- {/* Body */}
305
- {issue.body ? (
306
- <div className="border-t border-gray-700 pt-4">
307
- <Suspense fallback={<ContentSkeleton />}>
308
- <FileContentView path="issue.md" content={issue.body} encoding="utf-8" />
309
- </Suspense>
310
- </div>
311
- ) : (
312
- <div className="border-t border-gray-700 pt-4">
313
- <p className="text-gray-500 text-sm italic">本文がありません</p>
314
- </div>
315
- )}
316
- </div>
317
- );
318
- }
@@ -1,93 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import { render, screen, waitFor } from "@testing-library/react";
3
-
4
- vi.mock("@/lib/api", () => ({
5
- listIssuesApi: vi.fn(),
6
- getErrorMessage: vi.fn((e: unknown) => String(e)),
7
- isAbortError: vi.fn(() => false),
8
- }));
9
-
10
- import { listIssuesApi } from "@/lib/api";
11
- import IssueList from "./IssueList";
12
-
13
- const mockListIssues = vi.mocked(listIssuesApi);
14
-
15
- const MOCK_ISSUES = [
16
- {
17
- number: 10,
18
- title: "Issue 10",
19
- state: "open" as const,
20
- author: "user1",
21
- assignees: [],
22
- comments: 0,
23
- labels: [],
24
- createdAt: "2026-05-19T00:00:00Z",
25
- updatedAt: "2026-05-19T00:00:00Z",
26
- },
27
- {
28
- number: 9,
29
- title: "Issue 9",
30
- state: "open" as const,
31
- author: "user1",
32
- assignees: [],
33
- comments: 0,
34
- labels: [],
35
- createdAt: "2026-05-18T00:00:00Z",
36
- updatedAt: "2026-05-18T00:00:00Z",
37
- },
38
- ];
39
-
40
- describe("IssueList", () => {
41
- beforeEach(() => {
42
- vi.clearAllMocks();
43
- });
44
-
45
- it("Issueリストを取得して表示する", async () => {
46
- mockListIssues.mockResolvedValue({ issues: MOCK_ISSUES });
47
-
48
- render(<IssueList onSelect={vi.fn()} selectedIssue={null} />);
49
-
50
- await waitFor(() => {
51
- expect(screen.getByText("Issue 10")).toBeTruthy();
52
- expect(screen.getByText("Issue 9")).toBeTruthy();
53
- });
54
- });
55
-
56
- it("reloadTrigger が変わると listIssuesApi を再呼び出しする", async () => {
57
- mockListIssues.mockResolvedValue({ issues: MOCK_ISSUES });
58
-
59
- const { rerender } = render(
60
- <IssueList onSelect={vi.fn()} selectedIssue={null} reloadTrigger={0} />,
61
- );
62
-
63
- await waitFor(() => {
64
- expect(mockListIssues).toHaveBeenCalledTimes(1);
65
- });
66
-
67
- rerender(
68
- <IssueList onSelect={vi.fn()} selectedIssue={null} reloadTrigger={1} />,
69
- );
70
-
71
- await waitFor(() => {
72
- expect(mockListIssues).toHaveBeenCalledTimes(2);
73
- });
74
- });
75
-
76
- it("reloadTrigger が同じ値のままなら再呼び出しされない", async () => {
77
- mockListIssues.mockResolvedValue({ issues: MOCK_ISSUES });
78
-
79
- const { rerender } = render(
80
- <IssueList onSelect={vi.fn()} selectedIssue={null} reloadTrigger={0} />,
81
- );
82
-
83
- await waitFor(() => {
84
- expect(mockListIssues).toHaveBeenCalledTimes(1);
85
- });
86
-
87
- rerender(
88
- <IssueList onSelect={vi.fn()} selectedIssue={null} reloadTrigger={0} />,
89
- );
90
-
91
- expect(mockListIssues).toHaveBeenCalledTimes(1);
92
- });
93
- });