@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,182 @@
1
+ ---
2
+ name: django-patterns
3
+ description: Django patterns for models, views, serializers, middleware, signals, custom managers, and admin customization.
4
+ ---
5
+
6
+ # Django Patterns
7
+
8
+ ## When to Use
9
+
10
+ Apply these patterns when building Django 4.2+ applications. Use this skill for
11
+ structuring models with custom managers, writing class-based views, building DRF
12
+ serializers, wiring signals correctly, and customizing the admin interface.
13
+
14
+ ## How It Works
15
+
16
+ ### Models with Custom Managers and QuerySets
17
+
18
+ Define a custom QuerySet, then create a manager from it. This lets you chain custom
19
+ filters naturally. Always add `Meta.ordering` and `__str__`.
20
+
21
+ ```python
22
+ class ArticleQuerySet(models.QuerySet):
23
+ def published(self):
24
+ return self.filter(status="published", pub_date__lte=timezone.now())
25
+
26
+ def by_author(self, user):
27
+ return self.filter(author=user)
28
+
29
+ class Article(models.Model):
30
+ title = models.CharField(max_length=200)
31
+ status = models.CharField(max_length=20, choices=STATUS_CHOICES, db_index=True)
32
+ pub_date = models.DateTimeField(null=True, blank=True)
33
+ author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
34
+
35
+ objects = ArticleQuerySet.as_manager()
36
+
37
+ class Meta:
38
+ ordering = ["-pub_date"]
39
+ indexes = [models.Index(fields=["status", "pub_date"])]
40
+
41
+ def __str__(self):
42
+ return self.title
43
+ ```
44
+
45
+ ### Views: Fat Models, Thin Views
46
+
47
+ Keep business logic in model methods or service functions. Views handle HTTP
48
+ concerns only: parsing requests, calling services, returning responses.
49
+
50
+ ```python
51
+ # services.py
52
+ def publish_article(article: Article, user: User) -> Article:
53
+ if not user.has_perm("articles.publish"):
54
+ raise PermissionDenied("Cannot publish")
55
+ article.status = "published"
56
+ article.pub_date = timezone.now()
57
+ article.save(update_fields=["status", "pub_date"])
58
+ return article
59
+
60
+ # views.py
61
+ class ArticlePublishView(LoginRequiredMixin, View):
62
+ def post(self, request, pk):
63
+ article = get_object_or_404(Article, pk=pk)
64
+ publish_article(article, request.user)
65
+ return redirect("article-detail", pk=pk)
66
+ ```
67
+
68
+ ### DRF Serializers
69
+
70
+ Use `ModelSerializer` for CRUD. Override `validate_<field>` for field-level
71
+ validation. Use `SerializerMethodField` for computed fields. Nest read serializers;
72
+ accept IDs for writes.
73
+
74
+ ```python
75
+ class ArticleSerializer(serializers.ModelSerializer):
76
+ author_name = serializers.SerializerMethodField()
77
+ word_count = serializers.SerializerMethodField()
78
+
79
+ class Meta:
80
+ model = Article
81
+ fields = ["id", "title", "status", "pub_date", "author", "author_name", "word_count"]
82
+ read_only_fields = ["pub_date"]
83
+
84
+ def get_author_name(self, obj):
85
+ return obj.author.get_full_name()
86
+
87
+ def get_word_count(self, obj):
88
+ return len(obj.body.split())
89
+
90
+ def validate_title(self, value):
91
+ if len(value) < 5:
92
+ raise serializers.ValidationError("Title must be at least 5 characters")
93
+ return value
94
+ ```
95
+
96
+ ### Middleware
97
+
98
+ Middleware processes every request/response. Keep it fast. Use `__init__` for
99
+ one-time setup and `__call__` for per-request logic.
100
+
101
+ ```python
102
+ class RequestTimingMiddleware:
103
+ def __init__(self, get_response):
104
+ self.get_response = get_response
105
+
106
+ def __call__(self, request):
107
+ start = time.monotonic()
108
+ response = self.get_response(request)
109
+ duration_ms = (time.monotonic() - start) * 1000
110
+ response["X-Request-Duration-Ms"] = f"{duration_ms:.1f}"
111
+ return response
112
+ ```
113
+
114
+ ### Signals: Use Sparingly
115
+
116
+ Signals create implicit coupling. Prefer explicit service calls. When you do use
117
+ signals, connect them in `AppConfig.ready()` and keep handlers idempotent.
118
+
119
+ ```python
120
+ # apps.py
121
+ class ArticlesConfig(AppConfig):
122
+ name = "articles"
123
+ def ready(self):
124
+ from . import signals # noqa: F401
125
+
126
+ # signals.py
127
+ from django.db.models.signals import post_save
128
+ from django.dispatch import receiver
129
+
130
+ @receiver(post_save, sender=Article)
131
+ def notify_subscribers(sender, instance, created, **kwargs):
132
+ if created and instance.status == "published":
133
+ enqueue_notification_task(instance.pk)
134
+ ```
135
+
136
+ ### Admin Customization
137
+
138
+ Use `list_display`, `list_filter`, `search_fields` for usability. Add custom
139
+ actions for bulk operations. Use `readonly_fields` for computed displays.
140
+
141
+ ```python
142
+ @admin.register(Article)
143
+ class ArticleAdmin(admin.ModelAdmin):
144
+ list_display = ["title", "author", "status", "pub_date"]
145
+ list_filter = ["status", "pub_date"]
146
+ search_fields = ["title", "body"]
147
+ raw_id_fields = ["author"]
148
+ actions = ["mark_published"]
149
+
150
+ @admin.action(description="Mark selected as published")
151
+ def mark_published(self, request, queryset):
152
+ queryset.update(status="published", pub_date=timezone.now())
153
+ ```
154
+
155
+ ## Examples
156
+
157
+ **Pattern: select_related / prefetch_related to avoid N+1**
158
+ ```python
159
+ articles = Article.objects.published().select_related("author").prefetch_related("tags")
160
+ ```
161
+
162
+ **Pattern: Database constraint at the model level**
163
+ ```python
164
+ class Meta:
165
+ constraints = [
166
+ models.UniqueConstraint(fields=["author", "slug"], name="unique_author_slug"),
167
+ models.CheckConstraint(check=models.Q(price__gte=0), name="price_non_negative"),
168
+ ]
169
+ ```
170
+
171
+ ## Checklist
172
+
173
+ - [ ] Every model has `__str__`, `Meta.ordering`, and relevant indexes
174
+ - [ ] Custom QuerySet methods chained via `.as_manager()`
175
+ - [ ] Business logic in service functions, not views
176
+ - [ ] `select_related` / `prefetch_related` for related lookups (no N+1 queries)
177
+ - [ ] Serializers validate at field and object level
178
+ - [ ] Signals connected in `AppConfig.ready()`, kept idempotent
179
+ - [ ] Middleware is fast and stateless
180
+ - [ ] Admin uses `list_display`, `list_filter`, `search_fields`
181
+ - [ ] Database constraints (`UniqueConstraint`, `CheckConstraint`) enforce integrity
182
+ - [ ] `update_fields` passed to `.save()` when only specific columns change
@@ -0,0 +1,235 @@
1
+ ---
2
+ name: docker-patterns
3
+ description: Build efficient Docker images with multi-stage builds, layer caching, health checks, and Compose orchestration.
4
+ ---
5
+
6
+ # Docker Patterns
7
+
8
+ ## When to Use
9
+
10
+ Use these patterns when containerizing any application for development, CI, or
11
+ production. A poorly written Dockerfile wastes minutes per build, ships hundreds
12
+ of megabytes of unnecessary files, and creates security vulnerabilities.
13
+
14
+ ## How It Works
15
+
16
+ ### 1. Multi-Stage Builds
17
+
18
+ Separate build dependencies from the runtime image. Final image contains only
19
+ what's needed to run.
20
+
21
+ ```dockerfile
22
+ # Stage 1: Build
23
+ FROM node:20-alpine AS builder
24
+ WORKDIR /app
25
+ COPY package.json package-lock.json ./
26
+ RUN npm ci --ignore-scripts
27
+ COPY tsconfig.json ./
28
+ COPY src/ src/
29
+ RUN npm run build
30
+
31
+ # Stage 2: Production
32
+ FROM node:20-alpine AS runner
33
+ WORKDIR /app
34
+ ENV NODE_ENV=production
35
+
36
+ RUN addgroup -g 1001 appuser && adduser -u 1001 -G appuser -s /bin/sh -D appuser
37
+
38
+ COPY --from=builder /app/dist ./dist
39
+ COPY --from=builder /app/package.json /app/package-lock.json ./
40
+ RUN npm ci --ignore-scripts --omit=dev && npm cache clean --force
41
+
42
+ USER appuser
43
+ EXPOSE 3000
44
+ CMD ["node", "dist/index.js"]
45
+ ```
46
+
47
+ Result: builder stage has TypeScript, dev dependencies, source. Runner stage has
48
+ only compiled JS and production deps. Image shrinks from ~800MB to ~150MB.
49
+
50
+ ### 2. Layer Caching
51
+
52
+ Docker caches each layer. Order instructions from least-changing to most-changing.
53
+
54
+ ```dockerfile
55
+ # Good order — dependencies change less often than source code
56
+ COPY package.json package-lock.json ./ # Layer 1: cached unless deps change
57
+ RUN npm ci # Layer 2: cached with Layer 1
58
+ COPY src/ src/ # Layer 3: busted on every code change
59
+ RUN npm run build # Layer 4: busted with Layer 3
60
+ ```
61
+
62
+ ```dockerfile
63
+ # Bad order — every code change reinstalls dependencies
64
+ COPY . . # Always busted
65
+ RUN npm ci && npm run build # Always reinstalls
66
+ ```
67
+
68
+ ### 3. .dockerignore
69
+
70
+ Exclude files that bloat context and leak secrets.
71
+
72
+ ```
73
+ node_modules
74
+ .git
75
+ .env*
76
+ *.md
77
+ coverage
78
+ .next
79
+ dist
80
+ ```
81
+
82
+ ### 4. Health Checks
83
+
84
+ Let orchestrators know when your app is actually ready to serve traffic.
85
+
86
+ ```dockerfile
87
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
88
+ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
89
+ ```
90
+
91
+ The health endpoint should verify critical dependencies:
92
+
93
+ ```typescript
94
+ app.get('/health', async (req, res) => {
95
+ try {
96
+ await db.$queryRaw`SELECT 1`;
97
+ res.json({ status: 'ok', db: 'connected' });
98
+ } catch {
99
+ res.status(503).json({ status: 'unhealthy', db: 'disconnected' });
100
+ }
101
+ });
102
+ ```
103
+
104
+ ### 5. Docker Compose for Development
105
+
106
+ ```yaml
107
+ # docker-compose.yml
108
+ services:
109
+ app:
110
+ build:
111
+ context: .
112
+ target: builder # use the builder stage for dev
113
+ ports:
114
+ - "3000:3000"
115
+ volumes:
116
+ - ./src:/app/src # hot reload
117
+ - /app/node_modules # prevent host overwrite
118
+ environment:
119
+ - DATABASE_URL=postgresql://postgres:postgres@db:5432/app
120
+ - REDIS_URL=redis://cache:6379
121
+ depends_on:
122
+ db:
123
+ condition: service_healthy
124
+ command: npm run dev
125
+
126
+ db:
127
+ image: postgres:16-alpine
128
+ environment:
129
+ POSTGRES_DB: app
130
+ POSTGRES_PASSWORD: postgres
131
+ volumes:
132
+ - pgdata:/var/lib/postgresql/data
133
+ ports:
134
+ - "5432:5432"
135
+ healthcheck:
136
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
137
+ interval: 5s
138
+ timeout: 3s
139
+ retries: 5
140
+
141
+ cache:
142
+ image: redis:7-alpine
143
+ ports:
144
+ - "6379:6379"
145
+
146
+ volumes:
147
+ pgdata:
148
+ ```
149
+
150
+ ### 6. Secrets Management
151
+
152
+ Never bake secrets into images. Use build-time secrets or runtime injection.
153
+
154
+ ```dockerfile
155
+ # Build-time secret (Docker BuildKit)
156
+ RUN --mount=type=secret,id=npm_token \
157
+ NPM_TOKEN=$(cat /run/secrets/npm_token) npm ci
158
+ ```
159
+
160
+ ```bash
161
+ docker build --secret id=npm_token,src=.npm_token .
162
+ ```
163
+
164
+ Runtime: pass via environment variables or mounted secret files.
165
+
166
+ ```yaml
167
+ # docker-compose.yml
168
+ services:
169
+ app:
170
+ environment:
171
+ - DATABASE_URL # inherits from host env
172
+ secrets:
173
+ - api_key
174
+
175
+ secrets:
176
+ api_key:
177
+ file: ./secrets/api_key.txt
178
+ ```
179
+
180
+ ### 7. Volume Patterns
181
+
182
+ ```yaml
183
+ volumes:
184
+ # Named volume — persists across restarts
185
+ pgdata:
186
+ driver: local
187
+
188
+ # Bind mount — sync host files into container
189
+ # Good for dev, bad for production
190
+ ./src:/app/src
191
+
192
+ # Anonymous volume — prevent host from overwriting container dirs
193
+ /app/node_modules
194
+ ```
195
+
196
+ ### 8. Production Best Practices
197
+
198
+ ```dockerfile
199
+ # Pin exact versions, not :latest
200
+ FROM node:20.11.1-alpine3.19
201
+
202
+ # Run as non-root
203
+ USER 1001
204
+
205
+ # Set memory limits
206
+ ENV NODE_OPTIONS="--max-old-space-size=256"
207
+
208
+ # Use tini as PID 1 for proper signal handling
209
+ RUN apk add --no-cache tini
210
+ ENTRYPOINT ["/sbin/tini", "--"]
211
+ CMD ["node", "dist/index.js"]
212
+ ```
213
+
214
+ ## Examples
215
+
216
+ | Goal | Pattern |
217
+ |------|---------|
218
+ | Small image | Multi-stage build, Alpine base |
219
+ | Fast rebuilds | Layer caching, .dockerignore |
220
+ | Reliable orchestration | Health checks with dependency verification |
221
+ | Local development | Compose with bind mounts and hot reload |
222
+ | Secret protection | BuildKit secrets, never `ENV` or `ARG` for secrets |
223
+
224
+ ## Checklist
225
+
226
+ - [ ] Dockerfile uses multi-stage build — final image has no build tools
227
+ - [ ] Base image is pinned to a specific version, not `:latest`
228
+ - [ ] `.dockerignore` excludes `node_modules`, `.git`, `.env*`, and build artifacts
229
+ - [ ] COPY order puts `package*.json` before source code for layer caching
230
+ - [ ] `HEALTHCHECK` instruction is present with reasonable intervals
231
+ - [ ] Application runs as a non-root user
232
+ - [ ] No secrets in the image — use BuildKit secrets or runtime env vars
233
+ - [ ] `tini` or equivalent handles PID 1 for proper signal forwarding
234
+ - [ ] Compose `depends_on` uses `condition: service_healthy`
235
+ - [ ] Production image is < 200MB (verify with `docker images`)
@@ -0,0 +1,287 @@
1
+ ---
2
+ name: e2e-testing
3
+ description: E2E testing patterns with Playwright including page objects, fixtures, network mocking, visual comparisons, and CI integration.
4
+ ---
5
+
6
+ # E2E Testing Patterns
7
+
8
+ ## When to Use
9
+ Write end-to-end tests for critical user journeys: authentication, checkout, onboarding, data-entry workflows, and any path where a failure means lost revenue or broken trust. Use Playwright for cross-browser coverage (Chromium, Firefox, WebKit), built-in auto-waiting, and powerful network interception. Reserve E2E for high-value flows and use unit/integration tests for everything else.
10
+
11
+ ## How It Works
12
+
13
+ ### Playwright Configuration
14
+
15
+ ```typescript
16
+ // playwright.config.ts
17
+ import { defineConfig, devices } from '@playwright/test';
18
+
19
+ export default defineConfig({
20
+ testDir: './e2e',
21
+ fullyParallel: true,
22
+ forbidOnly: !!process.env.CI,
23
+ retries: process.env.CI ? 2 : 0,
24
+ workers: process.env.CI ? 1 : undefined,
25
+ reporter: [
26
+ ['html', { open: 'never' }],
27
+ ['junit', { outputFile: 'results/e2e.xml' }],
28
+ ],
29
+ use: {
30
+ baseURL: 'http://localhost:3000',
31
+ trace: 'on-first-retry',
32
+ screenshot: 'only-on-failure',
33
+ video: 'on-first-retry',
34
+ },
35
+ projects: [
36
+ { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
37
+ { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
38
+ { name: 'webkit', use: { ...devices['Desktop Safari'] } },
39
+ { name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },
40
+ { name: 'mobile-safari', use: { ...devices['iPhone 13'] } },
41
+ ],
42
+ webServer: {
43
+ command: 'npm run dev',
44
+ url: 'http://localhost:3000',
45
+ reuseExistingServer: !process.env.CI,
46
+ timeout: 120_000,
47
+ },
48
+ });
49
+ ```
50
+
51
+ ### Page Object Model
52
+
53
+ ```typescript
54
+ // e2e/pages/login.page.ts
55
+ import { Page, Locator, expect } from '@playwright/test';
56
+
57
+ export class LoginPage {
58
+ readonly page: Page;
59
+ readonly emailInput: Locator;
60
+ readonly passwordInput: Locator;
61
+ readonly submitButton: Locator;
62
+ readonly errorAlert: Locator;
63
+
64
+ constructor(page: Page) {
65
+ this.page = page;
66
+ this.emailInput = page.getByLabel('Email');
67
+ this.passwordInput = page.getByLabel('Password');
68
+ this.submitButton = page.getByRole('button', { name: 'Sign in' });
69
+ this.errorAlert = page.getByRole('alert');
70
+ }
71
+
72
+ async goto() {
73
+ await this.page.goto('/login');
74
+ }
75
+
76
+ async login(email: string, password: string) {
77
+ await this.emailInput.fill(email);
78
+ await this.passwordInput.fill(password);
79
+ await this.submitButton.click();
80
+ }
81
+
82
+ async expectError(message: string) {
83
+ await expect(this.errorAlert).toContainText(message);
84
+ }
85
+
86
+ async expectRedirectTo(path: string) {
87
+ await expect(this.page).toHaveURL(new RegExp(path));
88
+ }
89
+ }
90
+ ```
91
+
92
+ ```typescript
93
+ // e2e/pages/dashboard.page.ts
94
+ import { Page, Locator, expect } from '@playwright/test';
95
+
96
+ export class DashboardPage {
97
+ readonly page: Page;
98
+ readonly heading: Locator;
99
+ readonly userMenu: Locator;
100
+ readonly navLinks: Locator;
101
+
102
+ constructor(page: Page) {
103
+ this.page = page;
104
+ this.heading = page.getByRole('heading', { level: 1 });
105
+ this.userMenu = page.getByTestId('user-menu');
106
+ this.navLinks = page.getByRole('navigation').getByRole('link');
107
+ }
108
+
109
+ async expectLoaded() {
110
+ await expect(this.heading).toBeVisible();
111
+ }
112
+
113
+ async logout() {
114
+ await this.userMenu.click();
115
+ await this.page.getByRole('menuitem', { name: 'Logout' }).click();
116
+ }
117
+ }
118
+ ```
119
+
120
+ ### Custom Fixtures
121
+
122
+ ```typescript
123
+ // e2e/fixtures.ts
124
+ import { test as base, expect } from '@playwright/test';
125
+ import { LoginPage } from './pages/login.page';
126
+ import { DashboardPage } from './pages/dashboard.page';
127
+
128
+ type Fixtures = {
129
+ loginPage: LoginPage;
130
+ dashboardPage: DashboardPage;
131
+ authenticatedPage: Page;
132
+ };
133
+
134
+ export const test = base.extend<Fixtures>({
135
+ loginPage: async ({ page }, use) => {
136
+ await use(new LoginPage(page));
137
+ },
138
+ dashboardPage: async ({ page }, use) => {
139
+ await use(new DashboardPage(page));
140
+ },
141
+ authenticatedPage: async ({ browser }, use) => {
142
+ const context = await browser.newContext({
143
+ storageState: 'e2e/.auth/user.json',
144
+ });
145
+ const page = await context.newPage();
146
+ await use(page);
147
+ await context.close();
148
+ },
149
+ });
150
+
151
+ export { expect };
152
+ ```
153
+
154
+ ### Authentication Setup
155
+
156
+ ```typescript
157
+ // e2e/auth.setup.ts
158
+ import { test as setup, expect } from '@playwright/test';
159
+
160
+ setup('authenticate', async ({ page }) => {
161
+ await page.goto('/login');
162
+ await page.getByLabel('Email').fill('test@example.com');
163
+ await page.getByLabel('Password').fill('TestPassword123');
164
+ await page.getByRole('button', { name: 'Sign in' }).click();
165
+ await expect(page).toHaveURL('/dashboard');
166
+
167
+ // Save auth state for reuse
168
+ await page.context().storageState({ path: 'e2e/.auth/user.json' });
169
+ });
170
+ ```
171
+
172
+ ### Network Mocking
173
+
174
+ ```typescript
175
+ // e2e/tests/dashboard.spec.ts
176
+ import { test, expect } from '../fixtures';
177
+
178
+ test.describe('Dashboard', () => {
179
+ test('displays analytics data', async ({ page }) => {
180
+ // Mock the API response
181
+ await page.route('**/api/analytics*', (route) =>
182
+ route.fulfill({
183
+ status: 200,
184
+ contentType: 'application/json',
185
+ body: JSON.stringify({
186
+ visitors: 1234,
187
+ pageViews: 5678,
188
+ bounceRate: 0.42,
189
+ }),
190
+ })
191
+ );
192
+
193
+ await page.goto('/dashboard');
194
+ await expect(page.getByText('1,234')).toBeVisible();
195
+ await expect(page.getByText('5,678')).toBeVisible();
196
+ });
197
+
198
+ test('handles API failure gracefully', async ({ page }) => {
199
+ await page.route('**/api/analytics*', (route) =>
200
+ route.fulfill({ status: 500 })
201
+ );
202
+
203
+ await page.goto('/dashboard');
204
+ await expect(page.getByText('Failed to load analytics')).toBeVisible();
205
+ await expect(page.getByRole('button', { name: 'Retry' })).toBeVisible();
206
+ });
207
+
208
+ test('waits for network idle', async ({ page }) => {
209
+ const responsePromise = page.waitForResponse('**/api/analytics*');
210
+ await page.goto('/dashboard');
211
+ const response = await responsePromise;
212
+ expect(response.status()).toBe(200);
213
+ });
214
+ });
215
+ ```
216
+
217
+ ### Visual Comparisons
218
+
219
+ ```typescript
220
+ // e2e/tests/visual.spec.ts
221
+ import { test, expect } from '@playwright/test';
222
+
223
+ test('homepage visual regression', async ({ page }) => {
224
+ await page.goto('/');
225
+ await expect(page).toHaveScreenshot('homepage.png', {
226
+ maxDiffPixelRatio: 0.01,
227
+ fullPage: true,
228
+ });
229
+ });
230
+
231
+ test('component visual states', async ({ page }) => {
232
+ await page.goto('/storybook/iframe.html?id=button--primary');
233
+ await expect(page.locator('#storybook-root')).toHaveScreenshot('button-primary.png');
234
+
235
+ // Hover state
236
+ await page.locator('button').hover();
237
+ await expect(page.locator('#storybook-root')).toHaveScreenshot('button-primary-hover.png');
238
+ });
239
+ ```
240
+
241
+ ### CI Integration
242
+
243
+ ```yaml
244
+ # .github/workflows/e2e.yml
245
+ name: E2E Tests
246
+ on:
247
+ pull_request:
248
+ branches: [main]
249
+
250
+ jobs:
251
+ e2e:
252
+ runs-on: ubuntu-latest
253
+ timeout-minutes: 30
254
+ steps:
255
+ - uses: actions/checkout@v4
256
+ - uses: actions/setup-node@v4
257
+ with: { node-version: 20, cache: npm }
258
+ - run: npm ci
259
+ - run: npx playwright install --with-deps
260
+ - run: npx playwright test
261
+ - uses: actions/upload-artifact@v4
262
+ if: failure()
263
+ with:
264
+ name: playwright-report
265
+ path: playwright-report/
266
+ retention-days: 7
267
+ ```
268
+
269
+ ## Examples
270
+
271
+ | Pattern | Problem | Solution |
272
+ |---------|---------|----------|
273
+ | Page Object | Duplicate locators across tests | Centralized selectors and actions |
274
+ | Custom fixture | Repeated setup per test | Shared auth, page objects via fixture |
275
+ | Storage state | Re-login per test is slow | Save/restore auth cookies |
276
+ | `page.route()` | Tests depend on live API | Mock API for deterministic data |
277
+ | `toHaveScreenshot()` | Visual bugs missed in review | Pixel-diff screenshots in CI |
278
+
279
+ ## Checklist
280
+ - [ ] Tests use Page Object Model for reusable selectors and actions
281
+ - [ ] Custom fixtures provide page objects and authenticated contexts
282
+ - [ ] Auth state saved to file and reused across tests (not re-login per test)
283
+ - [ ] Network requests mocked for deterministic test data
284
+ - [ ] Locators use accessible roles (`getByRole`, `getByLabel`) not CSS selectors
285
+ - [ ] Visual regression screenshots baselined and checked on PR
286
+ - [ ] CI configured with retries, trace-on-failure, and artifact upload
287
+ - [ ] Tests run across at least Chromium, Firefox, and one mobile viewport