@girardmedia/bootspring 3.3.2 → 3.4.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 (171) hide show
  1. package/assets/agents/accessibility-auditor.md +39 -0
  2. package/assets/agents/api-designer.md +40 -0
  3. package/assets/agents/auth-implementer.md +64 -0
  4. package/assets/agents/bug-hunter.md +42 -0
  5. package/assets/agents/bundle-analyzer.md +40 -0
  6. package/assets/agents/cache-optimizer.md +55 -0
  7. package/assets/agents/changelog-writer.md +55 -0
  8. package/assets/agents/ci-cd-builder.md +40 -0
  9. package/assets/agents/code-explainer.md +39 -0
  10. package/assets/agents/code-reviewer.md +39 -0
  11. package/assets/agents/cost-optimizer.md +57 -0
  12. package/assets/agents/cron-scheduler.md +51 -0
  13. package/assets/agents/data-seeder.md +56 -0
  14. package/assets/agents/database-architect.md +40 -0
  15. package/assets/agents/dependency-updater.md +40 -0
  16. package/assets/agents/deploy-checker.md +40 -0
  17. package/assets/agents/docker-optimizer.md +40 -0
  18. package/assets/agents/documentation-writer.md +40 -0
  19. package/assets/agents/email-builder.md +55 -0
  20. package/assets/agents/env-setup.md +40 -0
  21. package/assets/agents/error-handler.md +40 -0
  22. package/assets/agents/eslint-fixer.md +46 -0
  23. package/assets/agents/feature-flagger.md +69 -0
  24. package/assets/agents/git-detective.md +39 -0
  25. package/assets/agents/graphql-builder.md +60 -0
  26. package/assets/agents/incident-responder.md +59 -0
  27. package/assets/agents/log-analyzer.md +39 -0
  28. package/assets/agents/migration-planner.md +41 -0
  29. package/assets/agents/monorepo-navigator.md +39 -0
  30. package/assets/agents/nextjs-expert.md +57 -0
  31. package/assets/agents/notification-builder.md +56 -0
  32. package/assets/agents/onboarding-guide.md +39 -0
  33. package/assets/agents/performance-profiler.md +40 -0
  34. package/assets/agents/prisma-expert.md +57 -0
  35. package/assets/agents/rate-limiter.md +58 -0
  36. package/assets/agents/react-expert.md +58 -0
  37. package/assets/agents/refactorer.md +42 -0
  38. package/assets/agents/regex-builder.md +46 -0
  39. package/assets/agents/release-manager.md +40 -0
  40. package/assets/agents/s3-manager.md +58 -0
  41. package/assets/agents/schema-validator.md +40 -0
  42. package/assets/agents/search-builder.md +62 -0
  43. package/assets/agents/security-auditor.md +39 -0
  44. package/assets/agents/sitemap-generator.md +53 -0
  45. package/assets/agents/stripe-integrator.md +59 -0
  46. package/assets/agents/tailwind-expert.md +55 -0
  47. package/assets/agents/tech-debt-tracker.md +39 -0
  48. package/assets/agents/test-writer.md +42 -0
  49. package/assets/agents/type-fixer.md +45 -0
  50. package/assets/agents/webhook-builder.md +54 -0
  51. package/assets/rules/cpp.md +53 -0
  52. package/assets/rules/css.md +52 -0
  53. package/assets/rules/go.md +50 -0
  54. package/assets/rules/html.md +52 -0
  55. package/assets/rules/java.md +51 -0
  56. package/assets/rules/kotlin.md +50 -0
  57. package/assets/rules/php.md +51 -0
  58. package/assets/rules/python.md +51 -0
  59. package/assets/rules/ruby.md +51 -0
  60. package/assets/rules/rust.md +49 -0
  61. package/assets/rules/shell.md +52 -0
  62. package/assets/rules/sql.md +49 -0
  63. package/assets/rules/swift.md +50 -0
  64. package/assets/rules/typescript.md +52 -0
  65. package/assets/rules/yaml-json.md +51 -0
  66. package/assets/skills/accessibility.md +210 -0
  67. package/assets/skills/agent-patterns.md +387 -0
  68. package/assets/skills/ai-integration.md +263 -0
  69. package/assets/skills/animation-patterns.md +224 -0
  70. package/assets/skills/api-design.md +218 -0
  71. package/assets/skills/api-gateway.md +341 -0
  72. package/assets/skills/api-versioning.md +226 -0
  73. package/assets/skills/astro-patterns.md +233 -0
  74. package/assets/skills/auth-patterns.md +248 -0
  75. package/assets/skills/aws-patterns.md +171 -0
  76. package/assets/skills/background-jobs.md +162 -0
  77. package/assets/skills/browser-extensions.md +309 -0
  78. package/assets/skills/caching-patterns.md +253 -0
  79. package/assets/skills/ci-cd.md +251 -0
  80. package/assets/skills/cli-development.md +296 -0
  81. package/assets/skills/code-review.md +185 -0
  82. package/assets/skills/cron-patterns.md +327 -0
  83. package/assets/skills/data-fetching.md +231 -0
  84. package/assets/skills/database-migrations.md +346 -0
  85. package/assets/skills/database-patterns.md +219 -0
  86. package/assets/skills/debugging.md +281 -0
  87. package/assets/skills/design-system.md +289 -0
  88. package/assets/skills/django-patterns.md +182 -0
  89. package/assets/skills/docker-patterns.md +235 -0
  90. package/assets/skills/e2e-testing.md +287 -0
  91. package/assets/skills/edge-computing.md +268 -0
  92. package/assets/skills/electron-patterns.md +266 -0
  93. package/assets/skills/email-templates.md +206 -0
  94. package/assets/skills/error-handling.md +265 -0
  95. package/assets/skills/event-driven.md +232 -0
  96. package/assets/skills/express-patterns.md +239 -0
  97. package/assets/skills/fastapi-patterns.md +198 -0
  98. package/assets/skills/feature-flags.md +212 -0
  99. package/assets/skills/figma-to-code.md +298 -0
  100. package/assets/skills/file-upload.md +228 -0
  101. package/assets/skills/forms-patterns.md +264 -0
  102. package/assets/skills/gcp-patterns.md +189 -0
  103. package/assets/skills/git-workflow.md +187 -0
  104. package/assets/skills/golang-patterns.md +185 -0
  105. package/assets/skills/graphql-patterns.md +244 -0
  106. package/assets/skills/i18n-patterns.md +172 -0
  107. package/assets/skills/image-processing.md +350 -0
  108. package/assets/skills/java-springboot.md +226 -0
  109. package/assets/skills/kotlin-patterns.md +207 -0
  110. package/assets/skills/kubernetes-patterns.md +326 -0
  111. package/assets/skills/laravel-patterns.md +261 -0
  112. package/assets/skills/llm-fine-tuning.md +335 -0
  113. package/assets/skills/load-testing.md +303 -0
  114. package/assets/skills/logging-observability.md +228 -0
  115. package/assets/skills/markdown-processing.md +318 -0
  116. package/assets/skills/mcp-server-patterns.md +292 -0
  117. package/assets/skills/microservices.md +272 -0
  118. package/assets/skills/migration-patterns.md +239 -0
  119. package/assets/skills/mongodb-patterns.md +189 -0
  120. package/assets/skills/monorepo-patterns.md +287 -0
  121. package/assets/skills/nextjs-app-router.md +237 -0
  122. package/assets/skills/notification-patterns.md +348 -0
  123. package/assets/skills/oauth-patterns.md +246 -0
  124. package/assets/skills/payment-integration.md +222 -0
  125. package/assets/skills/pdf-generation.md +307 -0
  126. package/assets/skills/performance-optimization.md +277 -0
  127. package/assets/skills/php-patterns.md +210 -0
  128. package/assets/skills/prisma-patterns.md +241 -0
  129. package/assets/skills/prompt-engineering.md +193 -0
  130. package/assets/skills/pwa-patterns.md +247 -0
  131. package/assets/skills/python-patterns.md +158 -0
  132. package/assets/skills/python-testing.md +172 -0
  133. package/assets/skills/queue-patterns.md +295 -0
  134. package/assets/skills/rag-patterns.md +159 -0
  135. package/assets/skills/rate-limiting.md +319 -0
  136. package/assets/skills/react-components.md +201 -0
  137. package/assets/skills/react-native-patterns.md +299 -0
  138. package/assets/skills/real-time-patterns.md +181 -0
  139. package/assets/skills/redis-patterns.md +188 -0
  140. package/assets/skills/refactoring.md +218 -0
  141. package/assets/skills/regex-patterns.md +191 -0
  142. package/assets/skills/remix-patterns.md +262 -0
  143. package/assets/skills/responsive-design.md +199 -0
  144. package/assets/skills/ruby-rails-patterns.md +178 -0
  145. package/assets/skills/rust-patterns.md +211 -0
  146. package/assets/skills/search-patterns.md +227 -0
  147. package/assets/skills/security-hardening.md +237 -0
  148. package/assets/skills/seo-patterns.md +179 -0
  149. package/assets/skills/serverless-patterns.md +223 -0
  150. package/assets/skills/sql-optimization.md +154 -0
  151. package/assets/skills/state-management.md +254 -0
  152. package/assets/skills/storybook-patterns.md +330 -0
  153. package/assets/skills/svelte-patterns.md +258 -0
  154. package/assets/skills/swift-patterns.md +227 -0
  155. package/assets/skills/tailwind-patterns.md +272 -0
  156. package/assets/skills/tdd-workflow.md +199 -0
  157. package/assets/skills/terraform-patterns.md +270 -0
  158. package/assets/skills/testing-react.md +240 -0
  159. package/assets/skills/testing-vitest.md +232 -0
  160. package/assets/skills/typescript-strict.md +159 -0
  161. package/assets/skills/video-processing.md +340 -0
  162. package/assets/skills/vue-patterns.md +247 -0
  163. package/assets/skills/web-workers.md +327 -0
  164. package/assets/skills/webhooks-patterns.md +283 -0
  165. package/assets/skills/websocket-patterns.md +306 -0
  166. package/dist/cli/index.js +941 -958
  167. package/dist/core/index.d.ts +341 -11
  168. package/dist/core.js +58 -95
  169. package/dist/mcp/index.d.ts +33 -1
  170. package/dist/mcp-server.js +177 -255
  171. package/package.json +4 -1
