@pageai/ralph-loop 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/PROMPT.md +58 -0
- package/.agent/STEERING.md +3 -0
- package/.agent/logs/LOG.md +13 -0
- package/.agent/prd/.gitkeep +0 -0
- package/.agent/screenshots/.gitkeep +0 -0
- package/.agent/skills/component-refactoring/SKILL.md +247 -0
- package/.agent/skills/component-refactoring/references/complexity-patterns.md +485 -0
- package/.agent/skills/component-refactoring/references/component-splitting.md +419 -0
- package/.agent/skills/component-refactoring/references/hook-extraction.md +317 -0
- package/.agent/skills/e2e-tester/SKILL.md +595 -0
- package/.agent/skills/frontend-code-review/SKILL.md +73 -0
- package/.agent/skills/frontend-code-review/references/code-quality.md +28 -0
- package/.agent/skills/frontend-code-review/references/performance.md +36 -0
- package/.agent/skills/frontend-testing/SKILL.md +316 -0
- package/.agent/skills/frontend-testing/assets/component-test.template.tsx +293 -0
- package/.agent/skills/frontend-testing/assets/hook-test.template.ts +207 -0
- package/.agent/skills/frontend-testing/assets/utility-test.template.ts +154 -0
- package/.agent/skills/frontend-testing/references/async-testing.md +345 -0
- package/.agent/skills/frontend-testing/references/checklist.md +188 -0
- package/.agent/skills/frontend-testing/references/common-patterns.md +449 -0
- package/.agent/skills/frontend-testing/references/mocking.md +289 -0
- package/.agent/skills/frontend-testing/references/workflow.md +265 -0
- package/.agent/skills/prd-creator/JSON.md +613 -0
- package/.agent/skills/prd-creator/PRD.md +196 -0
- package/.agent/skills/prd-creator/SKILL.md +143 -0
- package/.agent/skills/skill-creator/SKILL.md +355 -0
- package/.agent/skills/skill-creator/references/output-patterns.md +86 -0
- package/.agent/skills/skill-creator/references/workflows.md +28 -0
- package/.agent/skills/skill-creator/scripts/init_skill.py +300 -0
- package/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.agent/skills/vercel-react-best-practices/AGENTS.md +2249 -0
- package/.agent/skills/vercel-react-best-practices/SKILL.md +125 -0
- package/.agent/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/.agent/skills/vercel-react-best-practices/rules/advanced-use-latest.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-dependencies.md +36 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/.agent/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/.agent/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-cache-react.md +26 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +79 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/.agent/skills/vitest-best-practices/AGENTS.md +84 -0
- package/.agent/skills/vitest-best-practices/SKILL.md +130 -0
- package/.agent/skills/vitest-best-practices/references/aaa-pattern.md +260 -0
- package/.agent/skills/vitest-best-practices/references/assertions.md +393 -0
- package/.agent/skills/vitest-best-practices/references/async-testing.md +454 -0
- package/.agent/skills/vitest-best-practices/references/error-handling.md +382 -0
- package/.agent/skills/vitest-best-practices/references/organization.md +212 -0
- package/.agent/skills/vitest-best-practices/references/parameterized-tests.md +297 -0
- package/.agent/skills/vitest-best-practices/references/performance.md +528 -0
- package/.agent/skills/vitest-best-practices/references/snapshot-testing.md +483 -0
- package/.agent/skills/vitest-best-practices/references/test-doubles.md +499 -0
- package/.agent/skills/vitest-best-practices/references/vitest-features.md +529 -0
- package/.agent/skills/web-design-guidelines/SKILL.md +39 -0
- package/.agent/tasks/.gitkeep +0 -0
- package/.agent/tasks.json +1 -0
- package/.claude/agents/code-reviewer.md +172 -0
- package/.claude/commands/aw.md +50 -0
- package/.claude/hooks/play-sound.js +87 -0
- package/.claude/hooks/pre-tool-use.js +40 -0
- package/.claude/settings.json +54 -0
- package/.claude/settings.local.json +13 -0
- package/.mcp.json +31 -0
- package/AGENTS.md +44 -0
- package/CLAUDE.md +1 -0
- package/README.md +236 -0
- package/bin/cli.js +156 -0
- package/bin/lib/copy.js +149 -0
- package/bin/lib/display.js +137 -0
- package/package.json +65 -0
- package/ralph.sh +333 -0
- package/scripts/lib/args.sh +44 -0
- package/scripts/lib/cleanup.sh +53 -0
- package/scripts/lib/constants.sh +25 -0
- package/scripts/lib/display.sh +196 -0
- package/scripts/lib/logging.sh +30 -0
- package/scripts/lib/notify.sh +41 -0
- package/scripts/lib/output.sh +147 -0
- package/scripts/lib/preflight.sh +57 -0
- package/scripts/lib/preview.sh +77 -0
- package/scripts/lib/promise.sh +76 -0
- package/scripts/lib/spinner.sh +85 -0
- package/scripts/lib/terminal.sh +57 -0
- package/scripts/lib/timing.sh +223 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
# 1.3 Parameterized Tests
|
|
2
|
+
|
|
3
|
+
Use `it.each` to test the same behavior with different inputs. This eliminates code duplication while keeping tests focused and readable.
|
|
4
|
+
|
|
5
|
+
## Basic Parameterized Tests
|
|
6
|
+
|
|
7
|
+
Test one behavior with multiple input/output combinations using `it.each`.
|
|
8
|
+
|
|
9
|
+
**✅ Correct: it.each for variations of same behavior**
|
|
10
|
+
```ts
|
|
11
|
+
describe('factorial', () => {
|
|
12
|
+
it.each([
|
|
13
|
+
{ input: 0, expected: 1 },
|
|
14
|
+
{ input: 1, expected: 1 },
|
|
15
|
+
{ input: 5, expected: 120 },
|
|
16
|
+
{ input: 7, expected: 5040 },
|
|
17
|
+
])('should return $expected when given $input', ({ input, expected }) => {
|
|
18
|
+
expect(factorial(input)).toEqual(expected);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should throw when the input is negative', () => {
|
|
22
|
+
expect(() => factorial(-1)).toThrow('Number must not be negative');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**❌ Incorrect: duplicate tests**
|
|
28
|
+
```ts
|
|
29
|
+
describe('factorial', () => {
|
|
30
|
+
it('should return 1 when given 0', () => {
|
|
31
|
+
expect(factorial(0)).toEqual(1);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should return 1 when given 1', () => {
|
|
35
|
+
expect(factorial(1)).toEqual(1);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should return 120 when given 5', () => {
|
|
39
|
+
expect(factorial(5)).toEqual(120);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should return 5040 when given 7', () => {
|
|
43
|
+
expect(factorial(7)).toEqual(5040);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
*Why?* Duplicated test code is harder to maintain. If the assertion logic changes, you need to update multiple tests.
|
|
48
|
+
|
|
49
|
+
## Template Strings in Test Names
|
|
50
|
+
|
|
51
|
+
Use `$variable` syntax in test descriptions to show which inputs are being tested.
|
|
52
|
+
|
|
53
|
+
**✅ Correct: descriptive test names with variables**
|
|
54
|
+
```ts
|
|
55
|
+
it.each([
|
|
56
|
+
{ email: 'test@example.com', valid: true },
|
|
57
|
+
{ email: 'invalid-email', valid: false },
|
|
58
|
+
{ email: 'test@', valid: false },
|
|
59
|
+
{ email: '@example.com', valid: false },
|
|
60
|
+
])('should return $valid for email "$email"', ({ email, valid }) => {
|
|
61
|
+
expect(isValidEmail(email)).toEqual(valid);
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**❌ Incorrect: generic test name**
|
|
66
|
+
```ts
|
|
67
|
+
it.each([
|
|
68
|
+
{ email: 'test@example.com', valid: true },
|
|
69
|
+
{ email: 'invalid-email', valid: false },
|
|
70
|
+
])('should validate email', ({ email, valid }) => {
|
|
71
|
+
expect(isValidEmail(email)).toEqual(valid);
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
*Why?* When a test fails, you won't know which input caused the failure without checking test output details.
|
|
75
|
+
|
|
76
|
+
## Array Syntax
|
|
77
|
+
|
|
78
|
+
You can also use array syntax instead of objects for simpler cases.
|
|
79
|
+
|
|
80
|
+
**✅ Correct: array syntax for simple cases**
|
|
81
|
+
```ts
|
|
82
|
+
it.each([
|
|
83
|
+
[0, 1],
|
|
84
|
+
[1, 1],
|
|
85
|
+
[5, 120],
|
|
86
|
+
[7, 5040],
|
|
87
|
+
])('factorial(%i) should equal %i', (input, expected) => {
|
|
88
|
+
expect(factorial(input)).toEqual(expected);
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Object syntax is preferred** when you have many parameters or want clearer naming:
|
|
93
|
+
|
|
94
|
+
**✅ Better: object syntax for clarity**
|
|
95
|
+
```ts
|
|
96
|
+
it.each([
|
|
97
|
+
{ n: 0, expected: 1 },
|
|
98
|
+
{ n: 1, expected: 1 },
|
|
99
|
+
{ n: 5, expected: 120 },
|
|
100
|
+
{ n: 7, expected: 5040 },
|
|
101
|
+
])('factorial($n) should equal $expected', ({ n, expected }) => {
|
|
102
|
+
expect(factorial(n)).toEqual(expected);
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Testing Multiple Related Behaviors
|
|
107
|
+
|
|
108
|
+
Don't mix different behaviors in one parameterized test. Split them into separate tests.
|
|
109
|
+
|
|
110
|
+
**✅ Correct: separate tests for different behaviors**
|
|
111
|
+
```ts
|
|
112
|
+
describe('calculateDiscount', () => {
|
|
113
|
+
it.each([
|
|
114
|
+
{ price: 100, discount: 0.1, expected: 90 },
|
|
115
|
+
{ price: 50, discount: 0.2, expected: 40 },
|
|
116
|
+
{ price: 200, discount: 0.15, expected: 170 },
|
|
117
|
+
])('should return $expected when price is $price and discount is $discount',
|
|
118
|
+
({ price, discount, expected }) => {
|
|
119
|
+
expect(calculateDiscount(price, discount)).toEqual(expected);
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
it.each([
|
|
124
|
+
{ price: -10, discount: 0.1 },
|
|
125
|
+
{ price: 100, discount: -0.1 },
|
|
126
|
+
{ price: 100, discount: 1.5 },
|
|
127
|
+
])('should throw for invalid inputs: price=$price, discount=$discount',
|
|
128
|
+
({ price, discount }) => {
|
|
129
|
+
expect(() => calculateDiscount(price, discount)).toThrow();
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**❌ Incorrect: mixing valid and error cases**
|
|
136
|
+
```ts
|
|
137
|
+
it.each([
|
|
138
|
+
{ price: 100, discount: 0.1, expected: 90, shouldThrow: false },
|
|
139
|
+
{ price: 50, discount: 0.2, expected: 40, shouldThrow: false },
|
|
140
|
+
{ price: -10, discount: 0.1, expected: null, shouldThrow: true },
|
|
141
|
+
{ price: 100, discount: -0.1, expected: null, shouldThrow: true },
|
|
142
|
+
])('should handle price=$price and discount=$discount',
|
|
143
|
+
({ price, discount, expected, shouldThrow }) => {
|
|
144
|
+
if (shouldThrow) {
|
|
145
|
+
expect(() => calculateDiscount(price, discount)).toThrow();
|
|
146
|
+
} else {
|
|
147
|
+
expect(calculateDiscount(price, discount)).toEqual(expected);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
```
|
|
152
|
+
*Why?* Conditional logic in tests makes them harder to understand and maintain. Each test should have one clear purpose.
|
|
153
|
+
|
|
154
|
+
## Complex Test Data
|
|
155
|
+
|
|
156
|
+
For complex test cases, extract data to a separate constant.
|
|
157
|
+
|
|
158
|
+
**✅ Correct: extracted test data**
|
|
159
|
+
```ts
|
|
160
|
+
const USER_VALIDATION_CASES = [
|
|
161
|
+
{
|
|
162
|
+
user: { email: 'valid@example.com', age: 25, name: 'John' },
|
|
163
|
+
expected: true,
|
|
164
|
+
description: 'valid user',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
user: { email: 'invalid-email', age: 25, name: 'John' },
|
|
168
|
+
expected: false,
|
|
169
|
+
description: 'invalid email',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
user: { email: 'valid@example.com', age: 15, name: 'John' },
|
|
173
|
+
expected: false,
|
|
174
|
+
description: 'underage user',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
user: { email: 'valid@example.com', age: 25, name: '' },
|
|
178
|
+
expected: false,
|
|
179
|
+
description: 'empty name',
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
describe('validateUser', () => {
|
|
184
|
+
it.each(USER_VALIDATION_CASES)(
|
|
185
|
+
'should return $expected for $description',
|
|
186
|
+
({ user, expected }) => {
|
|
187
|
+
expect(validateUser(user)).toEqual(expected);
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**❌ Incorrect: inline complex data obscures test structure**
|
|
194
|
+
```ts
|
|
195
|
+
it.each([
|
|
196
|
+
{ user: { email: 'valid@example.com', age: 25, name: 'John', address: { street: '123 Main', city: 'NYC', zip: '10001' }, preferences: { newsletter: true, notifications: false } }, expected: true },
|
|
197
|
+
{ user: { email: 'invalid', age: 25, name: 'John', address: { street: '123 Main', city: 'NYC', zip: '10001' }, preferences: { newsletter: true, notifications: false } }, expected: false },
|
|
198
|
+
// ... more complex objects
|
|
199
|
+
])('should validate user', ({ user, expected }) => {
|
|
200
|
+
expect(validateUser(user)).toEqual(expected);
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Edge Cases and Boundaries
|
|
205
|
+
|
|
206
|
+
Use parameterized tests to comprehensively cover edge cases and boundary conditions.
|
|
207
|
+
|
|
208
|
+
**✅ Correct: comprehensive edge case coverage**
|
|
209
|
+
```ts
|
|
210
|
+
describe('clamp', () => {
|
|
211
|
+
it.each([
|
|
212
|
+
{ value: 5, min: 0, max: 10, expected: 5, case: 'value within range' },
|
|
213
|
+
{ value: -5, min: 0, max: 10, expected: 0, case: 'value below min' },
|
|
214
|
+
{ value: 15, min: 0, max: 10, expected: 10, case: 'value above max' },
|
|
215
|
+
{ value: 0, min: 0, max: 10, expected: 0, case: 'value equals min' },
|
|
216
|
+
{ value: 10, min: 0, max: 10, expected: 10, case: 'value equals max' },
|
|
217
|
+
{ value: 5, min: 5, max: 5, expected: 5, case: 'min equals max' },
|
|
218
|
+
])('should return $expected when $case', ({ value, min, max, expected }) => {
|
|
219
|
+
expect(clamp(value, min, max)).toEqual(expected);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Using describe.each for Test Groups
|
|
225
|
+
|
|
226
|
+
For testing multiple related scenarios, use `describe.each` to create test suites.
|
|
227
|
+
|
|
228
|
+
**✅ Correct: describe.each for different user roles**
|
|
229
|
+
```ts
|
|
230
|
+
describe.each([
|
|
231
|
+
{ role: 'admin', canEdit: true, canDelete: true, canView: true },
|
|
232
|
+
{ role: 'editor', canEdit: true, canDelete: false, canView: true },
|
|
233
|
+
{ role: 'viewer', canEdit: false, canDelete: false, canView: true },
|
|
234
|
+
])('User with $role role', ({ role, canEdit, canDelete, canView }) => {
|
|
235
|
+
let user: User;
|
|
236
|
+
|
|
237
|
+
beforeEach(() => {
|
|
238
|
+
user = createUser({ role });
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it(`should ${canEdit ? '' : 'not '}be able to edit`, () => {
|
|
242
|
+
expect(user.canEdit()).toEqual(canEdit);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it(`should ${canDelete ? '' : 'not '}be able to delete`, () => {
|
|
246
|
+
expect(user.canDelete()).toEqual(canDelete);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it(`should ${canView ? '' : 'not '}be able to view`, () => {
|
|
250
|
+
expect(user.canView()).toEqual(canView);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## When NOT to Use Parameterized Tests
|
|
256
|
+
|
|
257
|
+
Don't use `it.each` when test cases have different setup or assertion logic.
|
|
258
|
+
|
|
259
|
+
**❌ Incorrect: forcing parameterization**
|
|
260
|
+
```ts
|
|
261
|
+
it.each([
|
|
262
|
+
{ type: 'email', input: 'test@example.com', setupFn: setupEmail },
|
|
263
|
+
{ type: 'phone', input: '123-456-7890', setupFn: setupPhone },
|
|
264
|
+
])('should validate $type', ({ type, input, setupFn }) => {
|
|
265
|
+
setupFn(); // Different setup for each type
|
|
266
|
+
|
|
267
|
+
if (type === 'email') {
|
|
268
|
+
expect(validateEmail(input)).toEqual(true);
|
|
269
|
+
} else {
|
|
270
|
+
expect(validatePhone(input)).toEqual(true);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**✅ Correct: separate tests with different logic**
|
|
276
|
+
```ts
|
|
277
|
+
describe('validateEmail', () => {
|
|
278
|
+
it.each([
|
|
279
|
+
'test@example.com',
|
|
280
|
+
'user.name@example.co.uk',
|
|
281
|
+
'user+tag@example.com',
|
|
282
|
+
])('should return true for valid email: %s', (email) => {
|
|
283
|
+
expect(validateEmail(email)).toEqual(true);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe('validatePhone', () => {
|
|
288
|
+
it.each([
|
|
289
|
+
'123-456-7890',
|
|
290
|
+
'(123) 456-7890',
|
|
291
|
+
'+1-123-456-7890',
|
|
292
|
+
])('should return true for valid phone: %s', (phone) => {
|
|
293
|
+
expect(validatePhone(phone)).toEqual(true);
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
*Why?* When setup or assertions differ significantly, separate tests are clearer than conditional logic within a parameterized test.
|