anima-core 0.3.0 → 1.0.0

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 (269) hide show
  1. checksums.yaml +4 -4
  2. data/.reek.yml +27 -1
  3. data/CHANGELOG.md +4 -0
  4. data/README.md +219 -25
  5. data/agents/codebase-analyzer.md +88 -0
  6. data/agents/codebase-pattern-finder.md +83 -0
  7. data/agents/documentation-researcher.md +59 -0
  8. data/agents/thoughts-analyzer.md +102 -0
  9. data/agents/web-search-researcher.md +71 -0
  10. data/anima-core.gemspec +3 -0
  11. data/app/channels/session_channel.rb +76 -28
  12. data/app/jobs/agent_request_job.rb +24 -0
  13. data/app/jobs/analytical_brain_job.rb +33 -0
  14. data/app/jobs/count_event_tokens_job.rb +1 -1
  15. data/app/models/concerns/event/broadcasting.rb +20 -2
  16. data/app/models/event.rb +1 -1
  17. data/app/models/goal.rb +91 -0
  18. data/app/models/session.rb +347 -22
  19. data/config/application.rb +2 -0
  20. data/db/migrate/20260314075248_add_subagent_support_to_sessions.rb +6 -0
  21. data/db/migrate/20260314112417_add_granted_tools_to_sessions.rb +5 -0
  22. data/db/migrate/20260314140000_add_name_to_sessions.rb +7 -0
  23. data/db/migrate/20260314150000_add_viewport_event_ids_to_sessions.rb +7 -0
  24. data/db/migrate/20260315100000_add_active_skills_to_sessions.rb +7 -0
  25. data/db/migrate/20260315140843_create_goals.rb +16 -0
  26. data/db/migrate/20260315144837_add_completed_at_to_goals.rb +5 -0
  27. data/db/migrate/20260315191105_add_active_workflow_to_sessions.rb +5 -0
  28. data/lib/agent_loop.rb +65 -9
  29. data/lib/agents/definition.rb +116 -0
  30. data/lib/agents/registry.rb +106 -0
  31. data/lib/analytical_brain/runner.rb +276 -0
  32. data/lib/analytical_brain/tools/activate_skill.rb +52 -0
  33. data/lib/analytical_brain/tools/deactivate_skill.rb +43 -0
  34. data/lib/analytical_brain/tools/deactivate_workflow.rb +34 -0
  35. data/lib/analytical_brain/tools/everything_is_ready.rb +28 -0
  36. data/lib/analytical_brain/tools/finish_goal.rb +62 -0
  37. data/lib/analytical_brain/tools/read_workflow.rb +58 -0
  38. data/lib/analytical_brain/tools/rename_session.rb +63 -0
  39. data/lib/analytical_brain/tools/set_goal.rb +60 -0
  40. data/lib/analytical_brain/tools/update_goal.rb +60 -0
  41. data/lib/analytical_brain.rb +23 -0
  42. data/lib/anima/cli/mcp/secrets.rb +76 -0
  43. data/lib/anima/cli/mcp.rb +197 -0
  44. data/lib/anima/cli.rb +4 -0
  45. data/lib/anima/installer.rb +168 -0
  46. data/lib/anima/settings.rb +226 -0
  47. data/lib/anima/version.rb +1 -1
  48. data/lib/anima.rb +9 -0
  49. data/lib/credential_store.rb +103 -0
  50. data/lib/environment_probe.rb +232 -0
  51. data/lib/llm/client.rb +29 -10
  52. data/lib/mcp/client_manager.rb +86 -0
  53. data/lib/mcp/config.rb +213 -0
  54. data/lib/mcp/health_check.rb +77 -0
  55. data/lib/mcp/secrets.rb +73 -0
  56. data/lib/mcp/stdio_transport.rb +206 -0
  57. data/lib/providers/anthropic.rb +8 -7
  58. data/lib/shell_session.rb +11 -10
  59. data/lib/skills/definition.rb +97 -0
  60. data/lib/skills/registry.rb +105 -0
  61. data/lib/tools/edit.rb +3 -4
  62. data/lib/tools/mcp_tool.rb +114 -0
  63. data/lib/tools/read.rb +15 -16
  64. data/lib/tools/registry.rb +14 -12
  65. data/lib/tools/request_feature.rb +121 -0
  66. data/lib/tools/return_result.rb +81 -0
  67. data/lib/tools/spawn_specialist.rb +109 -0
  68. data/lib/tools/spawn_subagent.rb +111 -0
  69. data/lib/tools/subagent_prompts.rb +12 -0
  70. data/lib/tools/web_get.rb +8 -9
  71. data/lib/tui/app.rb +332 -43
  72. data/lib/tui/message_store.rb +20 -0
  73. data/lib/tui/screens/chat.rb +207 -20
  74. data/lib/workflows/definition.rb +97 -0
  75. data/lib/workflows/registry.rb +89 -0
  76. data/skills/activerecord/SKILL.md +255 -0
  77. data/skills/activerecord/examples/associations/association_extensions.rb +298 -0
  78. data/skills/activerecord/examples/associations/basic_associations.rb +118 -0
  79. data/skills/activerecord/examples/associations/counter_caches.rb +215 -0
  80. data/skills/activerecord/examples/associations/polymorphic_associations.rb +217 -0
  81. data/skills/activerecord/examples/associations/self_referential.rb +302 -0
  82. data/skills/activerecord/examples/associations/through_associations.rb +203 -0
  83. data/skills/activerecord/examples/basics/crud_operations.rb +209 -0
  84. data/skills/activerecord/examples/basics/dirty_tracking.rb +218 -0
  85. data/skills/activerecord/examples/basics/inheritance.rb +377 -0
  86. data/skills/activerecord/examples/basics/type_casting.rb +317 -0
  87. data/skills/activerecord/examples/callbacks/alternatives_to_callbacks.rb +447 -0
  88. data/skills/activerecord/examples/callbacks/conditional_callbacks.rb +353 -0
  89. data/skills/activerecord/examples/callbacks/lifecycle_callbacks.rb +280 -0
  90. data/skills/activerecord/examples/callbacks/transaction_callbacks.rb +340 -0
  91. data/skills/activerecord/examples/migrations/indexes_and_constraints.rb +337 -0
  92. data/skills/activerecord/examples/migrations/reversible_patterns.rb +403 -0
  93. data/skills/activerecord/examples/migrations/safe_patterns.rb +420 -0
  94. data/skills/activerecord/examples/migrations/schema_changes.rb +277 -0
  95. data/skills/activerecord/examples/querying/batch_processing.rb +226 -0
  96. data/skills/activerecord/examples/querying/eager_loading.rb +259 -0
  97. data/skills/activerecord/examples/querying/finder_methods.rb +170 -0
  98. data/skills/activerecord/examples/querying/optimization.rb +275 -0
  99. data/skills/activerecord/examples/querying/scopes.rb +260 -0
  100. data/skills/activerecord/examples/validations/built_in_validators.rb +277 -0
  101. data/skills/activerecord/examples/validations/conditional_validations.rb +288 -0
  102. data/skills/activerecord/examples/validations/custom_validators.rb +381 -0
  103. data/skills/activerecord/examples/validations/database_constraints.rb +432 -0
  104. data/skills/activerecord/examples/validations/validation_contexts.rb +367 -0
  105. data/skills/activerecord/references/associations.md +709 -0
  106. data/skills/activerecord/references/basics.md +622 -0
  107. data/skills/activerecord/references/callbacks.md +738 -0
  108. data/skills/activerecord/references/migrations.md +657 -0
  109. data/skills/activerecord/references/querying.md +655 -0
  110. data/skills/activerecord/references/validations.md +596 -0
  111. data/skills/dragonruby/SKILL.md +250 -0
  112. data/skills/dragonruby/examples/audio/audio_events.rb +55 -0
  113. data/skills/dragonruby/examples/audio/background_music.rb +29 -0
  114. data/skills/dragonruby/examples/audio/crossfade.rb +51 -0
  115. data/skills/dragonruby/examples/audio/music_controls.rb +51 -0
  116. data/skills/dragonruby/examples/audio/sound_effects.rb +30 -0
  117. data/skills/dragonruby/examples/core/coordinate_system.rb +27 -0
  118. data/skills/dragonruby/examples/core/hello_world.rb +24 -0
  119. data/skills/dragonruby/examples/core/labels.rb +22 -0
  120. data/skills/dragonruby/examples/core/sprites.rb +35 -0
  121. data/skills/dragonruby/examples/core/state_management.rb +29 -0
  122. data/skills/dragonruby/examples/distribution/background_pause.rb +42 -0
  123. data/skills/dragonruby/examples/distribution/build_workflow.sh +26 -0
  124. data/skills/dragonruby/examples/distribution/cvars_production.txt +16 -0
  125. data/skills/dragonruby/examples/distribution/game_metadata_hd.txt +23 -0
  126. data/skills/dragonruby/examples/distribution/game_metadata_minimal.txt +9 -0
  127. data/skills/dragonruby/examples/distribution/game_metadata_mobile.txt +31 -0
  128. data/skills/dragonruby/examples/distribution/platform_detection.rb +36 -0
  129. data/skills/dragonruby/examples/distribution/steam_metadata.txt +19 -0
  130. data/skills/dragonruby/examples/entities/collision_detection.rb +43 -0
  131. data/skills/dragonruby/examples/entities/entity_lifecycle.rb +68 -0
  132. data/skills/dragonruby/examples/entities/entity_storage.rb +38 -0
  133. data/skills/dragonruby/examples/entities/factory_methods.rb +45 -0
  134. data/skills/dragonruby/examples/entities/random_spawning.rb +50 -0
  135. data/skills/dragonruby/examples/game-logic/reset_patterns.rb +98 -0
  136. data/skills/dragonruby/examples/game-logic/save_load.rb +101 -0
  137. data/skills/dragonruby/examples/game-logic/scoring.rb +104 -0
  138. data/skills/dragonruby/examples/game-logic/state_transitions.rb +103 -0
  139. data/skills/dragonruby/examples/game-logic/timers.rb +87 -0
  140. data/skills/dragonruby/examples/input/action_triggers.rb +36 -0
  141. data/skills/dragonruby/examples/input/analog_movement.rb +28 -0
  142. data/skills/dragonruby/examples/input/controller_input.rb +28 -0
  143. data/skills/dragonruby/examples/input/directional_input.rb +24 -0
  144. data/skills/dragonruby/examples/input/keyboard_input.rb +28 -0
  145. data/skills/dragonruby/examples/input/mouse_click.rb +26 -0
  146. data/skills/dragonruby/examples/input/movement_with_bounds.rb +22 -0
  147. data/skills/dragonruby/examples/input/normalized_movement.rb +32 -0
  148. data/skills/dragonruby/examples/rendering/frame_animation.rb +32 -0
  149. data/skills/dragonruby/examples/rendering/labels.rb +32 -0
  150. data/skills/dragonruby/examples/rendering/layering.rb +51 -0
  151. data/skills/dragonruby/examples/rendering/solids.rb +61 -0
  152. data/skills/dragonruby/examples/rendering/sprites.rb +33 -0
  153. data/skills/dragonruby/examples/rendering/spritesheet_animation.rb +39 -0
  154. data/skills/dragonruby/examples/scenes/case_dispatch.rb +60 -0
  155. data/skills/dragonruby/examples/scenes/class_based.rb +150 -0
  156. data/skills/dragonruby/examples/scenes/pause_overlay.rb +100 -0
  157. data/skills/dragonruby/examples/scenes/safe_transitions.rb +68 -0
  158. data/skills/dragonruby/examples/scenes/scene_transitions.rb +98 -0
  159. data/skills/dragonruby/examples/scenes/send_dispatch.rb +88 -0
  160. data/skills/dragonruby/references/audio.md +396 -0
  161. data/skills/dragonruby/references/core.md +385 -0
  162. data/skills/dragonruby/references/distribution.md +434 -0
  163. data/skills/dragonruby/references/entities.md +516 -0
  164. data/skills/dragonruby/references/game-logic/persistence.md +386 -0
  165. data/skills/dragonruby/references/game-logic/state.md +389 -0
  166. data/skills/dragonruby/references/input.md +414 -0
  167. data/skills/dragonruby/references/rendering/animation.md +467 -0
  168. data/skills/dragonruby/references/rendering/primitives.md +403 -0
  169. data/skills/dragonruby/references/scenes.md +443 -0
  170. data/skills/draper-decorators/SKILL.md +344 -0
  171. data/skills/draper-decorators/examples/application_decorator.rb +61 -0
  172. data/skills/draper-decorators/examples/decorator_spec.rb +253 -0
  173. data/skills/draper-decorators/examples/model_decorator.rb +152 -0
  174. data/skills/draper-decorators/references/anti-patterns.md +640 -0
  175. data/skills/draper-decorators/references/patterns.md +507 -0
  176. data/skills/draper-decorators/references/testing.md +559 -0
  177. data/skills/gh-issue.md +182 -0
  178. data/skills/mcp-server/SKILL.md +177 -0
  179. data/skills/mcp-server/examples/dynamic_tools.rb +36 -0
  180. data/skills/mcp-server/examples/file_manager_tool.rb +85 -0
  181. data/skills/mcp-server/examples/http_client.rb +48 -0
  182. data/skills/mcp-server/examples/http_server.rb +97 -0
  183. data/skills/mcp-server/examples/rails_integration.rb +88 -0
  184. data/skills/mcp-server/examples/stdio_server.rb +108 -0
  185. data/skills/mcp-server/examples/streaming_client.rb +95 -0
  186. data/skills/mcp-server/references/gotchas.md +183 -0
  187. data/skills/mcp-server/references/prompts.md +98 -0
  188. data/skills/mcp-server/references/resources.md +53 -0
  189. data/skills/mcp-server/references/server.md +140 -0
  190. data/skills/mcp-server/references/tools.md +146 -0
  191. data/skills/mcp-server/references/transport.md +104 -0
  192. data/skills/ratatui-ruby/SKILL.md +315 -0
  193. data/skills/ratatui-ruby/references/core-concepts.md +340 -0
  194. data/skills/ratatui-ruby/references/events.md +387 -0
  195. data/skills/ratatui-ruby/references/frameworks.md +522 -0
  196. data/skills/ratatui-ruby/references/layout.md +423 -0
  197. data/skills/ratatui-ruby/references/styling.md +268 -0
  198. data/skills/ratatui-ruby/references/testing.md +433 -0
  199. data/skills/ratatui-ruby/references/widgets.md +532 -0
  200. data/skills/rspec/SKILL.md +340 -0
  201. data/skills/rspec/examples/core/basic_structure.rb +69 -0
  202. data/skills/rspec/examples/core/configuration.rb +126 -0
  203. data/skills/rspec/examples/core/hooks.rb +126 -0
  204. data/skills/rspec/examples/core/memoized_helpers.rb +139 -0
  205. data/skills/rspec/examples/core/metadata_filtering.rb +144 -0
  206. data/skills/rspec/examples/core/shared_examples.rb +145 -0
  207. data/skills/rspec/examples/factory_bot/associations.rb +314 -0
  208. data/skills/rspec/examples/factory_bot/build_strategies.rb +272 -0
  209. data/skills/rspec/examples/factory_bot/callbacks.rb +320 -0
  210. data/skills/rspec/examples/factory_bot/custom_construction.rb +328 -0
  211. data/skills/rspec/examples/factory_bot/factory_definition.rb +191 -0
  212. data/skills/rspec/examples/factory_bot/inheritance.rb +314 -0
  213. data/skills/rspec/examples/factory_bot/traits.rb +293 -0
  214. data/skills/rspec/examples/factory_bot/transients.rb +229 -0
  215. data/skills/rspec/examples/matchers/change.rb +115 -0
  216. data/skills/rspec/examples/matchers/collections.rb +154 -0
  217. data/skills/rspec/examples/matchers/comparisons.rb +79 -0
  218. data/skills/rspec/examples/matchers/composing.rb +155 -0
  219. data/skills/rspec/examples/matchers/custom_matchers.rb +197 -0
  220. data/skills/rspec/examples/matchers/equality.rb +58 -0
  221. data/skills/rspec/examples/matchers/errors.rb +136 -0
  222. data/skills/rspec/examples/matchers/output.rb +103 -0
  223. data/skills/rspec/examples/matchers/predicates.rb +87 -0
  224. data/skills/rspec/examples/matchers/truthiness.rb +101 -0
  225. data/skills/rspec/examples/matchers/types.rb +82 -0
  226. data/skills/rspec/examples/matchers/yield.rb +147 -0
  227. data/skills/rspec/examples/mocks/any_instance.rb +172 -0
  228. data/skills/rspec/examples/mocks/argument_matchers.rb +206 -0
  229. data/skills/rspec/examples/mocks/constants.rb +177 -0
  230. data/skills/rspec/examples/mocks/doubles.rb +139 -0
  231. data/skills/rspec/examples/mocks/expectations.rb +137 -0
  232. data/skills/rspec/examples/mocks/message_chains.rb +173 -0
  233. data/skills/rspec/examples/mocks/ordering.rb +144 -0
  234. data/skills/rspec/examples/mocks/receive_counts.rb +181 -0
  235. data/skills/rspec/examples/mocks/responses.rb +223 -0
  236. data/skills/rspec/examples/mocks/spies.rb +149 -0
  237. data/skills/rspec/examples/mocks/stubbing.rb +133 -0
  238. data/skills/rspec/examples/rails/channels.rb +250 -0
  239. data/skills/rspec/examples/rails/controller_specs.rb +302 -0
  240. data/skills/rspec/examples/rails/helper_specs.rb +245 -0
  241. data/skills/rspec/examples/rails/job_specs.rb +256 -0
  242. data/skills/rspec/examples/rails/mailer_specs.rb +228 -0
  243. data/skills/rspec/examples/rails/matchers.rb +374 -0
  244. data/skills/rspec/examples/rails/model_specs.rb +193 -0
  245. data/skills/rspec/examples/rails/request_specs.rb +275 -0
  246. data/skills/rspec/examples/rails/routing_specs.rb +276 -0
  247. data/skills/rspec/examples/rails/system_specs.rb +294 -0
  248. data/skills/rspec/examples/rails/transactions.rb +254 -0
  249. data/skills/rspec/examples/rails/view_specs.rb +252 -0
  250. data/skills/rspec/references/core.md +816 -0
  251. data/skills/rspec/references/factory_bot.md +641 -0
  252. data/skills/rspec/references/matchers.md +516 -0
  253. data/skills/rspec/references/mocks.md +381 -0
  254. data/skills/rspec/references/rails.md +528 -0
  255. data/templates/soul.md +40 -0
  256. data/workflows/commit.md +45 -0
  257. data/workflows/create_handoff.md +98 -0
  258. data/workflows/create_note.md +82 -0
  259. data/workflows/create_plan.md +457 -0
  260. data/workflows/decompose_ticket.md +109 -0
  261. data/workflows/feature.md +91 -0
  262. data/workflows/implement_plan.md +87 -0
  263. data/workflows/iterate_plan.md +247 -0
  264. data/workflows/research_codebase.md +210 -0
  265. data/workflows/resume_handoff.md +217 -0
  266. data/workflows/review_pr.md +320 -0
  267. data/workflows/thoughts_init.md +71 -0
  268. data/workflows/validate_plan.md +166 -0
  269. metadata +284 -1