@@ -0,0 +1,270 @@
1
+ ---
2
+ name: terraform-patterns
3
+ description: Terraform and IaC patterns for modules, state management, workspaces, drift detection, and import.
4
+ ---
5
+
6
+ # Terraform / IaC Patterns
7
+
8
+ ## When to Use
9
+
10
+ Apply these patterns when managing cloud infrastructure with Terraform 1.5+. Use
11
+ this skill for structuring reusable modules, managing state safely, isolating
12
+ environments with workspaces, detecting configuration drift, and importing
13
+ existing resources under Terraform management.
14
+
15
+ ## How It Works
16
+
17
+ ### Module Structure
18
+
19
+ Create reusable modules with clear inputs, outputs, and documentation. A module
20
+ should represent one logical unit (VPC, ECS service, RDS instance).
21
+
22
+ ```
23
+ modules/
24
+ vpc/
25
+ main.tf # resources
26
+ variables.tf # input variables
27
+ outputs.tf # output values
28
+ versions.tf # required providers and versions
29
+ README.md # usage documentation
30
+ ecs-service/
31
+ main.tf
32
+ variables.tf
33
+ outputs.tf
34
+ iam.tf # IAM roles/policies for this module
35
+ versions.tf
36
+ ```
37
+
38
+ ```hcl
39
+ # modules/vpc/variables.tf
40
+ variable "name" {
41
+ type = string
42
+ description = "Name prefix for all VPC resources"
43
+ }
44
+
45
+ variable "cidr" {
46
+ type = string
47
+ description = "CIDR block for the VPC"
48
+ default = "10.0.0.0/16"
49
+
50
+ validation {
51
+ condition = can(cidrhost(var.cidr, 0))
52
+ error_message = "Must be a valid CIDR block."
53
+ }
54
+ }
55
+
56
+ variable "availability_zones" {
57
+ type = list(string)
58
+ description = "List of AZs to deploy subnets into"
59
+ }
60
+
61
+ # modules/vpc/outputs.tf
62
+ output "vpc_id" {
63
+ value = aws_vpc.main.id
64
+ description = "ID of the created VPC"
65
+ }
66
+
67
+ output "private_subnet_ids" {
68
+ value = aws_subnet.private[*].id
69
+ description = "IDs of private subnets"
70
+ }
71
+ ```
72
+
73
+ ### Root Module Composition
74
+
75
+ The root module wires together child modules and passes configuration. Keep
76
+ resource definitions in modules; the root should be mostly module calls.
77
+
78
+ ```hcl
79
+ # environments/production/main.tf
80
+ module "vpc" {
81
+ source = "../../modules/vpc"
82
+ name = "prod"
83
+ cidr = "10.0.0.0/16"
84
+ availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
85
+ }
86
+
87
+ module "database" {
88
+ source = "../../modules/rds"
89
+ name = "prod-db"
90
+ vpc_id = module.vpc.vpc_id
91
+ subnet_ids = module.vpc.private_subnet_ids
92
+ instance_class = "db.r6g.large"
93
+ allocated_storage = 100
94
+ multi_az = true
95
+ }
96
+
97
+ module "api" {
98
+ source = "../../modules/ecs-service"
99
+ name = "api"
100
+ vpc_id = module.vpc.vpc_id
101
+ subnet_ids = module.vpc.private_subnet_ids
102
+ image = "${var.ecr_repo}:${var.image_tag}"
103
+ cpu = 512
104
+ memory = 1024
105
+ desired_count = 3
106
+
107
+ environment = {
108
+ DATABASE_URL = module.database.connection_string
109
+ NODE_ENV = "production"
110
+ }
111
+ }
112
+ ```
113
+
114
+ ### State Management
115
+
116
+ Remote state prevents conflicts. Use S3 + DynamoDB for locking. Never store
117
+ state locally in production. Separate state per environment.
118
+
119
+ ```hcl
120
+ # environments/production/backend.tf
121
+ terraform {
122
+ backend "s3" {
123
+ bucket = "mycompany-terraform-state"
124
+ key = "production/terraform.tfstate"
125
+ region = "us-east-1"
126
+ dynamodb_table = "terraform-locks"
127
+ encrypt = true
128
+ }
129
+ }
130
+ ```
131
+
132
+ ```hcl
133
+ # State locking table (create once, outside Terraform)
134
+ resource "aws_dynamodb_table" "terraform_locks" {
135
+ name = "terraform-locks"
136
+ billing_mode = "PAY_PER_REQUEST"
137
+ hash_key = "LockID"
138
+
139
+ attribute {
140
+ name = "LockID"
141
+ type = "S"
142
+ }
143
+ }
144
+ ```
145
+
146
+ ### Workspaces vs. Directory Structure
147
+
148
+ Use **directory-per-environment** for significantly different configs. Use
149
+ **workspaces** only when environments share identical structure and differ
150
+ only in variable values.
151
+
152
+ ```
153
+ # Directory approach (recommended for most teams)
154
+ environments/
155
+ production/
156
+ main.tf
157
+ terraform.tfvars
158
+ backend.tf
159
+ staging/
160
+ main.tf
161
+ terraform.tfvars
162
+ backend.tf
163
+
164
+ # Workspace approach (same structure, different values)
165
+ terraform workspace select production
166
+ terraform apply -var-file="production.tfvars"
167
+ ```
168
+
169
+ ### Drift Detection
170
+
171
+ Run `terraform plan` on a schedule (CI/CD) to detect manual changes. Alert on
172
+ any planned changes when none are expected.
173
+
174
+ ```yaml
175
+ # .github/workflows/drift-detection.yml
176
+ name: Terraform Drift Detection
177
+ on:
178
+ schedule:
179
+ - cron: '0 8 * * 1-5' # weekday mornings
180
+
181
+ jobs:
182
+ detect-drift:
183
+ runs-on: ubuntu-latest
184
+ steps:
185
+ - uses: actions/checkout@v4
186
+ - uses: hashicorp/setup-terraform@v3
187
+ - run: terraform init
188
+ working-directory: environments/production
189
+ - run: terraform plan -detailed-exitcode -out=plan.tfplan
190
+ working-directory: environments/production
191
+ id: plan
192
+ continue-on-error: true
193
+ - if: steps.plan.outcome == 'failure'
194
+ run: |
195
+ echo "Drift detected in production!"
196
+ # Send alert to Slack/PagerDuty
197
+ ```
198
+
199
+ ### Import Existing Resources
200
+
201
+ Use `import` blocks (Terraform 1.5+) to bring existing resources under management
202
+ without destroying and recreating them.
203
+
204
+ ```hcl
205
+ # Import an existing S3 bucket
206
+ import {
207
+ to = aws_s3_bucket.existing_logs
208
+ id = "my-existing-log-bucket"
209
+ }
210
+
211
+ resource "aws_s3_bucket" "existing_logs" {
212
+ bucket = "my-existing-log-bucket"
213
+ # Configuration matching the existing resource
214
+ }
215
+ ```
216
+
217
+ ```bash
218
+ # Generate config for imported resources
219
+ terraform plan -generate-config-out=generated.tf
220
+ ```
221
+
222
+ ### Variable Validation and Locals
223
+
224
+ Use `validation` blocks on variables. Use `locals` for computed values to
225
+ keep resource blocks clean.
226
+
227
+ ```hcl
228
+ variable "environment" {
229
+ type = string
230
+ validation {
231
+ condition = contains(["dev", "staging", "production"], var.environment)
232
+ error_message = "Environment must be dev, staging, or production."
233
+ }
234
+ }
235
+
236
+ locals {
237
+ common_tags = {
238
+ Environment = var.environment
239
+ ManagedBy = "terraform"
240
+ Project = var.project_name
241
+ }
242
+ name_prefix = "${var.project_name}-${var.environment}"
243
+ }
244
+ ```
245
+
246
+ ## Examples
247
+
248
+ **Pattern: Conditional resource creation**
249
+ ```hcl
250
+ resource "aws_cloudwatch_metric_alarm" "cpu" {
251
+ count = var.enable_alarms ? 1 : 0
252
+ alarm_name = "${local.name_prefix}-cpu-high"
253
+ comparison_operator = "GreaterThanThreshold"
254
+ threshold = 80
255
+ # ...
256
+ }
257
+ ```
258
+
259
+ ## Checklist
260
+
261
+ - [ ] One logical unit per module (VPC, service, database)
262
+ - [ ] All variables have `type`, `description`, and `validation` where applicable
263
+ - [ ] All outputs have `description`
264
+ - [ ] Remote state with S3 + DynamoDB locking, encrypted at rest
265
+ - [ ] Separate state files per environment
266
+ - [ ] `terraform fmt` and `terraform validate` in CI
267
+ - [ ] Drift detection runs on a schedule, alerts on unexpected changes
268
+ - [ ] `import` blocks for existing resources (Terraform 1.5+)
269
+ - [ ] `locals` for computed values and common tags
270
+ - [ ] Provider versions pinned with `~>` in `versions.tf`
@@ -0,0 +1,240 @@
1
+ ---
2
+ name: testing-react
3
+ description: React Testing Library patterns including render, screen queries, userEvent, async testing, and custom hooks testing.
4
+ ---
5
+
6
+ # React Testing Library Patterns
7
+
8
+ ## When to Use
9
+ Use React Testing Library when testing React components from the user's perspective. These patterns focus on testing behavior rather than implementation details, querying by accessible roles and text rather than CSS selectors or component internals. Apply these patterns for unit tests of individual components, integration tests of connected components, and tests of custom hooks.
10
+
11
+ ## How It Works
12
+
13
+ ### Basic Component Rendering
14
+
15
+ ```typescript
16
+ import { describe, it, expect } from 'vitest';
17
+ import { render, screen } from '@testing-library/react';
18
+ import { Badge } from '../src/components/Badge';
19
+
20
+ describe('Badge', () => {
21
+ it('renders the label text', () => {
22
+ render(<Badge label="New" variant="info" />);
23
+
24
+ expect(screen.getByText('New')).toBeInTheDocument();
25
+ });
26
+
27
+ it('applies the correct variant class', () => {
28
+ render(<Badge label="Error" variant="danger" />);
29
+
30
+ const badge = screen.getByText('Error');
31
+ expect(badge).toHaveClass('badge-danger');
32
+ });
33
+
34
+ it('renders nothing when label is empty', () => {
35
+ const { container } = render(<Badge label="" variant="info" />);
36
+ expect(container.firstChild).toBeNull();
37
+ });
38
+ });
39
+ ```
40
+
41
+ ### User Interactions with userEvent
42
+
43
+ ```typescript
44
+ import { describe, it, expect, vi } from 'vitest';
45
+ import { render, screen } from '@testing-library/react';
46
+ import userEvent from '@testing-library/user-event';
47
+ import { LoginForm } from '../src/components/LoginForm';
48
+
49
+ describe('LoginForm', () => {
50
+ it('submits with email and password', async () => {
51
+ const user = userEvent.setup();
52
+ const onSubmit = vi.fn();
53
+
54
+ render(<LoginForm onSubmit={onSubmit} />);
55
+
56
+ await user.type(screen.getByLabelText('Email'), 'alice@example.com');
57
+ await user.type(screen.getByLabelText('Password'), 'secret123');
58
+ await user.click(screen.getByRole('button', { name: /sign in/i }));
59
+
60
+ expect(onSubmit).toHaveBeenCalledWith({
61
+ email: 'alice@example.com',
62
+ password: 'secret123',
63
+ });
64
+ });
65
+
66
+ it('disables submit when fields are empty', async () => {
67
+ render(<LoginForm onSubmit={vi.fn()} />);
68
+
69
+ const submitButton = screen.getByRole('button', { name: /sign in/i });
70
+ expect(submitButton).toBeDisabled();
71
+ });
72
+
73
+ it('shows validation error for invalid email', async () => {
74
+ const user = userEvent.setup();
75
+ render(<LoginForm onSubmit={vi.fn()} />);
76
+
77
+ await user.type(screen.getByLabelText('Email'), 'not-an-email');
78
+ await user.tab();
79
+
80
+ expect(screen.getByText('Please enter a valid email')).toBeInTheDocument();
81
+ });
82
+ });
83
+ ```
84
+
85
+ ### Async Testing with waitFor and findBy
86
+
87
+ ```typescript
88
+ import { describe, it, expect, vi } from 'vitest';
89
+ import { render, screen, waitFor } from '@testing-library/react';
90
+ import { UserList } from '../src/components/UserList';
91
+ import * as api from '../src/api';
92
+
93
+ vi.mock('../src/api');
94
+
95
+ describe('UserList', () => {
96
+ it('displays users after loading', async () => {
97
+ vi.mocked(api.fetchUsers).mockResolvedValueOnce([
98
+ { id: '1', name: 'Alice' },
99
+ { id: '2', name: 'Bob' },
100
+ ]);
101
+
102
+ render(<UserList />);
103
+
104
+ expect(screen.getByText('Loading...')).toBeInTheDocument();
105
+
106
+ const alice = await screen.findByText('Alice');
107
+ expect(alice).toBeInTheDocument();
108
+ expect(screen.getByText('Bob')).toBeInTheDocument();
109
+ expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
110
+ });
111
+
112
+ it('shows error message on fetch failure', async () => {
113
+ vi.mocked(api.fetchUsers).mockRejectedValueOnce(new Error('Server error'));
114
+
115
+ render(<UserList />);
116
+
117
+ await waitFor(() => {
118
+ expect(screen.getByRole('alert')).toHaveTextContent('Failed to load users');
119
+ });
120
+ });
121
+ });
122
+ ```
123
+
124
+ ### Testing Custom Hooks
125
+
126
+ ```typescript
127
+ import { describe, it, expect, vi } from 'vitest';
128
+ import { renderHook, act } from '@testing-library/react';
129
+ import { useDebounce } from '../src/hooks/useDebounce';
130
+
131
+ describe('useDebounce', () => {
132
+ it('returns debounced value after delay', async () => {
133
+ vi.useFakeTimers();
134
+
135
+ const { result, rerender } = renderHook(
136
+ ({ value }) => useDebounce(value, 500),
137
+ { initialProps: { value: 'hello' } },
138
+ );
139
+
140
+ expect(result.current).toBe('hello');
141
+
142
+ rerender({ value: 'hello world' });
143
+ expect(result.current).toBe('hello');
144
+
145
+ act(() => {
146
+ vi.advanceTimersByTime(500);
147
+ });
148
+
149
+ expect(result.current).toBe('hello world');
150
+ vi.useRealTimers();
151
+ });
152
+ });
153
+ ```
154
+
155
+ ### Testing with Providers (Router, Theme, Store)
156
+
157
+ ```typescript
158
+ import { describe, it, expect } from 'vitest';
159
+ import { render, screen } from '@testing-library/react';
160
+ import { MemoryRouter } from 'react-router-dom';
161
+ import { ThemeProvider } from '../src/context/ThemeContext';
162
+ import { Navbar } from '../src/components/Navbar';
163
+
164
+ function renderWithProviders(ui: React.ReactElement, { route = '/' } = {}) {
165
+ return render(
166
+ <MemoryRouter initialEntries={[route]}>
167
+ <ThemeProvider defaultTheme="light">
168
+ {ui}
169
+ </ThemeProvider>
170
+ </MemoryRouter>,
171
+ );
172
+ }
173
+
174
+ describe('Navbar', () => {
175
+ it('highlights the active link', () => {
176
+ renderWithProviders(<Navbar />, { route: '/settings' });
177
+
178
+ const settingsLink = screen.getByRole('link', { name: /settings/i });
179
+ expect(settingsLink).toHaveAttribute('aria-current', 'page');
180
+ });
181
+
182
+ it('renders all navigation items', () => {
183
+ renderWithProviders(<Navbar />);
184
+
185
+ expect(screen.getByRole('link', { name: /dashboard/i })).toBeInTheDocument();
186
+ expect(screen.getByRole('link', { name: /projects/i })).toBeInTheDocument();
187
+ expect(screen.getByRole('link', { name: /settings/i })).toBeInTheDocument();
188
+ });
189
+ });
190
+ ```
191
+
192
+ ### Accessibility Queries Priority
193
+
194
+ ```typescript
195
+ import { describe, it, expect } from 'vitest';
196
+ import { render, screen } from '@testing-library/react';
197
+ import { SearchBar } from '../src/components/SearchBar';
198
+
199
+ describe('SearchBar accessibility', () => {
200
+ it('uses accessible queries in priority order', () => {
201
+ render(<SearchBar placeholder="Search projects..." />);
202
+
203
+ // 1. getByRole - best, matches ARIA roles
204
+ screen.getByRole('searchbox');
205
+
206
+ // 2. getByLabelText - for form fields
207
+ screen.getByLabelText('Search');
208
+
209
+ // 3. getByPlaceholderText - when no label exists
210
+ screen.getByPlaceholderText('Search projects...');
211
+
212
+ // 4. getByText - for non-interactive elements
213
+ screen.getByText('Search');
214
+
215
+ // 5. getByTestId - last resort
216
+ screen.getByTestId('search-bar');
217
+ });
218
+ });
219
+ ```
220
+
221
+ ## Examples
222
+
223
+ | Query | When to Use | Example |
224
+ |-------|------------|---------|
225
+ | `getByRole` | Buttons, links, headings, form controls | `getByRole('button', { name: /save/i })` |
226
+ | `getByLabelText` | Form inputs with associated labels | `getByLabelText('Email address')` |
227
+ | `getByText` | Static text content | `getByText(/no results found/i)` |
228
+ | `findByText` | Elements that appear asynchronously | `await findByText('Loaded!')` |
229
+ | `queryByText` | Asserting an element does NOT exist | `expect(queryByText('Error')).toBeNull()` |
230
+ | `getAllByRole` | Multiple matching elements | `getAllByRole('listitem')` |
231
+
232
+ ## Checklist
233
+ - [ ] Use `userEvent.setup()` instead of `fireEvent` for realistic interactions
234
+ - [ ] Prefer `getByRole` and `getByLabelText` over `getByTestId`
235
+ - [ ] Use `findBy*` (async) for elements that appear after loading or state changes
236
+ - [ ] Use `queryBy*` only when asserting an element is absent
237
+ - [ ] Wrap state updates in `act()` only when not already handled by RTL utilities
238
+ - [ ] Provide wrapper components (router, theme, store) via a shared `renderWithProviders` helper
239
+ - [ ] Never test internal state or implementation details (no `component.state` access)
240
+ - [ ] Run with `jsdom` or `happy-dom` environment configured in vitest.config.ts