@su-record/vibe 2.6.13 → 2.6.15
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.
- package/LICENSE +21 -21
- package/README.md +497 -497
- package/agents/architect-low.md +41 -41
- package/agents/architect-medium.md +59 -59
- package/agents/architect.md +80 -80
- package/agents/build-error-resolver.md +115 -115
- package/agents/compounder.md +261 -261
- package/agents/diagrammer.md +178 -178
- package/agents/e2e-tester.md +266 -266
- package/agents/explorer-low.md +42 -42
- package/agents/explorer-medium.md +59 -59
- package/agents/explorer.md +48 -48
- package/agents/implementer-low.md +43 -43
- package/agents/implementer-medium.md +52 -52
- package/agents/implementer.md +54 -54
- package/agents/refactor-cleaner.md +143 -143
- package/agents/research/best-practices-agent.md +199 -199
- package/agents/research/codebase-patterns-agent.md +157 -157
- package/agents/research/framework-docs-agent.md +188 -188
- package/agents/research/security-advisory-agent.md +213 -213
- package/agents/review/architecture-reviewer.md +107 -107
- package/agents/review/complexity-reviewer.md +116 -116
- package/agents/review/data-integrity-reviewer.md +88 -88
- package/agents/review/git-history-reviewer.md +103 -103
- package/agents/review/performance-reviewer.md +86 -86
- package/agents/review/python-reviewer.md +150 -150
- package/agents/review/rails-reviewer.md +139 -139
- package/agents/review/react-reviewer.md +144 -144
- package/agents/review/security-reviewer.md +80 -80
- package/agents/review/simplicity-reviewer.md +140 -140
- package/agents/review/test-coverage-reviewer.md +116 -116
- package/agents/review/typescript-reviewer.md +127 -127
- package/agents/searcher.md +54 -54
- package/agents/simplifier.md +120 -120
- package/agents/tester.md +49 -49
- package/agents/ui-previewer.md +268 -268
- package/commands/vibe.analyze.md +356 -356
- package/commands/vibe.reason.md +329 -329
- package/commands/vibe.review.md +412 -412
- package/commands/vibe.run.md +1266 -1266
- package/commands/vibe.spec.md +1054 -1054
- package/commands/vibe.spec.review.md +319 -319
- package/commands/vibe.trace.md +161 -161
- package/commands/vibe.utils.md +376 -376
- package/commands/vibe.verify.md +375 -375
- package/dist/cli/collaborator.js +52 -52
- package/dist/cli/detect.d.ts.map +1 -1
- package/dist/cli/detect.js +118 -44
- package/dist/cli/detect.js.map +1 -1
- package/dist/cli/hud.js +20 -20
- package/dist/cli/index.js +118 -118
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/llm.js +144 -144
- package/dist/cli/mcp.d.ts +49 -0
- package/dist/cli/mcp.d.ts.map +1 -0
- package/dist/cli/mcp.js +169 -0
- package/dist/cli/mcp.js.map +1 -0
- package/dist/cli/postinstall.js +858 -858
- package/dist/cli/setup/ProjectSetup.d.ts +3 -0
- package/dist/cli/setup/ProjectSetup.d.ts.map +1 -1
- package/dist/cli/setup/ProjectSetup.js +28 -6
- package/dist/cli/setup/ProjectSetup.js.map +1 -1
- package/dist/lib/DeepInit.js +24 -24
- package/dist/lib/IterationTracker.js +11 -11
- package/dist/lib/PythonParser.js +108 -108
- package/dist/lib/ReviewRace.js +96 -96
- package/dist/lib/SkillFrontmatter.js +28 -28
- package/dist/lib/SkillQualityGate.js +9 -9
- package/dist/lib/SkillRepository.js +159 -159
- package/dist/lib/UltraQA.js +77 -77
- package/dist/lib/gemini-api.js +5 -5
- package/dist/lib/gemini-mcp.d.ts +10 -0
- package/dist/lib/gemini-mcp.d.ts.map +1 -0
- package/dist/lib/gemini-mcp.js +353 -0
- package/dist/lib/gemini-mcp.js.map +1 -0
- package/dist/lib/gpt-api.js +4 -4
- package/dist/lib/gpt-mcp.d.ts +10 -0
- package/dist/lib/gpt-mcp.d.ts.map +1 -0
- package/dist/lib/gpt-mcp.js +352 -0
- package/dist/lib/gpt-mcp.js.map +1 -0
- package/dist/lib/memory/KnowledgeGraph.js +4 -4
- package/dist/lib/memory/MemorySearch.js +20 -20
- package/dist/lib/memory/MemoryStorage.js +64 -64
- package/dist/orchestrator/AgentManager.js +12 -12
- package/dist/orchestrator/MultiLlmResearch.js +8 -8
- package/dist/orchestrator/SmartRouter.js +11 -11
- package/dist/orchestrator/parallelResearch.js +24 -24
- package/dist/tools/analytics/getUsageAnalytics.d.ts +10 -0
- package/dist/tools/analytics/getUsageAnalytics.d.ts.map +1 -0
- package/dist/tools/analytics/getUsageAnalytics.js +246 -0
- package/dist/tools/analytics/getUsageAnalytics.js.map +1 -0
- package/dist/tools/analytics/index.d.ts +5 -0
- package/dist/tools/analytics/index.d.ts.map +1 -0
- package/dist/tools/analytics/index.js +5 -0
- package/dist/tools/analytics/index.js.map +1 -0
- package/dist/tools/convention/analyzeComplexity.test.js +115 -115
- package/dist/tools/convention/getCodingGuide.d.ts +7 -0
- package/dist/tools/convention/getCodingGuide.d.ts.map +1 -0
- package/dist/tools/convention/getCodingGuide.js +69 -0
- package/dist/tools/convention/getCodingGuide.js.map +1 -0
- package/dist/tools/convention/validateCodeQuality.test.js +104 -104
- package/dist/tools/planning/analyzeRequirements.d.ts +9 -0
- package/dist/tools/planning/analyzeRequirements.d.ts.map +1 -0
- package/dist/tools/planning/analyzeRequirements.js +171 -0
- package/dist/tools/planning/analyzeRequirements.js.map +1 -0
- package/dist/tools/planning/createUserStories.d.ts +9 -0
- package/dist/tools/planning/createUserStories.d.ts.map +1 -0
- package/dist/tools/planning/createUserStories.js +124 -0
- package/dist/tools/planning/createUserStories.js.map +1 -0
- package/dist/tools/planning/featureRoadmap.d.ts +10 -0
- package/dist/tools/planning/featureRoadmap.d.ts.map +1 -0
- package/dist/tools/planning/featureRoadmap.js +207 -0
- package/dist/tools/planning/featureRoadmap.js.map +1 -0
- package/dist/tools/planning/generatePrd.d.ts +11 -0
- package/dist/tools/planning/generatePrd.d.ts.map +1 -0
- package/dist/tools/planning/generatePrd.js +161 -0
- package/dist/tools/planning/generatePrd.js.map +1 -0
- package/dist/tools/planning/index.d.ts +8 -0
- package/dist/tools/planning/index.d.ts.map +1 -0
- package/dist/tools/planning/index.js +8 -0
- package/dist/tools/planning/index.js.map +1 -0
- package/dist/tools/prompt/analyzePrompt.d.ts +7 -0
- package/dist/tools/prompt/analyzePrompt.d.ts.map +1 -0
- package/dist/tools/prompt/analyzePrompt.js +150 -0
- package/dist/tools/prompt/analyzePrompt.js.map +1 -0
- package/dist/tools/prompt/enhancePrompt.d.ts +8 -0
- package/dist/tools/prompt/enhancePrompt.d.ts.map +1 -0
- package/dist/tools/prompt/enhancePrompt.js +110 -0
- package/dist/tools/prompt/enhancePrompt.js.map +1 -0
- package/dist/tools/prompt/enhancePromptGemini.d.ts +8 -0
- package/dist/tools/prompt/enhancePromptGemini.d.ts.map +1 -0
- package/dist/tools/prompt/enhancePromptGemini.js +332 -0
- package/dist/tools/prompt/enhancePromptGemini.js.map +1 -0
- package/dist/tools/prompt/index.d.ts +7 -0
- package/dist/tools/prompt/index.d.ts.map +1 -0
- package/dist/tools/prompt/index.js +7 -0
- package/dist/tools/prompt/index.js.map +1 -0
- package/dist/tools/reasoning/applyReasoningFramework.d.ts +8 -0
- package/dist/tools/reasoning/applyReasoningFramework.d.ts.map +1 -0
- package/dist/tools/reasoning/applyReasoningFramework.js +266 -0
- package/dist/tools/reasoning/applyReasoningFramework.js.map +1 -0
- package/dist/tools/reasoning/index.d.ts +5 -0
- package/dist/tools/reasoning/index.d.ts.map +1 -0
- package/dist/tools/reasoning/index.js +5 -0
- package/dist/tools/reasoning/index.js.map +1 -0
- package/dist/tools/spec/prdParser.test.js +171 -171
- package/dist/tools/spec/specGenerator.js +169 -169
- package/dist/tools/spec/traceabilityMatrix.js +64 -64
- package/dist/tools/spec/traceabilityMatrix.test.js +28 -28
- package/dist/tools/thinking/analyzeProblem.d.ts +7 -0
- package/dist/tools/thinking/analyzeProblem.d.ts.map +1 -0
- package/dist/tools/thinking/analyzeProblem.js +55 -0
- package/dist/tools/thinking/analyzeProblem.js.map +1 -0
- package/dist/tools/thinking/breakDownProblem.d.ts +8 -0
- package/dist/tools/thinking/breakDownProblem.d.ts.map +1 -0
- package/dist/tools/thinking/breakDownProblem.js +145 -0
- package/dist/tools/thinking/breakDownProblem.js.map +1 -0
- package/dist/tools/thinking/createThinkingChain.d.ts +7 -0
- package/dist/tools/thinking/createThinkingChain.d.ts.map +1 -0
- package/dist/tools/thinking/createThinkingChain.js +44 -0
- package/dist/tools/thinking/createThinkingChain.js.map +1 -0
- package/dist/tools/thinking/formatAsPlan.d.ts +9 -0
- package/dist/tools/thinking/formatAsPlan.d.ts.map +1 -0
- package/dist/tools/thinking/formatAsPlan.js +78 -0
- package/dist/tools/thinking/formatAsPlan.js.map +1 -0
- package/dist/tools/thinking/index.d.ts +10 -0
- package/dist/tools/thinking/index.d.ts.map +1 -0
- package/dist/tools/thinking/index.js +10 -0
- package/dist/tools/thinking/index.js.map +1 -0
- package/dist/tools/thinking/stepByStepAnalysis.d.ts +8 -0
- package/dist/tools/thinking/stepByStepAnalysis.d.ts.map +1 -0
- package/dist/tools/thinking/stepByStepAnalysis.js +63 -0
- package/dist/tools/thinking/stepByStepAnalysis.js.map +1 -0
- package/dist/tools/thinking/thinkAloudProcess.d.ts +8 -0
- package/dist/tools/thinking/thinkAloudProcess.d.ts.map +1 -0
- package/dist/tools/thinking/thinkAloudProcess.js +80 -0
- package/dist/tools/thinking/thinkAloudProcess.js.map +1 -0
- package/hooks/hooks.json +222 -222
- package/hooks/scripts/code-check.js +22 -22
- package/hooks/scripts/code-review.js +22 -22
- package/hooks/scripts/complexity.js +22 -22
- package/hooks/scripts/compound.js +23 -23
- package/hooks/scripts/context-save.js +33 -33
- package/hooks/scripts/gemini-ui-gen.js +281 -281
- package/hooks/scripts/generate-brand-assets.js +474 -474
- package/hooks/scripts/hud-multiline.js +262 -262
- package/hooks/scripts/hud-status.js +291 -291
- package/hooks/scripts/keyword-detector.js +214 -214
- package/hooks/scripts/llm-orchestrate.js +171 -171
- package/hooks/scripts/post-edit.js +97 -97
- package/hooks/scripts/post-tool-verify.js +210 -210
- package/hooks/scripts/pre-tool-guard.js +125 -125
- package/hooks/scripts/recall.js +22 -22
- package/hooks/scripts/session-start.js +30 -30
- package/hooks/scripts/skill-injector.js +191 -191
- package/hooks/scripts/utils.js +97 -97
- package/languages/csharp-unity.md +515 -515
- package/languages/gdscript-godot.md +470 -470
- package/languages/ruby-rails.md +489 -489
- package/languages/typescript-angular.md +433 -433
- package/languages/typescript-astro.md +416 -416
- package/languages/typescript-electron.md +406 -406
- package/languages/typescript-nestjs.md +524 -524
- package/languages/typescript-svelte.md +407 -407
- package/languages/typescript-tauri.md +365 -365
- package/package.json +84 -84
- package/skills/brand-assets.md +141 -141
- package/skills/commerce-patterns.md +361 -361
- package/skills/context7-usage.md +102 -102
- package/skills/e2e-commerce.md +304 -304
- package/skills/frontend-design.md +92 -92
- package/skills/git-worktree.md +181 -181
- package/skills/parallel-research.md +77 -77
- package/skills/priority-todos.md +239 -239
- package/skills/seo-checklist.md +244 -244
- package/skills/tool-fallback.md +190 -190
- package/skills/vibe-capabilities.md +161 -161
- package/vibe/constitution.md +227 -227
- package/vibe/rules/core/communication-guide.md +98 -98
- package/vibe/rules/core/development-philosophy.md +52 -52
- package/vibe/rules/core/quick-start.md +102 -102
- package/vibe/rules/quality/bdd-contract-testing.md +393 -393
- package/vibe/rules/quality/checklist.md +276 -276
- package/vibe/rules/quality/testing-strategy.md +440 -440
- package/vibe/rules/standards/anti-patterns.md +541 -541
- package/vibe/rules/standards/code-structure.md +291 -291
- package/vibe/rules/standards/complexity-metrics.md +313 -313
- package/vibe/rules/standards/naming-conventions.md +198 -198
- package/vibe/setup.sh +31 -31
- package/vibe/templates/constitution-template.md +252 -252
- package/vibe/templates/contract-backend-template.md +526 -526
- package/vibe/templates/contract-frontend-template.md +599 -599
- package/vibe/templates/feature-template.md +96 -96
- package/vibe/templates/spec-template.md +221 -221
package/skills/e2e-commerce.md
CHANGED
|
@@ -1,304 +1,304 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: e2e-commerce
|
|
3
|
-
description: "E2E test scenarios for commerce checkout and payment flows"
|
|
4
|
-
triggers: [e2e commerce, checkout test, payment test, order flow test]
|
|
5
|
-
priority: 65
|
|
6
|
-
---
|
|
7
|
-
# E2E Commerce Test Scenarios
|
|
8
|
-
|
|
9
|
-
Playwright-based E2E testing for commerce checkout flows.
|
|
10
|
-
|
|
11
|
-
## When to Use
|
|
12
|
-
|
|
13
|
-
- After implementing checkout/payment features
|
|
14
|
-
- Before production deployment
|
|
15
|
-
- CI/CD pipeline quality gate
|
|
16
|
-
- Regression testing after changes
|
|
17
|
-
|
|
18
|
-
## Test Scenarios
|
|
19
|
-
|
|
20
|
-
### 1. Happy Path - Complete Checkout
|
|
21
|
-
|
|
22
|
-
```typescript
|
|
23
|
-
// tests/e2e/checkout.spec.ts
|
|
24
|
-
import { test, expect } from '@playwright/test';
|
|
25
|
-
|
|
26
|
-
test.describe('Checkout Flow', () => {
|
|
27
|
-
test('complete purchase - happy path', async ({ page }) => {
|
|
28
|
-
// 1. Add to cart
|
|
29
|
-
await page.goto('/products/test-product');
|
|
30
|
-
await page.click('[data-testid="add-to-cart"]');
|
|
31
|
-
await expect(page.locator('[data-testid="cart-count"]')).toHaveText('1');
|
|
32
|
-
|
|
33
|
-
// 2. Go to cart
|
|
34
|
-
await page.click('[data-testid="cart-icon"]');
|
|
35
|
-
await expect(page).toHaveURL('/cart');
|
|
36
|
-
|
|
37
|
-
// 3. Proceed to checkout
|
|
38
|
-
await page.click('[data-testid="checkout-button"]');
|
|
39
|
-
await expect(page).toHaveURL('/checkout');
|
|
40
|
-
|
|
41
|
-
// 4. Fill shipping info
|
|
42
|
-
await page.fill('[name="name"]', 'Test User');
|
|
43
|
-
await page.fill('[name="phone"]', '010-1234-5678');
|
|
44
|
-
await page.fill('[name="address"]', 'Test Address 123');
|
|
45
|
-
|
|
46
|
-
// 5. Select payment method
|
|
47
|
-
await page.click('[data-testid="payment-card"]');
|
|
48
|
-
|
|
49
|
-
// 6. Complete payment (sandbox/mock)
|
|
50
|
-
await page.click('[data-testid="pay-button"]');
|
|
51
|
-
|
|
52
|
-
// 7. Verify order complete
|
|
53
|
-
await expect(page).toHaveURL(/\/orders\/\w+/);
|
|
54
|
-
await expect(page.locator('[data-testid="order-status"]')).toHaveText('결제 완료');
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### 2. Stock Validation
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
test('prevent checkout when out of stock', async ({ page }) => {
|
|
63
|
-
// Setup: Product with stock = 1, another user reserves it
|
|
64
|
-
await page.goto('/products/low-stock-product');
|
|
65
|
-
await page.click('[data-testid="add-to-cart"]');
|
|
66
|
-
await page.goto('/checkout');
|
|
67
|
-
|
|
68
|
-
// Simulate stock depletion during checkout
|
|
69
|
-
await page.evaluate(async () => {
|
|
70
|
-
await fetch('/api/test/deplete-stock', { method: 'POST' });
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Attempt payment
|
|
74
|
-
await page.click('[data-testid="pay-button"]');
|
|
75
|
-
|
|
76
|
-
// Should show out of stock error
|
|
77
|
-
await expect(page.locator('[data-testid="error-message"]'))
|
|
78
|
-
.toContainText('재고가 부족합니다');
|
|
79
|
-
});
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### 3. Payment Failure Handling
|
|
83
|
-
|
|
84
|
-
```typescript
|
|
85
|
-
test('handle payment failure gracefully', async ({ page }) => {
|
|
86
|
-
await page.goto('/products/test-product');
|
|
87
|
-
await page.click('[data-testid="add-to-cart"]');
|
|
88
|
-
await page.goto('/checkout');
|
|
89
|
-
|
|
90
|
-
// Fill form
|
|
91
|
-
await page.fill('[name="name"]', 'Test User');
|
|
92
|
-
await page.fill('[name="address"]', 'Test Address');
|
|
93
|
-
|
|
94
|
-
// Use test card that triggers failure
|
|
95
|
-
await page.fill('[name="card-number"]', '4000000000000002'); // Decline card
|
|
96
|
-
await page.click('[data-testid="pay-button"]');
|
|
97
|
-
|
|
98
|
-
// Verify error handling
|
|
99
|
-
await expect(page.locator('[data-testid="payment-error"]'))
|
|
100
|
-
.toContainText('결제가 거절되었습니다');
|
|
101
|
-
|
|
102
|
-
// Stock should be released
|
|
103
|
-
await page.goto('/products/test-product');
|
|
104
|
-
await expect(page.locator('[data-testid="stock-status"]'))
|
|
105
|
-
.not.toContainText('품절');
|
|
106
|
-
});
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### 4. Duplicate Payment Prevention
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
test('prevent duplicate payment on double click', async ({ page }) => {
|
|
113
|
-
await page.goto('/checkout');
|
|
114
|
-
// Fill checkout form...
|
|
115
|
-
|
|
116
|
-
// Double click pay button rapidly
|
|
117
|
-
const payButton = page.locator('[data-testid="pay-button"]');
|
|
118
|
-
await Promise.all([
|
|
119
|
-
payButton.click(),
|
|
120
|
-
payButton.click(),
|
|
121
|
-
]);
|
|
122
|
-
|
|
123
|
-
// Wait for completion
|
|
124
|
-
await page.waitForURL(/\/orders\/\w+/);
|
|
125
|
-
|
|
126
|
-
// Verify only one order created
|
|
127
|
-
const orderId = page.url().split('/').pop();
|
|
128
|
-
const response = await page.request.get(`/api/orders?userId=test`);
|
|
129
|
-
const orders = await response.json();
|
|
130
|
-
|
|
131
|
-
expect(orders.filter(o => o.id === orderId)).toHaveLength(1);
|
|
132
|
-
});
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### 5. Coupon Application
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
test('apply coupon and verify discount', async ({ page }) => {
|
|
139
|
-
await page.goto('/products/test-product'); // Price: 10,000
|
|
140
|
-
await page.click('[data-testid="add-to-cart"]');
|
|
141
|
-
await page.goto('/checkout');
|
|
142
|
-
|
|
143
|
-
// Original price
|
|
144
|
-
await expect(page.locator('[data-testid="total-price"]'))
|
|
145
|
-
.toHaveText('10,000원');
|
|
146
|
-
|
|
147
|
-
// Apply 10% coupon
|
|
148
|
-
await page.fill('[name="coupon"]', 'DISCOUNT10');
|
|
149
|
-
await page.click('[data-testid="apply-coupon"]');
|
|
150
|
-
|
|
151
|
-
// Verify discount applied
|
|
152
|
-
await expect(page.locator('[data-testid="discount-amount"]'))
|
|
153
|
-
.toHaveText('-1,000원');
|
|
154
|
-
await expect(page.locator('[data-testid="total-price"]'))
|
|
155
|
-
.toHaveText('9,000원');
|
|
156
|
-
});
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### 6. Webhook Resilience
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
test('order completes even with webhook delay', async ({ page, request }) => {
|
|
163
|
-
// Configure webhook delay in test environment
|
|
164
|
-
await request.post('/api/test/configure-webhook', {
|
|
165
|
-
data: { delay: 5000 } // 5 second delay
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
// Complete checkout
|
|
169
|
-
await page.goto('/checkout');
|
|
170
|
-
// ... fill form
|
|
171
|
-
await page.click('[data-testid="pay-button"]');
|
|
172
|
-
|
|
173
|
-
// Should show processing state
|
|
174
|
-
await expect(page.locator('[data-testid="order-status"]'))
|
|
175
|
-
.toHaveText('처리 중');
|
|
176
|
-
|
|
177
|
-
// Wait for webhook
|
|
178
|
-
await page.waitForSelector('[data-testid="order-status"]:has-text("결제 완료")', {
|
|
179
|
-
timeout: 10000
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
## CLI Usage
|
|
185
|
-
|
|
186
|
-
```bash
|
|
187
|
-
# Run all commerce e2e tests
|
|
188
|
-
/vibe.utils --e2e commerce
|
|
189
|
-
|
|
190
|
-
# Run specific scenario
|
|
191
|
-
/vibe.utils --e2e checkout-flow
|
|
192
|
-
|
|
193
|
-
# Run with visual recording
|
|
194
|
-
/vibe.utils --e2e commerce --record
|
|
195
|
-
|
|
196
|
-
# Run against staging
|
|
197
|
-
/vibe.utils --e2e commerce --env staging
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
## Test Environment Setup
|
|
201
|
-
|
|
202
|
-
### Mock PG Server
|
|
203
|
-
```typescript
|
|
204
|
-
// tests/mocks/pg-server.ts
|
|
205
|
-
import { setupServer } from 'msw/node';
|
|
206
|
-
import { http, HttpResponse } from 'msw';
|
|
207
|
-
|
|
208
|
-
export const pgMockServer = setupServer(
|
|
209
|
-
// Success response
|
|
210
|
-
http.post('/payments/authorize', () => {
|
|
211
|
-
return HttpResponse.json({
|
|
212
|
-
success: true,
|
|
213
|
-
transactionId: `txn_${Date.now()}`,
|
|
214
|
-
status: 'AUTHORIZED',
|
|
215
|
-
});
|
|
216
|
-
}),
|
|
217
|
-
|
|
218
|
-
// Failure simulation
|
|
219
|
-
http.post('/payments/authorize', ({ request }) => {
|
|
220
|
-
const body = request.json();
|
|
221
|
-
if (body.cardNumber === '4000000000000002') {
|
|
222
|
-
return HttpResponse.json({
|
|
223
|
-
success: false,
|
|
224
|
-
error: 'CARD_DECLINED',
|
|
225
|
-
}, { status: 400 });
|
|
226
|
-
}
|
|
227
|
-
}),
|
|
228
|
-
);
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### Database Seeding
|
|
232
|
-
```typescript
|
|
233
|
-
// tests/fixtures/commerce.ts
|
|
234
|
-
export async function seedCommerceData(db: Database) {
|
|
235
|
-
// Create test products
|
|
236
|
-
await db.products.createMany([
|
|
237
|
-
{ id: 'test-product', name: 'Test Product', price: 10000, stock: 100 },
|
|
238
|
-
{ id: 'low-stock', name: 'Low Stock', price: 5000, stock: 1 },
|
|
239
|
-
]);
|
|
240
|
-
|
|
241
|
-
// Create test coupons
|
|
242
|
-
await db.coupons.create({
|
|
243
|
-
code: 'DISCOUNT10',
|
|
244
|
-
discountPercent: 10,
|
|
245
|
-
expiresAt: addDays(new Date(), 30),
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
## CI/CD Integration
|
|
251
|
-
|
|
252
|
-
```yaml
|
|
253
|
-
# .github/workflows/e2e.yml
|
|
254
|
-
name: E2E Commerce Tests
|
|
255
|
-
|
|
256
|
-
on:
|
|
257
|
-
pull_request:
|
|
258
|
-
paths:
|
|
259
|
-
- 'src/checkout/**'
|
|
260
|
-
- 'src/payment/**'
|
|
261
|
-
- 'src/cart/**'
|
|
262
|
-
|
|
263
|
-
jobs:
|
|
264
|
-
e2e:
|
|
265
|
-
runs-on: ubuntu-latest
|
|
266
|
-
steps:
|
|
267
|
-
- uses: actions/checkout@v4
|
|
268
|
-
|
|
269
|
-
- name: Install dependencies
|
|
270
|
-
run: npm ci
|
|
271
|
-
|
|
272
|
-
- name: Start test server
|
|
273
|
-
run: npm run start:test &
|
|
274
|
-
|
|
275
|
-
- name: Run E2E tests
|
|
276
|
-
run: npx playwright test tests/e2e/commerce/
|
|
277
|
-
|
|
278
|
-
- name: Upload test results
|
|
279
|
-
if: failure()
|
|
280
|
-
uses: actions/upload-artifact@v4
|
|
281
|
-
with:
|
|
282
|
-
name: playwright-report
|
|
283
|
-
path: playwright-report/
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
## Quality Checklist
|
|
287
|
-
|
|
288
|
-
### Must Pass (P0)
|
|
289
|
-
- [ ] Happy path checkout completes
|
|
290
|
-
- [ ] Payment failure releases stock
|
|
291
|
-
- [ ] Duplicate payment prevented
|
|
292
|
-
- [ ] Out of stock blocks checkout
|
|
293
|
-
|
|
294
|
-
### Should Pass (P1)
|
|
295
|
-
- [ ] Coupon calculation correct
|
|
296
|
-
- [ ] Webhook retry handled
|
|
297
|
-
- [ ] Cart merge on login works
|
|
298
|
-
- [ ] Partial refund processed
|
|
299
|
-
|
|
300
|
-
### Nice to Have (P2)
|
|
301
|
-
- [ ] Multiple payment methods
|
|
302
|
-
- [ ] Guest checkout flow
|
|
303
|
-
- [ ] Order cancellation
|
|
304
|
-
- [ ] Subscription renewal
|
|
1
|
+
---
|
|
2
|
+
name: e2e-commerce
|
|
3
|
+
description: "E2E test scenarios for commerce checkout and payment flows"
|
|
4
|
+
triggers: [e2e commerce, checkout test, payment test, order flow test]
|
|
5
|
+
priority: 65
|
|
6
|
+
---
|
|
7
|
+
# E2E Commerce Test Scenarios
|
|
8
|
+
|
|
9
|
+
Playwright-based E2E testing for commerce checkout flows.
|
|
10
|
+
|
|
11
|
+
## When to Use
|
|
12
|
+
|
|
13
|
+
- After implementing checkout/payment features
|
|
14
|
+
- Before production deployment
|
|
15
|
+
- CI/CD pipeline quality gate
|
|
16
|
+
- Regression testing after changes
|
|
17
|
+
|
|
18
|
+
## Test Scenarios
|
|
19
|
+
|
|
20
|
+
### 1. Happy Path - Complete Checkout
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// tests/e2e/checkout.spec.ts
|
|
24
|
+
import { test, expect } from '@playwright/test';
|
|
25
|
+
|
|
26
|
+
test.describe('Checkout Flow', () => {
|
|
27
|
+
test('complete purchase - happy path', async ({ page }) => {
|
|
28
|
+
// 1. Add to cart
|
|
29
|
+
await page.goto('/products/test-product');
|
|
30
|
+
await page.click('[data-testid="add-to-cart"]');
|
|
31
|
+
await expect(page.locator('[data-testid="cart-count"]')).toHaveText('1');
|
|
32
|
+
|
|
33
|
+
// 2. Go to cart
|
|
34
|
+
await page.click('[data-testid="cart-icon"]');
|
|
35
|
+
await expect(page).toHaveURL('/cart');
|
|
36
|
+
|
|
37
|
+
// 3. Proceed to checkout
|
|
38
|
+
await page.click('[data-testid="checkout-button"]');
|
|
39
|
+
await expect(page).toHaveURL('/checkout');
|
|
40
|
+
|
|
41
|
+
// 4. Fill shipping info
|
|
42
|
+
await page.fill('[name="name"]', 'Test User');
|
|
43
|
+
await page.fill('[name="phone"]', '010-1234-5678');
|
|
44
|
+
await page.fill('[name="address"]', 'Test Address 123');
|
|
45
|
+
|
|
46
|
+
// 5. Select payment method
|
|
47
|
+
await page.click('[data-testid="payment-card"]');
|
|
48
|
+
|
|
49
|
+
// 6. Complete payment (sandbox/mock)
|
|
50
|
+
await page.click('[data-testid="pay-button"]');
|
|
51
|
+
|
|
52
|
+
// 7. Verify order complete
|
|
53
|
+
await expect(page).toHaveURL(/\/orders\/\w+/);
|
|
54
|
+
await expect(page.locator('[data-testid="order-status"]')).toHaveText('결제 완료');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 2. Stock Validation
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
test('prevent checkout when out of stock', async ({ page }) => {
|
|
63
|
+
// Setup: Product with stock = 1, another user reserves it
|
|
64
|
+
await page.goto('/products/low-stock-product');
|
|
65
|
+
await page.click('[data-testid="add-to-cart"]');
|
|
66
|
+
await page.goto('/checkout');
|
|
67
|
+
|
|
68
|
+
// Simulate stock depletion during checkout
|
|
69
|
+
await page.evaluate(async () => {
|
|
70
|
+
await fetch('/api/test/deplete-stock', { method: 'POST' });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Attempt payment
|
|
74
|
+
await page.click('[data-testid="pay-button"]');
|
|
75
|
+
|
|
76
|
+
// Should show out of stock error
|
|
77
|
+
await expect(page.locator('[data-testid="error-message"]'))
|
|
78
|
+
.toContainText('재고가 부족합니다');
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 3. Payment Failure Handling
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
test('handle payment failure gracefully', async ({ page }) => {
|
|
86
|
+
await page.goto('/products/test-product');
|
|
87
|
+
await page.click('[data-testid="add-to-cart"]');
|
|
88
|
+
await page.goto('/checkout');
|
|
89
|
+
|
|
90
|
+
// Fill form
|
|
91
|
+
await page.fill('[name="name"]', 'Test User');
|
|
92
|
+
await page.fill('[name="address"]', 'Test Address');
|
|
93
|
+
|
|
94
|
+
// Use test card that triggers failure
|
|
95
|
+
await page.fill('[name="card-number"]', '4000000000000002'); // Decline card
|
|
96
|
+
await page.click('[data-testid="pay-button"]');
|
|
97
|
+
|
|
98
|
+
// Verify error handling
|
|
99
|
+
await expect(page.locator('[data-testid="payment-error"]'))
|
|
100
|
+
.toContainText('결제가 거절되었습니다');
|
|
101
|
+
|
|
102
|
+
// Stock should be released
|
|
103
|
+
await page.goto('/products/test-product');
|
|
104
|
+
await expect(page.locator('[data-testid="stock-status"]'))
|
|
105
|
+
.not.toContainText('품절');
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 4. Duplicate Payment Prevention
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
test('prevent duplicate payment on double click', async ({ page }) => {
|
|
113
|
+
await page.goto('/checkout');
|
|
114
|
+
// Fill checkout form...
|
|
115
|
+
|
|
116
|
+
// Double click pay button rapidly
|
|
117
|
+
const payButton = page.locator('[data-testid="pay-button"]');
|
|
118
|
+
await Promise.all([
|
|
119
|
+
payButton.click(),
|
|
120
|
+
payButton.click(),
|
|
121
|
+
]);
|
|
122
|
+
|
|
123
|
+
// Wait for completion
|
|
124
|
+
await page.waitForURL(/\/orders\/\w+/);
|
|
125
|
+
|
|
126
|
+
// Verify only one order created
|
|
127
|
+
const orderId = page.url().split('/').pop();
|
|
128
|
+
const response = await page.request.get(`/api/orders?userId=test`);
|
|
129
|
+
const orders = await response.json();
|
|
130
|
+
|
|
131
|
+
expect(orders.filter(o => o.id === orderId)).toHaveLength(1);
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 5. Coupon Application
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
test('apply coupon and verify discount', async ({ page }) => {
|
|
139
|
+
await page.goto('/products/test-product'); // Price: 10,000
|
|
140
|
+
await page.click('[data-testid="add-to-cart"]');
|
|
141
|
+
await page.goto('/checkout');
|
|
142
|
+
|
|
143
|
+
// Original price
|
|
144
|
+
await expect(page.locator('[data-testid="total-price"]'))
|
|
145
|
+
.toHaveText('10,000원');
|
|
146
|
+
|
|
147
|
+
// Apply 10% coupon
|
|
148
|
+
await page.fill('[name="coupon"]', 'DISCOUNT10');
|
|
149
|
+
await page.click('[data-testid="apply-coupon"]');
|
|
150
|
+
|
|
151
|
+
// Verify discount applied
|
|
152
|
+
await expect(page.locator('[data-testid="discount-amount"]'))
|
|
153
|
+
.toHaveText('-1,000원');
|
|
154
|
+
await expect(page.locator('[data-testid="total-price"]'))
|
|
155
|
+
.toHaveText('9,000원');
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 6. Webhook Resilience
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
test('order completes even with webhook delay', async ({ page, request }) => {
|
|
163
|
+
// Configure webhook delay in test environment
|
|
164
|
+
await request.post('/api/test/configure-webhook', {
|
|
165
|
+
data: { delay: 5000 } // 5 second delay
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Complete checkout
|
|
169
|
+
await page.goto('/checkout');
|
|
170
|
+
// ... fill form
|
|
171
|
+
await page.click('[data-testid="pay-button"]');
|
|
172
|
+
|
|
173
|
+
// Should show processing state
|
|
174
|
+
await expect(page.locator('[data-testid="order-status"]'))
|
|
175
|
+
.toHaveText('처리 중');
|
|
176
|
+
|
|
177
|
+
// Wait for webhook
|
|
178
|
+
await page.waitForSelector('[data-testid="order-status"]:has-text("결제 완료")', {
|
|
179
|
+
timeout: 10000
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## CLI Usage
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# Run all commerce e2e tests
|
|
188
|
+
/vibe.utils --e2e commerce
|
|
189
|
+
|
|
190
|
+
# Run specific scenario
|
|
191
|
+
/vibe.utils --e2e checkout-flow
|
|
192
|
+
|
|
193
|
+
# Run with visual recording
|
|
194
|
+
/vibe.utils --e2e commerce --record
|
|
195
|
+
|
|
196
|
+
# Run against staging
|
|
197
|
+
/vibe.utils --e2e commerce --env staging
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Test Environment Setup
|
|
201
|
+
|
|
202
|
+
### Mock PG Server
|
|
203
|
+
```typescript
|
|
204
|
+
// tests/mocks/pg-server.ts
|
|
205
|
+
import { setupServer } from 'msw/node';
|
|
206
|
+
import { http, HttpResponse } from 'msw';
|
|
207
|
+
|
|
208
|
+
export const pgMockServer = setupServer(
|
|
209
|
+
// Success response
|
|
210
|
+
http.post('/payments/authorize', () => {
|
|
211
|
+
return HttpResponse.json({
|
|
212
|
+
success: true,
|
|
213
|
+
transactionId: `txn_${Date.now()}`,
|
|
214
|
+
status: 'AUTHORIZED',
|
|
215
|
+
});
|
|
216
|
+
}),
|
|
217
|
+
|
|
218
|
+
// Failure simulation
|
|
219
|
+
http.post('/payments/authorize', ({ request }) => {
|
|
220
|
+
const body = request.json();
|
|
221
|
+
if (body.cardNumber === '4000000000000002') {
|
|
222
|
+
return HttpResponse.json({
|
|
223
|
+
success: false,
|
|
224
|
+
error: 'CARD_DECLINED',
|
|
225
|
+
}, { status: 400 });
|
|
226
|
+
}
|
|
227
|
+
}),
|
|
228
|
+
);
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Database Seeding
|
|
232
|
+
```typescript
|
|
233
|
+
// tests/fixtures/commerce.ts
|
|
234
|
+
export async function seedCommerceData(db: Database) {
|
|
235
|
+
// Create test products
|
|
236
|
+
await db.products.createMany([
|
|
237
|
+
{ id: 'test-product', name: 'Test Product', price: 10000, stock: 100 },
|
|
238
|
+
{ id: 'low-stock', name: 'Low Stock', price: 5000, stock: 1 },
|
|
239
|
+
]);
|
|
240
|
+
|
|
241
|
+
// Create test coupons
|
|
242
|
+
await db.coupons.create({
|
|
243
|
+
code: 'DISCOUNT10',
|
|
244
|
+
discountPercent: 10,
|
|
245
|
+
expiresAt: addDays(new Date(), 30),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## CI/CD Integration
|
|
251
|
+
|
|
252
|
+
```yaml
|
|
253
|
+
# .github/workflows/e2e.yml
|
|
254
|
+
name: E2E Commerce Tests
|
|
255
|
+
|
|
256
|
+
on:
|
|
257
|
+
pull_request:
|
|
258
|
+
paths:
|
|
259
|
+
- 'src/checkout/**'
|
|
260
|
+
- 'src/payment/**'
|
|
261
|
+
- 'src/cart/**'
|
|
262
|
+
|
|
263
|
+
jobs:
|
|
264
|
+
e2e:
|
|
265
|
+
runs-on: ubuntu-latest
|
|
266
|
+
steps:
|
|
267
|
+
- uses: actions/checkout@v4
|
|
268
|
+
|
|
269
|
+
- name: Install dependencies
|
|
270
|
+
run: npm ci
|
|
271
|
+
|
|
272
|
+
- name: Start test server
|
|
273
|
+
run: npm run start:test &
|
|
274
|
+
|
|
275
|
+
- name: Run E2E tests
|
|
276
|
+
run: npx playwright test tests/e2e/commerce/
|
|
277
|
+
|
|
278
|
+
- name: Upload test results
|
|
279
|
+
if: failure()
|
|
280
|
+
uses: actions/upload-artifact@v4
|
|
281
|
+
with:
|
|
282
|
+
name: playwright-report
|
|
283
|
+
path: playwright-report/
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Quality Checklist
|
|
287
|
+
|
|
288
|
+
### Must Pass (P0)
|
|
289
|
+
- [ ] Happy path checkout completes
|
|
290
|
+
- [ ] Payment failure releases stock
|
|
291
|
+
- [ ] Duplicate payment prevented
|
|
292
|
+
- [ ] Out of stock blocks checkout
|
|
293
|
+
|
|
294
|
+
### Should Pass (P1)
|
|
295
|
+
- [ ] Coupon calculation correct
|
|
296
|
+
- [ ] Webhook retry handled
|
|
297
|
+
- [ ] Cart merge on login works
|
|
298
|
+
- [ ] Partial refund processed
|
|
299
|
+
|
|
300
|
+
### Nice to Have (P2)
|
|
301
|
+
- [ ] Multiple payment methods
|
|
302
|
+
- [ ] Guest checkout flow
|
|
303
|
+
- [ ] Order cancellation
|
|
304
|
+
- [ ] Subscription renewal
|