@@ -0,0 +1,276 @@
1
+ # RSpec Rails: Routing Specs Examples
2
+ # Source: rspec-rails gem features/routing_specs/
3
+
4
+ # Routing specs test Rails routes in isolation.
5
+ # Location: spec/routing/
6
+
7
+ # Basic route_to matching
8
+ RSpec.describe "Widget routes", type: :routing do
9
+ describe "GET /widgets" do
10
+ it "routes to widgets#index" do
11
+ expect(get: "/widgets").to route_to("widgets#index")
12
+ end
13
+ end
14
+
15
+ describe "GET /widgets/:id" do
16
+ it "routes to widgets#show" do
17
+ expect(get: "/widgets/1").to route_to(
18
+ controller: "widgets",
19
+ action: "show",
20
+ id: "1"
21
+ )
22
+ end
23
+ end
24
+
25
+ describe "POST /widgets" do
26
+ it "routes to widgets#create" do
27
+ expect(post: "/widgets").to route_to("widgets#create")
28
+ end
29
+ end
30
+
31
+ describe "PATCH /widgets/:id" do
32
+ it "routes to widgets#update" do
33
+ expect(patch: "/widgets/1").to route_to(
34
+ controller: "widgets",
35
+ action: "update",
36
+ id: "1"
37
+ )
38
+ end
39
+ end
40
+
41
+ describe "DELETE /widgets/:id" do
42
+ it "routes to widgets#destroy" do
43
+ expect(delete: "/widgets/1").to route_to(
44
+ controller: "widgets",
45
+ action: "destroy",
46
+ id: "1"
47
+ )
48
+ end
49
+ end
50
+ end
51
+
52
+ # Testing nested routes
53
+ RSpec.describe "Comment routes", type: :routing do
54
+ describe "GET /posts/:post_id/comments" do
55
+ it "routes to comments#index" do
56
+ expect(get: "/posts/5/comments").to route_to(
57
+ controller: "comments",
58
+ action: "index",
59
+ post_id: "5"
60
+ )
61
+ end
62
+ end
63
+
64
+ describe "POST /posts/:post_id/comments" do
65
+ it "routes to comments#create" do
66
+ expect(post: "/posts/5/comments").to route_to(
67
+ controller: "comments",
68
+ action: "create",
69
+ post_id: "5"
70
+ )
71
+ end
72
+ end
73
+ end
74
+
75
+ # Testing namespaced routes
76
+ RSpec.describe "Admin routes", type: :routing do
77
+ describe "GET /admin/users" do
78
+ it "routes to admin/users#index" do
79
+ expect(get: "/admin/users").to route_to("admin/users#index")
80
+ end
81
+ end
82
+
83
+ describe "GET /admin/dashboard" do
84
+ it "routes to admin/dashboard#show" do
85
+ expect(get: "/admin/dashboard").to route_to("admin/dashboard#show")
86
+ end
87
+ end
88
+ end
89
+
90
+ # Testing API versioned routes
91
+ RSpec.describe "API v1 routes", type: :routing do
92
+ describe "GET /api/v1/widgets" do
93
+ it "routes to api/v1/widgets#index" do
94
+ expect(get: "/api/v1/widgets").to route_to(
95
+ controller: "api/v1/widgets",
96
+ action: "index"
97
+ )
98
+ end
99
+ end
100
+ end
101
+
102
+ # Testing custom member routes
103
+ RSpec.describe "Custom routes", type: :routing do
104
+ describe "POST /widgets/:id/publish" do
105
+ it "routes to widgets#publish" do
106
+ expect(post: "/widgets/1/publish").to route_to(
107
+ controller: "widgets",
108
+ action: "publish",
109
+ id: "1"
110
+ )
111
+ end
112
+ end
113
+
114
+ describe "POST /widgets/:id/archive" do
115
+ it "routes to widgets#archive" do
116
+ expect(post: "/widgets/1/archive").to route_to(
117
+ controller: "widgets",
118
+ action: "archive",
119
+ id: "1"
120
+ )
121
+ end
122
+ end
123
+ end
124
+
125
+ # Testing collection routes
126
+ RSpec.describe "Collection routes", type: :routing do
127
+ describe "GET /widgets/search" do
128
+ it "routes to widgets#search" do
129
+ expect(get: "/widgets/search").to route_to("widgets#search")
130
+ end
131
+ end
132
+
133
+ describe "POST /widgets/import" do
134
+ it "routes to widgets#import" do
135
+ expect(post: "/widgets/import").to route_to("widgets#import")
136
+ end
137
+ end
138
+ end
139
+
140
+ # be_routable matcher
141
+ RSpec.describe "Route existence", type: :routing do
142
+ describe "existing routes" do
143
+ it "is routable to GET /widgets" do
144
+ expect(get: "/widgets").to be_routable
145
+ end
146
+
147
+ it "is routable to POST /widgets" do
148
+ expect(post: "/widgets").to be_routable
149
+ end
150
+ end
151
+
152
+ describe "non-existing routes" do
153
+ it "is not routable to PUT /widgets" do
154
+ expect(put: "/widgets").not_to be_routable
155
+ end
156
+
157
+ it "is not routable to DELETE /admin" do
158
+ expect(delete: "/admin").not_to be_routable
159
+ end
160
+ end
161
+ end
162
+
163
+ # Testing routes with constraints
164
+ RSpec.describe "Constrained routes", type: :routing do
165
+ describe "routes with format constraint" do
166
+ it "routes /widgets.json to widgets#index" do
167
+ expect(get: "/widgets.json").to route_to(
168
+ controller: "widgets",
169
+ action: "index",
170
+ format: "json"
171
+ )
172
+ end
173
+ end
174
+
175
+ describe "routes with ID constraint" do
176
+ it "routes numeric ID" do
177
+ expect(get: "/widgets/123").to be_routable
178
+ end
179
+
180
+ # Depending on route constraints, this might not be routable
181
+ # it "does not route non-numeric ID" do
182
+ # expect(get: "/widgets/abc").not_to be_routable
183
+ # end
184
+ end
185
+ end
186
+
187
+ # Testing subdomain routes
188
+ RSpec.describe "Subdomain routes", type: :routing do
189
+ describe "api subdomain" do
190
+ before { host! "api.example.com" }
191
+
192
+ it "routes to API controller" do
193
+ expect(get: "/widgets").to route_to("api/widgets#index")
194
+ end
195
+ end
196
+
197
+ describe "admin subdomain" do
198
+ before { host! "admin.example.com" }
199
+
200
+ it "routes to admin controller" do
201
+ expect(get: "/dashboard").to route_to("admin/dashboard#show")
202
+ end
203
+ end
204
+ end
205
+
206
+ # Testing route helpers
207
+ RSpec.describe "Route helpers", type: :routing do
208
+ it "generates widget_path" do
209
+ expect(widget_path(1)).to eq("/widgets/1")
210
+ end
211
+
212
+ it "generates widgets_path" do
213
+ expect(widgets_path).to eq("/widgets")
214
+ end
215
+
216
+ it "generates new_widget_path" do
217
+ expect(new_widget_path).to eq("/widgets/new")
218
+ end
219
+
220
+ it "generates edit_widget_path" do
221
+ expect(edit_widget_path(1)).to eq("/widgets/1/edit")
222
+ end
223
+ end
224
+
225
+ # Testing shallow nested routes
226
+ RSpec.describe "Shallow routes", type: :routing do
227
+ describe "nested under parent" do
228
+ it "routes comments#create under posts" do
229
+ expect(post: "/posts/1/comments").to route_to(
230
+ controller: "comments",
231
+ action: "create",
232
+ post_id: "1"
233
+ )
234
+ end
235
+ end
236
+
237
+ describe "shallow (non-nested)" do
238
+ it "routes comments#show without parent" do
239
+ expect(get: "/comments/5").to route_to(
240
+ controller: "comments",
241
+ action: "show",
242
+ id: "5"
243
+ )
244
+ end
245
+ end
246
+ end
247
+
248
+ # Testing root route
249
+ RSpec.describe "Root route", type: :routing do
250
+ it "routes / to pages#home" do
251
+ expect(get: "/").to route_to("pages#home")
252
+ end
253
+ end
254
+
255
+ # Testing optional format
256
+ RSpec.describe "Format routing", type: :routing do
257
+ describe "with format" do
258
+ it "routes with JSON format" do
259
+ expect(get: "/widgets/1.json").to route_to(
260
+ controller: "widgets",
261
+ action: "show",
262
+ id: "1",
263
+ format: "json"
264
+ )
265
+ end
266
+
267
+ it "routes with XML format" do
268
+ expect(get: "/widgets/1.xml").to route_to(
269
+ controller: "widgets",
270
+ action: "show",
271
+ id: "1",
272
+ format: "xml"
273
+ )
274
+ end
275
+ end
276
+ end
@@ -0,0 +1,294 @@
1
+ # RSpec Rails: System Specs Examples
2
+ # Source: rspec-rails gem features/system_specs/
3
+
4
+ # System specs are browser-based tests using Capybara.
5
+ # They run within database transactions by default.
6
+ # Location: spec/system/
7
+
8
+ # Basic system spec with rack_test driver
9
+ RSpec.describe "Widget management", type: :system do
10
+ before { driven_by(:rack_test) }
11
+
12
+ describe "creating a widget" do
13
+ it "creates a new widget" do
14
+ visit "/widgets/new"
15
+
16
+ fill_in "Name", with: "My Widget"
17
+ fill_in "Description", with: "A useful widget"
18
+ click_button "Create Widget"
19
+
20
+ expect(page).to have_text("Widget was successfully created")
21
+ expect(page).to have_text("My Widget")
22
+ end
23
+ end
24
+ end
25
+
26
+ # Using headless Chrome for JavaScript testing
27
+ RSpec.describe "JavaScript features", type: :system do
28
+ before { driven_by(:selenium_chrome_headless) }
29
+
30
+ describe "dynamic form" do
31
+ it "shows validation errors without page reload" do
32
+ visit "/widgets/new"
33
+ click_button "Create Widget"
34
+
35
+ expect(page).to have_text("Name can't be blank")
36
+ end
37
+ end
38
+
39
+ describe "modal dialog" do
40
+ let!(:widget) { create(:widget) }
41
+
42
+ it "confirms deletion in modal" do
43
+ visit "/widgets/#{widget.id}"
44
+
45
+ click_button "Delete"
46
+ within(".modal") do
47
+ click_button "Confirm"
48
+ end
49
+
50
+ expect(page).to have_text("Widget was deleted")
51
+ end
52
+ end
53
+ end
54
+
55
+ # Testing user flows
56
+ RSpec.describe "User registration flow", type: :system do
57
+ before { driven_by(:rack_test) }
58
+
59
+ it "allows new user to sign up" do
60
+ visit "/"
61
+
62
+ click_link "Sign Up"
63
+ fill_in "Email", with: "new@example.com"
64
+ fill_in "Password", with: "password123"
65
+ fill_in "Password confirmation", with: "password123"
66
+ click_button "Create Account"
67
+
68
+ expect(page).to have_text("Welcome! You have signed up successfully")
69
+ expect(page).to have_link("Sign Out")
70
+ end
71
+ end
72
+
73
+ # Testing authentication
74
+ RSpec.describe "User sessions", type: :system do
75
+ before { driven_by(:rack_test) }
76
+
77
+ let(:user) { create(:user, email: "test@example.com", password: "password") }
78
+
79
+ describe "logging in" do
80
+ it "logs in with valid credentials" do
81
+ visit "/login"
82
+
83
+ fill_in "Email", with: "test@example.com"
84
+ fill_in "Password", with: "password"
85
+ click_button "Log In"
86
+
87
+ expect(page).to have_text("Logged in successfully")
88
+ expect(page).to have_link("Sign Out")
89
+ end
90
+
91
+ it "shows error with invalid credentials" do
92
+ visit "/login"
93
+
94
+ fill_in "Email", with: "test@example.com"
95
+ fill_in "Password", with: "wrong"
96
+ click_button "Log In"
97
+
98
+ expect(page).to have_text("Invalid email or password")
99
+ end
100
+ end
101
+
102
+ describe "logging out" do
103
+ before do
104
+ visit "/login"
105
+ fill_in "Email", with: "test@example.com"
106
+ fill_in "Password", with: "password"
107
+ click_button "Log In"
108
+ end
109
+
110
+ it "logs out the user" do
111
+ click_link "Sign Out"
112
+ expect(page).to have_text("You have been logged out")
113
+ end
114
+ end
115
+ end
116
+
117
+ # Testing CRUD operations
118
+ RSpec.describe "Widget CRUD", type: :system do
119
+ before { driven_by(:rack_test) }
120
+
121
+ let(:user) { create(:user) }
122
+
123
+ before do
124
+ # Login helper
125
+ visit "/login"
126
+ fill_in "Email", with: user.email
127
+ fill_in "Password", with: "password"
128
+ click_button "Log In"
129
+ end
130
+
131
+ describe "listing widgets" do
132
+ let!(:widgets) { create_list(:widget, 3, user:) }
133
+
134
+ it "displays all user widgets" do
135
+ visit "/widgets"
136
+
137
+ widgets.each do |widget|
138
+ expect(page).to have_text(widget.name)
139
+ end
140
+ end
141
+ end
142
+
143
+ describe "creating a widget" do
144
+ it "creates with valid data" do
145
+ visit "/widgets/new"
146
+
147
+ fill_in "Name", with: "New Widget"
148
+ click_button "Create Widget"
149
+
150
+ expect(page).to have_text("Widget was successfully created")
151
+ end
152
+ end
153
+
154
+ describe "editing a widget" do
155
+ let!(:widget) { create(:widget, name: "Old Name", user:) }
156
+
157
+ it "updates the widget" do
158
+ visit "/widgets/#{widget.id}/edit"
159
+
160
+ fill_in "Name", with: "Updated Name"
161
+ click_button "Update Widget"
162
+
163
+ expect(page).to have_text("Widget was successfully updated")
164
+ expect(page).to have_text("Updated Name")
165
+ end
166
+ end
167
+
168
+ describe "deleting a widget" do
169
+ let!(:widget) { create(:widget, user:) }
170
+
171
+ it "removes the widget" do
172
+ visit "/widgets/#{widget.id}"
173
+
174
+ click_button "Delete"
175
+
176
+ expect(page).to have_text("Widget was deleted")
177
+ expect(page).not_to have_text(widget.name)
178
+ end
179
+ end
180
+ end
181
+
182
+ # Testing navigation
183
+ RSpec.describe "Navigation", type: :system do
184
+ before { driven_by(:rack_test) }
185
+
186
+ it "navigates between pages" do
187
+ visit "/"
188
+
189
+ click_link "Widgets"
190
+ expect(page).to have_current_path("/widgets")
191
+
192
+ click_link "About"
193
+ expect(page).to have_current_path("/about")
194
+ end
195
+ end
196
+
197
+ # Testing flash messages
198
+ RSpec.describe "Flash messages", type: :system do
199
+ before { driven_by(:rack_test) }
200
+
201
+ it "shows success message" do
202
+ visit "/widgets/new"
203
+ fill_in "Name", with: "Test"
204
+ click_button "Create Widget"
205
+
206
+ expect(page).to have_css(".flash.notice", text: "successfully created")
207
+ end
208
+
209
+ it "shows error message" do
210
+ visit "/widgets/new"
211
+ click_button "Create Widget"
212
+
213
+ expect(page).to have_css(".flash.alert")
214
+ end
215
+ end
216
+
217
+ # Testing with JavaScript (Turbo)
218
+ RSpec.describe "Turbo interactions", type: :system do
219
+ before { driven_by(:selenium_chrome_headless) }
220
+
221
+ let!(:widget) { create(:widget) }
222
+
223
+ describe "inline editing" do
224
+ it "updates without full page reload" do
225
+ visit "/widgets/#{widget.id}"
226
+
227
+ click_link "Edit"
228
+ fill_in "Name", with: "Updated via Turbo"
229
+ click_button "Save"
230
+
231
+ expect(page).to have_text("Updated via Turbo")
232
+ # Page was not fully reloaded
233
+ end
234
+ end
235
+ end
236
+
237
+ # Testing file uploads
238
+ RSpec.describe "File uploads", type: :system do
239
+ before { driven_by(:rack_test) }
240
+
241
+ it "uploads an image" do
242
+ visit "/widgets/new"
243
+
244
+ fill_in "Name", with: "Widget with Image"
245
+ attach_file "Image", Rails.root.join("spec/fixtures/files/image.png")
246
+ click_button "Create Widget"
247
+
248
+ expect(page).to have_css("img.widget-image")
249
+ end
250
+ end
251
+
252
+ # Testing pagination
253
+ RSpec.describe "Pagination", type: :system do
254
+ before { driven_by(:rack_test) }
255
+
256
+ let!(:widgets) { create_list(:widget, 30) }
257
+
258
+ it "paginates results" do
259
+ visit "/widgets"
260
+
261
+ expect(page).to have_css(".widget", count: 10)
262
+ expect(page).to have_link("Next")
263
+
264
+ click_link "Next"
265
+
266
+ expect(page).to have_css(".widget", count: 10)
267
+ expect(page).to have_link("Previous")
268
+ end
269
+ end
270
+
271
+ # Testing search
272
+ RSpec.describe "Search", type: :system do
273
+ before { driven_by(:rack_test) }
274
+
275
+ let!(:matching_widget) { create(:widget, name: "Super Widget") }
276
+ let!(:other_widget) { create(:widget, name: "Regular Item") }
277
+
278
+ it "filters results by search term" do
279
+ visit "/widgets"
280
+
281
+ fill_in "Search", with: "Super"
282
+ click_button "Search"
283
+
284
+ expect(page).to have_text("Super Widget")
285
+ expect(page).not_to have_text("Regular Item")
286
+ end
287
+ end
288
+
289
+ # Configuration in rails_helper.rb
290
+ # RSpec.configure do |config|
291
+ # config.before(type: :system) do
292
+ # driven_by :selenium_chrome_headless
293
+ # end
294
+ # end