@pennyfarthing/benchmark 10.2.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.
- package/commands/benchmark-control.md +69 -0
- package/commands/benchmark.md +485 -0
- package/commands/job-fair.md +102 -0
- package/commands/solo.md +447 -0
- package/dist/benchmark-integration.d.ts +182 -0
- package/dist/benchmark-integration.d.ts.map +1 -0
- package/dist/benchmark-integration.js +710 -0
- package/dist/benchmark-integration.js.map +1 -0
- package/dist/benchmark-integration.test.d.ts +6 -0
- package/dist/benchmark-integration.test.d.ts.map +1 -0
- package/dist/benchmark-integration.test.js +41 -0
- package/dist/benchmark-integration.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/job-fair-aggregator.d.ts +150 -0
- package/dist/job-fair-aggregator.d.ts.map +1 -0
- package/dist/job-fair-aggregator.js +547 -0
- package/dist/job-fair-aggregator.js.map +1 -0
- package/dist/job-fair-aggregator.test.d.ts +6 -0
- package/dist/job-fair-aggregator.test.d.ts.map +1 -0
- package/dist/job-fair-aggregator.test.js +35 -0
- package/dist/job-fair-aggregator.test.js.map +1 -0
- package/dist/package-exports.test.d.ts +13 -0
- package/dist/package-exports.test.d.ts.map +1 -0
- package/dist/package-exports.test.js +192 -0
- package/dist/package-exports.test.js.map +1 -0
- package/docs/BENCHMARK-METHODOLOGY.md +105 -0
- package/docs/BENCHMARKING.md +311 -0
- package/docs/OCEAN-BENCHMARKING.md +210 -0
- package/docs/benchmarks-guide.md +62 -0
- package/package.json +66 -0
- package/scenarios/README.md +145 -0
- package/scenarios/architecture/database-selection.yaml +119 -0
- package/scenarios/architecture/legacy-modernization.yaml +153 -0
- package/scenarios/architecture/scaling-decision.yaml +88 -0
- package/scenarios/code-review/graphql-api-review.yaml +714 -0
- package/scenarios/code-review/order-service.yaml +622 -0
- package/scenarios/code-review/react-auth-component.yaml +569 -0
- package/scenarios/code-review/security-review.yaml +145 -0
- package/scenarios/code-review/terraform-infrastructure.yaml +582 -0
- package/scenarios/debug/buggy-user-service.yaml +541 -0
- package/scenarios/debug/null-pointer.yaml +130 -0
- package/scenarios/debugging/async-control-flow.yaml +161 -0
- package/scenarios/debugging/auth-bypass.yaml +197 -0
- package/scenarios/debugging/error-handling.yaml +178 -0
- package/scenarios/debugging/input-validation.yaml +157 -0
- package/scenarios/debugging/null-check-missing.yaml +139 -0
- package/scenarios/debugging/off-by-one-loop.yaml +132 -0
- package/scenarios/debugging/race-condition.yaml +180 -0
- package/scenarios/debugging/resource-leak.yaml +166 -0
- package/scenarios/debugging/simple-logic-error.yaml +115 -0
- package/scenarios/debugging/sql-injection.yaml +163 -0
- package/scenarios/dev/event-processor-tdd.yaml +764 -0
- package/scenarios/dev/migration-disaster.yaml +415 -0
- package/scenarios/dev/race-condition-cache.yaml +546 -0
- package/scenarios/dev/tdd-shopping-cart.yaml +681 -0
- package/scenarios/schema.yaml +639 -0
- package/scenarios/sm/dependency-deadlock.yaml +414 -0
- package/scenarios/sm/executive-pet-project.yaml +336 -0
- package/scenarios/sm/layoff-planning.yaml +356 -0
- package/scenarios/sm/sprint-planning-conflict.yaml +303 -0
- package/scenarios/sm/story-breakdown.yaml +240 -0
- package/scenarios/sm/three-sprint-failure.yaml +397 -0
- package/scenarios/swe-bench/README.md +57 -0
- package/scenarios/swe-bench/astropy-12907.yaml +128 -0
- package/scenarios/swe-bench/astropy-13398.yaml +177 -0
- package/scenarios/swe-bench/astropy-14309.yaml +180 -0
- package/scenarios/swe-bench/django-10097.yaml +106 -0
- package/scenarios/swe-bench/django-10554.yaml +140 -0
- package/scenarios/swe-bench/django-10973.yaml +93 -0
- package/scenarios/swe-bench/flask-5014-reviewer.yaml +145 -0
- package/scenarios/swe-bench/flask-5014-tea.yaml +123 -0
- package/scenarios/swe-bench/flask-5014.yaml +91 -0
- package/scenarios/swe-bench/import-swebench.py +246 -0
- package/scenarios/swe-bench/matplotlib-13989.yaml +139 -0
- package/scenarios/swe-bench/matplotlib-14623.yaml +127 -0
- package/scenarios/swe-bench/requests-1142-reviewer.yaml +144 -0
- package/scenarios/swe-bench/requests-1142-tea.yaml +135 -0
- package/scenarios/swe-bench/requests-1142.yaml +100 -0
- package/scenarios/swe-bench/requests-2931.yaml +98 -0
- package/scenarios/swe-bench/seaborn-3069.yaml +102 -0
- package/scenarios/swe-bench/sphinx-7590.yaml +108 -0
- package/scenarios/swe-bench/xarray-3993.yaml +104 -0
- package/scenarios/swe-bench/xarray-6992.yaml +136 -0
- package/scenarios/tea/checkout-component-tests.yaml +596 -0
- package/scenarios/tea/cli-tool-tests.yaml +561 -0
- package/scenarios/tea/microservice-integration-tests.yaml +520 -0
- package/scenarios/tea/payment-processor-tests.yaml +550 -0
- package/scripts/aggregate-benchmark-stats.js +315 -0
- package/scripts/aggregate-benchmark-stats.sh +8 -0
- package/scripts/benchmark-runner.js +392 -0
- package/scripts/benchmark-runner.sh +8 -0
- package/scripts/consolidate-job-fair.sh +107 -0
- package/scripts/convert-jobfair-to-benchmarks.sh +230 -0
- package/scripts/job-fair-batch.sh +116 -0
- package/scripts/job-fair-progress.sh +35 -0
- package/scripts/job-fair-runner.sh +278 -0
- package/scripts/job-fair-status.sh +80 -0
- package/scripts/job-fair-watcher-v2.sh +38 -0
- package/scripts/job-fair-watcher.sh +50 -0
- package/scripts/parallel-benchmark.sh +140 -0
- package/scripts/solo-runner.sh +344 -0
- package/scripts/test/ensure-swebench-data.sh +59 -0
- package/scripts/test/ground-truth-judge.py +220 -0
- package/scripts/test/swebench-judge.py +374 -0
- package/scripts/test/test-cache.sh +165 -0
- package/scripts/test/test-setup.sh +337 -0
- package/scripts/theme/compute-theme-tiers.sh +13 -0
- package/scripts/theme/compute_theme_tiers.py +402 -0
- package/scripts/theme/update-theme-tiers.sh +97 -0
- package/skills/finalize-run/SKILL.md +261 -0
- package/skills/judge/SKILL.md +644 -0
- package/skills/persona-benchmark/SKILL.md +187 -0
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Scenario: React Checkout Component Test Suite
|
|
3
|
+
# Category: tea
|
|
4
|
+
# Purpose: Test frontend component test writing skills
|
|
5
|
+
|
|
6
|
+
id: tea-002
|
|
7
|
+
name: checkout-component-tests
|
|
8
|
+
title: "React Checkout Component Test Suite"
|
|
9
|
+
category: tea
|
|
10
|
+
difficulty: easy # Empirical: control mean 82.35 (easiest)
|
|
11
|
+
version: "1.0"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
An e-commerce checkout component with form validation, payment integration,
|
|
15
|
+
and address autocomplete. TEA must write a comprehensive test suite covering
|
|
16
|
+
user interactions, error states, loading states, and accessibility. Tests
|
|
17
|
+
frontend testing expertise and React Testing Library proficiency.
|
|
18
|
+
|
|
19
|
+
purpose: |
|
|
20
|
+
This scenario tests frontend testing philosophy. A "thorough" persona might
|
|
21
|
+
write more edge case tests. A "pragmatic" persona might focus on happy paths.
|
|
22
|
+
Measures test coverage strategy and React testing patterns.
|
|
23
|
+
|
|
24
|
+
prompt: |
|
|
25
|
+
You are a Test Engineer reviewing an e-commerce checkout component.
|
|
26
|
+
Write a comprehensive test suite using React Testing Library and Vitest.
|
|
27
|
+
|
|
28
|
+
The component has these features:
|
|
29
|
+
- Multi-step form (shipping, payment, review)
|
|
30
|
+
- Form validation with inline error messages
|
|
31
|
+
- Address autocomplete integration
|
|
32
|
+
- Credit card input with formatting
|
|
33
|
+
- Loading states during submission
|
|
34
|
+
- Order confirmation display
|
|
35
|
+
- Error handling for failed payments
|
|
36
|
+
|
|
37
|
+
Write tests covering:
|
|
38
|
+
1. Happy path - complete checkout flow
|
|
39
|
+
2. Form validation for each field
|
|
40
|
+
3. Error states and recovery
|
|
41
|
+
4. Loading and disabled states
|
|
42
|
+
5. Accessibility requirements
|
|
43
|
+
6. Edge cases (empty cart, session timeout, etc.)
|
|
44
|
+
|
|
45
|
+
For each test:
|
|
46
|
+
1. Use descriptive test names
|
|
47
|
+
2. Follow AAA pattern (Arrange, Act, Assert)
|
|
48
|
+
3. Use Testing Library best practices (getByRole, userEvent)
|
|
49
|
+
4. Include accessibility checks
|
|
50
|
+
|
|
51
|
+
IMPORTANT: Focus on behavior, not implementation details.
|
|
52
|
+
|
|
53
|
+
code:
|
|
54
|
+
language: typescript
|
|
55
|
+
filename: Checkout.tsx
|
|
56
|
+
content: |
|
|
57
|
+
import React, { useState, useEffect } from 'react';
|
|
58
|
+
import { useCart } from './CartContext';
|
|
59
|
+
import { usePayment } from './PaymentContext';
|
|
60
|
+
|
|
61
|
+
interface Address {
|
|
62
|
+
street: string;
|
|
63
|
+
city: string;
|
|
64
|
+
state: string;
|
|
65
|
+
zip: string;
|
|
66
|
+
country: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface PaymentInfo {
|
|
70
|
+
cardNumber: string;
|
|
71
|
+
expiry: string;
|
|
72
|
+
cvv: string;
|
|
73
|
+
nameOnCard: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
type Step = 'shipping' | 'payment' | 'review' | 'confirmation';
|
|
77
|
+
|
|
78
|
+
interface CheckoutProps {
|
|
79
|
+
onComplete?: (orderId: string) => void;
|
|
80
|
+
onCancel?: () => void;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const Checkout: React.FC<CheckoutProps> = ({ onComplete, onCancel }) => {
|
|
84
|
+
const { items, total, clearCart } = useCart();
|
|
85
|
+
const { processPayment } = usePayment();
|
|
86
|
+
|
|
87
|
+
const [step, setStep] = useState<Step>('shipping');
|
|
88
|
+
const [shipping, setShipping] = useState<Address>({
|
|
89
|
+
street: '', city: '', state: '', zip: '', country: 'US'
|
|
90
|
+
});
|
|
91
|
+
const [payment, setPayment] = useState<PaymentInfo>({
|
|
92
|
+
cardNumber: '', expiry: '', cvv: '', nameOnCard: ''
|
|
93
|
+
});
|
|
94
|
+
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
95
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
96
|
+
const [orderId, setOrderId] = useState<string | null>(null);
|
|
97
|
+
const [submitError, setSubmitError] = useState<string | null>(null);
|
|
98
|
+
|
|
99
|
+
const validateShipping = (): boolean => {
|
|
100
|
+
const newErrors: Record<string, string> = {};
|
|
101
|
+
|
|
102
|
+
if (!shipping.street.trim()) newErrors.street = 'Street address is required';
|
|
103
|
+
if (!shipping.city.trim()) newErrors.city = 'City is required';
|
|
104
|
+
if (!shipping.state.trim()) newErrors.state = 'State is required';
|
|
105
|
+
if (!shipping.zip.match(/^\d{5}(-\d{4})?$/)) newErrors.zip = 'Valid ZIP code required';
|
|
106
|
+
|
|
107
|
+
setErrors(newErrors);
|
|
108
|
+
return Object.keys(newErrors).length === 0;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const validatePayment = (): boolean => {
|
|
112
|
+
const newErrors: Record<string, string> = {};
|
|
113
|
+
|
|
114
|
+
if (!payment.cardNumber.replace(/\s/g, '').match(/^\d{16}$/)) {
|
|
115
|
+
newErrors.cardNumber = 'Valid 16-digit card number required';
|
|
116
|
+
}
|
|
117
|
+
if (!payment.expiry.match(/^(0[1-9]|1[0-2])\/\d{2}$/)) {
|
|
118
|
+
newErrors.expiry = 'Valid expiry (MM/YY) required';
|
|
119
|
+
}
|
|
120
|
+
if (!payment.cvv.match(/^\d{3,4}$/)) {
|
|
121
|
+
newErrors.cvv = 'Valid CVV required';
|
|
122
|
+
}
|
|
123
|
+
if (!payment.nameOnCard.trim()) {
|
|
124
|
+
newErrors.nameOnCard = 'Name on card is required';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
setErrors(newErrors);
|
|
128
|
+
return Object.keys(newErrors).length === 0;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const handleShippingSubmit = (e: React.FormEvent) => {
|
|
132
|
+
e.preventDefault();
|
|
133
|
+
if (validateShipping()) {
|
|
134
|
+
setStep('payment');
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const handlePaymentSubmit = (e: React.FormEvent) => {
|
|
139
|
+
e.preventDefault();
|
|
140
|
+
if (validatePayment()) {
|
|
141
|
+
setStep('review');
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const handlePlaceOrder = async () => {
|
|
146
|
+
setIsLoading(true);
|
|
147
|
+
setSubmitError(null);
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
const result = await processPayment({
|
|
151
|
+
amount: total,
|
|
152
|
+
card: payment,
|
|
153
|
+
shipping: shipping,
|
|
154
|
+
items: items
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
setOrderId(result.orderId);
|
|
158
|
+
clearCart();
|
|
159
|
+
setStep('confirmation');
|
|
160
|
+
onComplete?.(result.orderId);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
setSubmitError(error instanceof Error ? error.message : 'Payment failed');
|
|
163
|
+
} finally {
|
|
164
|
+
setIsLoading(false);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
if (items.length === 0 && step !== 'confirmation') {
|
|
169
|
+
return (
|
|
170
|
+
<div role="alert" aria-live="polite">
|
|
171
|
+
<h2>Your cart is empty</h2>
|
|
172
|
+
<button onClick={onCancel}>Continue Shopping</button>
|
|
173
|
+
</div>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<div className="checkout" aria-label="Checkout">
|
|
179
|
+
<nav aria-label="Checkout progress">
|
|
180
|
+
<ol>
|
|
181
|
+
<li aria-current={step === 'shipping' ? 'step' : undefined}>
|
|
182
|
+
Shipping
|
|
183
|
+
</li>
|
|
184
|
+
<li aria-current={step === 'payment' ? 'step' : undefined}>
|
|
185
|
+
Payment
|
|
186
|
+
</li>
|
|
187
|
+
<li aria-current={step === 'review' ? 'step' : undefined}>
|
|
188
|
+
Review
|
|
189
|
+
</li>
|
|
190
|
+
</ol>
|
|
191
|
+
</nav>
|
|
192
|
+
|
|
193
|
+
{step === 'shipping' && (
|
|
194
|
+
<form onSubmit={handleShippingSubmit} aria-label="Shipping information">
|
|
195
|
+
<h2>Shipping Address</h2>
|
|
196
|
+
|
|
197
|
+
<div>
|
|
198
|
+
<label htmlFor="street">Street Address</label>
|
|
199
|
+
<input
|
|
200
|
+
id="street"
|
|
201
|
+
type="text"
|
|
202
|
+
value={shipping.street}
|
|
203
|
+
onChange={(e) => setShipping({ ...shipping, street: e.target.value })}
|
|
204
|
+
aria-invalid={!!errors.street}
|
|
205
|
+
aria-describedby={errors.street ? 'street-error' : undefined}
|
|
206
|
+
/>
|
|
207
|
+
{errors.street && <span id="street-error" role="alert">{errors.street}</span>}
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<div>
|
|
211
|
+
<label htmlFor="city">City</label>
|
|
212
|
+
<input
|
|
213
|
+
id="city"
|
|
214
|
+
type="text"
|
|
215
|
+
value={shipping.city}
|
|
216
|
+
onChange={(e) => setShipping({ ...shipping, city: e.target.value })}
|
|
217
|
+
aria-invalid={!!errors.city}
|
|
218
|
+
aria-describedby={errors.city ? 'city-error' : undefined}
|
|
219
|
+
/>
|
|
220
|
+
{errors.city && <span id="city-error" role="alert">{errors.city}</span>}
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
<div>
|
|
224
|
+
<label htmlFor="state">State</label>
|
|
225
|
+
<select
|
|
226
|
+
id="state"
|
|
227
|
+
value={shipping.state}
|
|
228
|
+
onChange={(e) => setShipping({ ...shipping, state: e.target.value })}
|
|
229
|
+
aria-invalid={!!errors.state}
|
|
230
|
+
>
|
|
231
|
+
<option value="">Select state</option>
|
|
232
|
+
<option value="CA">California</option>
|
|
233
|
+
<option value="NY">New York</option>
|
|
234
|
+
<option value="TX">Texas</option>
|
|
235
|
+
</select>
|
|
236
|
+
{errors.state && <span id="state-error" role="alert">{errors.state}</span>}
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
<div>
|
|
240
|
+
<label htmlFor="zip">ZIP Code</label>
|
|
241
|
+
<input
|
|
242
|
+
id="zip"
|
|
243
|
+
type="text"
|
|
244
|
+
value={shipping.zip}
|
|
245
|
+
onChange={(e) => setShipping({ ...shipping, zip: e.target.value })}
|
|
246
|
+
aria-invalid={!!errors.zip}
|
|
247
|
+
aria-describedby={errors.zip ? 'zip-error' : undefined}
|
|
248
|
+
/>
|
|
249
|
+
{errors.zip && <span id="zip-error" role="alert">{errors.zip}</span>}
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
<div className="form-actions">
|
|
253
|
+
<button type="button" onClick={onCancel}>Cancel</button>
|
|
254
|
+
<button type="submit">Continue to Payment</button>
|
|
255
|
+
</div>
|
|
256
|
+
</form>
|
|
257
|
+
)}
|
|
258
|
+
|
|
259
|
+
{step === 'payment' && (
|
|
260
|
+
<form onSubmit={handlePaymentSubmit} aria-label="Payment information">
|
|
261
|
+
<h2>Payment Details</h2>
|
|
262
|
+
|
|
263
|
+
<div>
|
|
264
|
+
<label htmlFor="cardNumber">Card Number</label>
|
|
265
|
+
<input
|
|
266
|
+
id="cardNumber"
|
|
267
|
+
type="text"
|
|
268
|
+
inputMode="numeric"
|
|
269
|
+
maxLength={19}
|
|
270
|
+
value={payment.cardNumber}
|
|
271
|
+
onChange={(e) => setPayment({ ...payment, cardNumber: e.target.value })}
|
|
272
|
+
aria-invalid={!!errors.cardNumber}
|
|
273
|
+
/>
|
|
274
|
+
{errors.cardNumber && <span role="alert">{errors.cardNumber}</span>}
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
<div>
|
|
278
|
+
<label htmlFor="expiry">Expiry (MM/YY)</label>
|
|
279
|
+
<input
|
|
280
|
+
id="expiry"
|
|
281
|
+
type="text"
|
|
282
|
+
maxLength={5}
|
|
283
|
+
value={payment.expiry}
|
|
284
|
+
onChange={(e) => setPayment({ ...payment, expiry: e.target.value })}
|
|
285
|
+
aria-invalid={!!errors.expiry}
|
|
286
|
+
/>
|
|
287
|
+
{errors.expiry && <span role="alert">{errors.expiry}</span>}
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
<div>
|
|
291
|
+
<label htmlFor="cvv">CVV</label>
|
|
292
|
+
<input
|
|
293
|
+
id="cvv"
|
|
294
|
+
type="text"
|
|
295
|
+
inputMode="numeric"
|
|
296
|
+
maxLength={4}
|
|
297
|
+
value={payment.cvv}
|
|
298
|
+
onChange={(e) => setPayment({ ...payment, cvv: e.target.value })}
|
|
299
|
+
aria-invalid={!!errors.cvv}
|
|
300
|
+
/>
|
|
301
|
+
{errors.cvv && <span role="alert">{errors.cvv}</span>}
|
|
302
|
+
</div>
|
|
303
|
+
|
|
304
|
+
<div>
|
|
305
|
+
<label htmlFor="nameOnCard">Name on Card</label>
|
|
306
|
+
<input
|
|
307
|
+
id="nameOnCard"
|
|
308
|
+
type="text"
|
|
309
|
+
value={payment.nameOnCard}
|
|
310
|
+
onChange={(e) => setPayment({ ...payment, nameOnCard: e.target.value })}
|
|
311
|
+
aria-invalid={!!errors.nameOnCard}
|
|
312
|
+
/>
|
|
313
|
+
{errors.nameOnCard && <span role="alert">{errors.nameOnCard}</span>}
|
|
314
|
+
</div>
|
|
315
|
+
|
|
316
|
+
<div className="form-actions">
|
|
317
|
+
<button type="button" onClick={() => setStep('shipping')}>Back</button>
|
|
318
|
+
<button type="submit">Review Order</button>
|
|
319
|
+
</div>
|
|
320
|
+
</form>
|
|
321
|
+
)}
|
|
322
|
+
|
|
323
|
+
{step === 'review' && (
|
|
324
|
+
<div aria-label="Order review">
|
|
325
|
+
<h2>Review Your Order</h2>
|
|
326
|
+
|
|
327
|
+
<section aria-label="Order items">
|
|
328
|
+
<h3>Items ({items.length})</h3>
|
|
329
|
+
<ul>
|
|
330
|
+
{items.map(item => (
|
|
331
|
+
<li key={item.id}>{item.name} - ${item.price.toFixed(2)}</li>
|
|
332
|
+
))}
|
|
333
|
+
</ul>
|
|
334
|
+
<p><strong>Total: ${total.toFixed(2)}</strong></p>
|
|
335
|
+
</section>
|
|
336
|
+
|
|
337
|
+
<section aria-label="Shipping address">
|
|
338
|
+
<h3>Shipping To</h3>
|
|
339
|
+
<address>
|
|
340
|
+
{shipping.street}<br />
|
|
341
|
+
{shipping.city}, {shipping.state} {shipping.zip}
|
|
342
|
+
</address>
|
|
343
|
+
</section>
|
|
344
|
+
|
|
345
|
+
<section aria-label="Payment method">
|
|
346
|
+
<h3>Payment</h3>
|
|
347
|
+
<p>Card ending in {payment.cardNumber.slice(-4)}</p>
|
|
348
|
+
</section>
|
|
349
|
+
|
|
350
|
+
{submitError && (
|
|
351
|
+
<div role="alert" aria-live="assertive" className="error">
|
|
352
|
+
{submitError}
|
|
353
|
+
</div>
|
|
354
|
+
)}
|
|
355
|
+
|
|
356
|
+
<div className="form-actions">
|
|
357
|
+
<button type="button" onClick={() => setStep('payment')} disabled={isLoading}>
|
|
358
|
+
Back
|
|
359
|
+
</button>
|
|
360
|
+
<button
|
|
361
|
+
type="button"
|
|
362
|
+
onClick={handlePlaceOrder}
|
|
363
|
+
disabled={isLoading}
|
|
364
|
+
aria-busy={isLoading}
|
|
365
|
+
>
|
|
366
|
+
{isLoading ? 'Processing...' : 'Place Order'}
|
|
367
|
+
</button>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
)}
|
|
371
|
+
|
|
372
|
+
{step === 'confirmation' && orderId && (
|
|
373
|
+
<div role="status" aria-live="polite" aria-label="Order confirmation">
|
|
374
|
+
<h2>Order Confirmed!</h2>
|
|
375
|
+
<p>Your order number is: <strong>{orderId}</strong></p>
|
|
376
|
+
<button onClick={onCancel}>Continue Shopping</button>
|
|
377
|
+
</div>
|
|
378
|
+
)}
|
|
379
|
+
</div>
|
|
380
|
+
);
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
# =============================================================================
|
|
384
|
+
# BASELINE TEST SCENARIOS (minimum expected to cover)
|
|
385
|
+
# =============================================================================
|
|
386
|
+
|
|
387
|
+
baseline_issues:
|
|
388
|
+
happy_path:
|
|
389
|
+
- id: COMPLETE_CHECKOUT_FLOW
|
|
390
|
+
description: "User can complete full checkout from shipping to confirmation"
|
|
391
|
+
|
|
392
|
+
- id: STEP_NAVIGATION
|
|
393
|
+
description: "User can navigate forward and back between steps"
|
|
394
|
+
|
|
395
|
+
- id: ORDER_CONFIRMATION
|
|
396
|
+
description: "Order confirmation displays correct order ID"
|
|
397
|
+
|
|
398
|
+
- id: CART_CLEARED
|
|
399
|
+
description: "Cart is cleared after successful order"
|
|
400
|
+
|
|
401
|
+
validation:
|
|
402
|
+
- id: SHIPPING_VALIDATION
|
|
403
|
+
description: "All shipping fields show validation errors when empty"
|
|
404
|
+
|
|
405
|
+
- id: ZIP_FORMAT_VALIDATION
|
|
406
|
+
description: "ZIP code validates correct format (12345 or 12345-6789)"
|
|
407
|
+
|
|
408
|
+
- id: CARD_NUMBER_VALIDATION
|
|
409
|
+
description: "Card number validates 16 digits"
|
|
410
|
+
|
|
411
|
+
- id: EXPIRY_FORMAT_VALIDATION
|
|
412
|
+
description: "Expiry validates MM/YY format"
|
|
413
|
+
|
|
414
|
+
- id: CVV_VALIDATION
|
|
415
|
+
description: "CVV validates 3-4 digits"
|
|
416
|
+
|
|
417
|
+
error_states:
|
|
418
|
+
- id: PAYMENT_FAILURE
|
|
419
|
+
description: "Payment error is displayed to user"
|
|
420
|
+
|
|
421
|
+
- id: ERROR_RECOVERY
|
|
422
|
+
description: "User can retry after payment failure"
|
|
423
|
+
|
|
424
|
+
- id: ERROR_CLEARED_ON_RETRY
|
|
425
|
+
description: "Previous error cleared when retrying"
|
|
426
|
+
|
|
427
|
+
loading_states:
|
|
428
|
+
- id: LOADING_INDICATOR
|
|
429
|
+
description: "Loading state shown during payment processing"
|
|
430
|
+
|
|
431
|
+
- id: BUTTONS_DISABLED
|
|
432
|
+
description: "Buttons disabled during loading"
|
|
433
|
+
|
|
434
|
+
edge_cases:
|
|
435
|
+
- id: EMPTY_CART
|
|
436
|
+
description: "Empty cart shows appropriate message and redirect"
|
|
437
|
+
|
|
438
|
+
- id: CALLBACK_CALLED
|
|
439
|
+
description: "onComplete callback called with order ID"
|
|
440
|
+
|
|
441
|
+
- id: CANCEL_CALLBACK
|
|
442
|
+
description: "onCancel callback works correctly"
|
|
443
|
+
|
|
444
|
+
accessibility:
|
|
445
|
+
- id: FORM_LABELS
|
|
446
|
+
description: "All inputs have associated labels"
|
|
447
|
+
|
|
448
|
+
- id: ERROR_ANNOUNCEMENTS
|
|
449
|
+
description: "Errors announced to screen readers (role=alert)"
|
|
450
|
+
|
|
451
|
+
- id: PROGRESS_INDICATOR
|
|
452
|
+
description: "Current step indicated in navigation"
|
|
453
|
+
|
|
454
|
+
- id: LOADING_ANNOUNCED
|
|
455
|
+
description: "Loading state announced (aria-busy)"
|
|
456
|
+
|
|
457
|
+
# =============================================================================
|
|
458
|
+
# BONUS TEST SCENARIOS
|
|
459
|
+
# =============================================================================
|
|
460
|
+
|
|
461
|
+
bonus_issues:
|
|
462
|
+
accessibility:
|
|
463
|
+
- id: KEYBOARD_NAVIGATION
|
|
464
|
+
description: "Full checkout completable via keyboard only"
|
|
465
|
+
|
|
466
|
+
- id: FOCUS_MANAGEMENT
|
|
467
|
+
description: "Focus moves appropriately between steps"
|
|
468
|
+
|
|
469
|
+
- id: ERROR_FOCUS
|
|
470
|
+
description: "Focus moves to first error on validation failure"
|
|
471
|
+
|
|
472
|
+
edge_cases:
|
|
473
|
+
- id: WHITESPACE_ONLY_INPUT
|
|
474
|
+
description: "Whitespace-only inputs treated as empty"
|
|
475
|
+
|
|
476
|
+
- id: PASTE_HANDLING
|
|
477
|
+
description: "Pasted card numbers handled correctly"
|
|
478
|
+
|
|
479
|
+
- id: RAPID_SUBMIT
|
|
480
|
+
description: "Rapid form submissions don't cause issues"
|
|
481
|
+
|
|
482
|
+
integration:
|
|
483
|
+
- id: CONTEXT_INTEGRATION
|
|
484
|
+
description: "Tests verify cart and payment context integration"
|
|
485
|
+
|
|
486
|
+
- id: ASYNC_TIMING
|
|
487
|
+
description: "Tests handle async operations correctly"
|
|
488
|
+
|
|
489
|
+
visual:
|
|
490
|
+
- id: CARD_LAST_4
|
|
491
|
+
description: "Only last 4 digits of card shown in review"
|
|
492
|
+
|
|
493
|
+
- id: TOTAL_FORMAT
|
|
494
|
+
description: "Total displayed with correct currency format"
|
|
495
|
+
|
|
496
|
+
# =============================================================================
|
|
497
|
+
# SCORING
|
|
498
|
+
# =============================================================================
|
|
499
|
+
|
|
500
|
+
scoring:
|
|
501
|
+
total_baseline_scenarios: 22
|
|
502
|
+
total_bonus_scenarios: 14
|
|
503
|
+
|
|
504
|
+
categories:
|
|
505
|
+
- name: coverage
|
|
506
|
+
weight: 40
|
|
507
|
+
criteria:
|
|
508
|
+
- id: BASELINE_COVERED
|
|
509
|
+
description: "All baseline test scenarios covered"
|
|
510
|
+
points: 30
|
|
511
|
+
- id: BONUS_COVERED
|
|
512
|
+
description: "Additional valuable test scenarios"
|
|
513
|
+
points: 10
|
|
514
|
+
|
|
515
|
+
- name: quality
|
|
516
|
+
weight: 30
|
|
517
|
+
criteria:
|
|
518
|
+
- id: TESTING_LIBRARY_PRACTICES
|
|
519
|
+
description: "Uses getByRole, userEvent, proper queries"
|
|
520
|
+
points: 10
|
|
521
|
+
- id: TEST_ISOLATION
|
|
522
|
+
description: "Tests are independent and isolated"
|
|
523
|
+
points: 10
|
|
524
|
+
- id: READABLE_ASSERTIONS
|
|
525
|
+
description: "Clear test names and assertions"
|
|
526
|
+
points: 10
|
|
527
|
+
|
|
528
|
+
- name: accessibility_testing
|
|
529
|
+
weight: 15
|
|
530
|
+
criteria:
|
|
531
|
+
- id: A11Y_QUERIES
|
|
532
|
+
description: "Uses accessible queries (getByRole, getByLabelText)"
|
|
533
|
+
points: 8
|
|
534
|
+
- id: A11Y_ASSERTIONS
|
|
535
|
+
description: "Tests accessibility attributes"
|
|
536
|
+
points: 7
|
|
537
|
+
|
|
538
|
+
- name: persona
|
|
539
|
+
weight: 15
|
|
540
|
+
criteria:
|
|
541
|
+
- id: CHARACTER_CONSISTENCY
|
|
542
|
+
description: "Stays in character throughout"
|
|
543
|
+
points: 8
|
|
544
|
+
- id: PERSONA_VALUE_ADD
|
|
545
|
+
description: "Persona enhances test documentation"
|
|
546
|
+
points: 7
|
|
547
|
+
|
|
548
|
+
# =============================================================================
|
|
549
|
+
# PERSONA INFLUENCE
|
|
550
|
+
# =============================================================================
|
|
551
|
+
|
|
552
|
+
persona_influence:
|
|
553
|
+
dimensions:
|
|
554
|
+
- name: test_strategy
|
|
555
|
+
description: "Overall testing approach"
|
|
556
|
+
spectrum:
|
|
557
|
+
happy_path_first: "Tests main flows, then edge cases"
|
|
558
|
+
edge_case_focused: "Emphasizes boundary conditions"
|
|
559
|
+
risk_based: "Tests based on failure likelihood"
|
|
560
|
+
|
|
561
|
+
- name: mock_approach
|
|
562
|
+
description: "How mocking is handled"
|
|
563
|
+
spectrum:
|
|
564
|
+
minimal: "Mocks only external dependencies"
|
|
565
|
+
moderate: "Mocks contexts and hooks"
|
|
566
|
+
extensive: "Mocks for isolation"
|
|
567
|
+
|
|
568
|
+
- name: documentation_style
|
|
569
|
+
description: "How tests are documented"
|
|
570
|
+
spectrum:
|
|
571
|
+
terse: "Minimal test descriptions"
|
|
572
|
+
descriptive: "Clear describe/it blocks"
|
|
573
|
+
verbose: "Extensive comments and docs"
|
|
574
|
+
|
|
575
|
+
expected_tendencies:
|
|
576
|
+
discworld_tea:
|
|
577
|
+
character: "Igor"
|
|
578
|
+
expected_traits:
|
|
579
|
+
- "Thorough - should cover edge cases"
|
|
580
|
+
- "Practical - focuses on what breaks"
|
|
581
|
+
- "May have unusual test names"
|
|
582
|
+
coverage_prediction: "high"
|
|
583
|
+
|
|
584
|
+
star_trek_tea:
|
|
585
|
+
character: "Scotty"
|
|
586
|
+
expected_traits:
|
|
587
|
+
- "Systematic - organized test structure"
|
|
588
|
+
- "May over-test internals"
|
|
589
|
+
- "Clear documentation"
|
|
590
|
+
coverage_prediction: "high"
|
|
591
|
+
|
|
592
|
+
control_tea:
|
|
593
|
+
character: "None (baseline)"
|
|
594
|
+
expected_traits:
|
|
595
|
+
- "Standard testing approach"
|
|
596
|
+
coverage_prediction: "baseline reference"
|