specweave 0.23.18 → 0.24.1
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/.claude-plugin/marketplace.json +144 -45
- package/CLAUDE.md +137 -4
- package/dist/src/cli/helpers/ado-area-path-mapper.d.ts +89 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.d.ts.map +1 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.js +213 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts +29 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js +109 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.js +2 -0
- package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
- package/dist/src/cli/helpers/smart-filter.d.ts +83 -0
- package/dist/src/cli/helpers/smart-filter.d.ts.map +1 -0
- package/dist/src/cli/helpers/smart-filter.js +265 -0
- package/dist/src/cli/helpers/smart-filter.js.map +1 -0
- package/dist/src/core/qa/quality-gate-decider.d.ts +1 -1
- package/dist/src/core/qa/quality-gate-decider.js +2 -2
- package/dist/src/core/qa/quality-gate-decider.js.map +1 -1
- package/dist/src/core/qa/risk-calculator.d.ts +2 -2
- package/dist/src/core/qa/risk-calculator.js +2 -2
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.js +76 -43
- package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
- package/dist/src/core/validators/ac-presence-validator.d.ts +56 -0
- package/dist/src/core/validators/ac-presence-validator.d.ts.map +1 -0
- package/dist/src/core/validators/ac-presence-validator.js +149 -0
- package/dist/src/core/validators/ac-presence-validator.js.map +1 -0
- package/dist/src/integrations/ado/area-path-mapper.d.ts +137 -0
- package/dist/src/integrations/ado/area-path-mapper.d.ts.map +1 -0
- package/dist/src/integrations/ado/area-path-mapper.js +267 -0
- package/dist/src/integrations/ado/area-path-mapper.js.map +1 -0
- package/dist/src/integrations/jira/filter-processor.d.ts +126 -0
- package/dist/src/integrations/jira/filter-processor.d.ts.map +1 -0
- package/dist/src/integrations/jira/filter-processor.js +207 -0
- package/dist/src/integrations/jira/filter-processor.js.map +1 -0
- package/dist/src/integrations/jira/jira-client.d.ts +13 -0
- package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-client.js +33 -0
- package/dist/src/integrations/jira/jira-client.js.map +1 -1
- package/dist/src/utils/ac-embedder.d.ts +63 -0
- package/dist/src/utils/ac-embedder.d.ts.map +1 -0
- package/dist/src/utils/ac-embedder.js +217 -0
- package/dist/src/utils/ac-embedder.js.map +1 -0
- package/dist/src/utils/env-manager.d.ts +86 -0
- package/dist/src/utils/env-manager.d.ts.map +1 -0
- package/dist/src/utils/env-manager.js +188 -0
- package/dist/src/utils/env-manager.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave/agents/AGENTS-INDEX.md +1 -1
- package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +9 -9
- package/plugins/specweave/commands/specweave-do.md +37 -0
- package/plugins/specweave/commands/specweave-done.md +159 -0
- package/plugins/specweave/commands/specweave-embed-acs.md +446 -0
- package/plugins/specweave/commands/specweave-next.md +148 -3
- package/plugins/specweave/commands/specweave-qa.md +2 -2
- package/plugins/specweave/hooks/pre-increment-start.sh +168 -0
- package/plugins/specweave/skills/SKILLS-INDEX.md +1 -1
- package/plugins/specweave-ado/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-ado/commands/specweave-ado-import-projects.md +331 -0
- package/plugins/specweave-alternatives/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-alternatives/commands/alternatives-analyze.md +336 -0
- package/plugins/specweave-alternatives/skills/architecture-alternatives/SKILL.md +651 -0
- package/plugins/specweave-alternatives/skills/bmad-method/SKILL.md +420 -0
- package/plugins/specweave-alternatives/skills/spec-kit-expert/SKILL.md +487 -0
- package/plugins/specweave-backend/commands/api-scaffold.md +80 -0
- package/plugins/specweave-backend/commands/crud-generate.md +109 -0
- package/plugins/specweave-backend/commands/migration-generate.md +139 -0
- package/plugins/specweave-confluent/commands/connector-deploy.md +154 -0
- package/plugins/specweave-confluent/commands/ksqldb-query.md +179 -0
- package/plugins/specweave-confluent/commands/schema-register.md +123 -0
- package/plugins/specweave-core/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-core/commands/architecture-review.md +288 -0
- package/plugins/specweave-core/commands/code-review.md +213 -0
- package/plugins/specweave-core/commands/refactor-plan.md +249 -0
- package/plugins/specweave-core/skills/code-quality/SKILL.md +157 -0
- package/plugins/specweave-core/skills/design-patterns/SKILL.md +244 -0
- package/plugins/specweave-core/skills/software-architecture/SKILL.md +83 -0
- package/plugins/specweave-cost-optimizer/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave-cost-optimizer/commands/cost-analyze.md +360 -0
- package/plugins/specweave-cost-optimizer/commands/cost-optimize.md +480 -0
- package/plugins/specweave-cost-optimizer/skills/aws-cost-expert/SKILL.md +416 -0
- package/plugins/specweave-cost-optimizer/skills/cloud-pricing/SKILL.md +325 -0
- package/plugins/specweave-cost-optimizer/skills/cost-optimization/SKILL.md +337 -0
- package/plugins/specweave-diagrams/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-diagrams/commands/diagrams-generate.md +168 -0
- package/plugins/specweave-docs/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-docs/commands/docs-generate.md +441 -0
- package/plugins/specweave-docs/commands/docs-init.md +334 -0
- package/plugins/specweave-docs/skills/docusaurus/SKILL.md +581 -0
- package/plugins/specweave-docs/skills/spec-driven-brainstorming/SKILL.md +689 -0
- package/plugins/specweave-docs/skills/technical-writing/SKILL.md +1039 -0
- package/plugins/specweave-docs-preview/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-figma/.claude-plugin/plugin.json +23 -0
- package/plugins/specweave-figma/commands/figma-import.md +690 -0
- package/plugins/specweave-figma/commands/figma-to-react.md +834 -0
- package/plugins/specweave-figma/commands/figma-tokens.md +815 -0
- package/plugins/specweave-frontend/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-frontend/agents/frontend-architect/AGENT.md +408 -0
- package/plugins/specweave-frontend/agents/frontend-architect/README.md +385 -0
- package/plugins/specweave-frontend/agents/frontend-architect/examples.md +590 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/component-template.tsx +152 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/hook-template.ts +311 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/page-template.tsx +228 -0
- package/plugins/specweave-frontend/commands/component-generate.md +510 -0
- package/plugins/specweave-frontend/commands/design-system-init.md +494 -0
- package/plugins/specweave-frontend/commands/frontend-scaffold.md +207 -0
- package/plugins/specweave-frontend/commands/nextjs-setup.md +396 -0
- package/plugins/specweave-frontend/skills/design-system-architect/SKILL.md +278 -0
- package/plugins/specweave-frontend/skills/frontend/SKILL.md +420 -0
- package/plugins/specweave-frontend/skills/nextjs/SKILL.md +546 -0
- package/plugins/specweave-github/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +212 -0
- package/plugins/specweave-infrastructure/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-jira/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-jira/commands/import-projects.js +183 -0
- package/plugins/specweave-jira/commands/import-projects.md +97 -0
- package/plugins/specweave-jira/commands/import-projects.ts +288 -0
- package/plugins/specweave-jira/commands/specweave-jira-import-projects.md +298 -0
- package/plugins/specweave-kafka/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-kubernetes/commands/cluster-setup.md +262 -0
- package/plugins/specweave-kubernetes/commands/deployment-generate.md +242 -0
- package/plugins/specweave-kubernetes/commands/helm-scaffold.md +333 -0
- package/plugins/specweave-ml/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-mobile/commands/app-scaffold.md +233 -0
- package/plugins/specweave-mobile/commands/build-config.md +256 -0
- package/plugins/specweave-mobile/commands/screen-generate.md +289 -0
- package/plugins/specweave-n8n/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-payments/commands/stripe-setup.md +931 -0
- package/plugins/specweave-payments/commands/subscription-flow.md +1193 -0
- package/plugins/specweave-payments/commands/subscription-manage.md +386 -0
- package/plugins/specweave-payments/commands/webhook-setup.md +295 -0
- package/plugins/specweave-plugin-dev/.claude-plugin/plugin.json +13 -12
- package/plugins/specweave-plugin-dev/commands/plugin-create.md +333 -0
- package/plugins/specweave-plugin-dev/commands/plugin-publish.md +339 -0
- package/plugins/specweave-plugin-dev/commands/plugin-test.md +293 -0
- package/plugins/specweave-plugin-dev/skills/claude-sdk/SKILL.md +162 -0
- package/plugins/specweave-plugin-dev/skills/marketplace-publishing/SKILL.md +263 -0
- package/plugins/specweave-plugin-dev/skills/plugin-development/SKILL.md +316 -0
- package/plugins/specweave-release/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-release/commands/specweave-release-npm.md +110 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +168 -0
- package/plugins/specweave-testing/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-testing/agents/qa-engineer/AGENT.md +818 -0
- package/plugins/specweave-testing/agents/qa-engineer/README.md +443 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/playwright-e2e-test.ts +470 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/test-data-factory.ts +507 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/vitest-unit-test.ts +400 -0
- package/plugins/specweave-testing/agents/qa-engineer/test-strategies.md +726 -0
- package/plugins/specweave-testing/commands/e2e-setup.md +1081 -0
- package/plugins/specweave-testing/commands/test-coverage.md +979 -0
- package/plugins/specweave-testing/commands/test-generate.md +1156 -0
- package/plugins/specweave-testing/commands/test-init.md +409 -0
- package/plugins/specweave-testing/skills/e2e-playwright/SKILL.md +769 -0
- package/plugins/specweave-testing/skills/tdd-expert/SKILL.md +934 -0
- package/plugins/specweave-testing/skills/unit-testing-expert/SKILL.md +1011 -0
- package/plugins/specweave-tooling/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-create.md +691 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-package.md +751 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-validate.md +858 -0
- package/plugins/specweave-ui/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-ui/commands/ui-automate.md +199 -0
- package/plugins/specweave-ui/commands/ui-inspect.md +70 -0
- package/plugins/specweave-ui/skills/browser-automation/SKILL.md +314 -0
- package/plugins/specweave-ui/skills/ui-testing/SKILL.md +716 -0
- package/plugins/specweave-ui/skills/visual-regression/SKILL.md +728 -0
- package/plugins/specweave/commands/check-hooks.md +0 -257
- package/plugins/specweave/commands/specweave-archive-increments.md +0 -82
- package/plugins/specweave-plugin-dev/skills/plugin-expert/SKILL.md +0 -1231
- /package/plugins/specweave/{agents/code-reviewer.md → skills/code-reviewer/SKILL.md} +0 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Data Factory Template
|
|
3
|
+
*
|
|
4
|
+
* This template demonstrates best practices for creating reusable
|
|
5
|
+
* test data factories using the Factory pattern.
|
|
6
|
+
*
|
|
7
|
+
* Benefits:
|
|
8
|
+
* - Consistent test data generation
|
|
9
|
+
* - Easy to customize with overrides
|
|
10
|
+
* - Reduces test setup boilerplate
|
|
11
|
+
* - Type-safe with TypeScript
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { faker } from '@faker-js/faker';
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// TYPES
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
export interface User {
|
|
21
|
+
id: string;
|
|
22
|
+
email: string;
|
|
23
|
+
username: string;
|
|
24
|
+
firstName: string;
|
|
25
|
+
lastName: string;
|
|
26
|
+
role: 'admin' | 'user' | 'guest';
|
|
27
|
+
isActive: boolean;
|
|
28
|
+
createdAt: Date;
|
|
29
|
+
updatedAt: Date;
|
|
30
|
+
profile?: UserProfile;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface UserProfile {
|
|
34
|
+
bio: string;
|
|
35
|
+
avatar: string;
|
|
36
|
+
phoneNumber: string;
|
|
37
|
+
address: Address;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface Address {
|
|
41
|
+
street: string;
|
|
42
|
+
city: string;
|
|
43
|
+
state: string;
|
|
44
|
+
zipCode: string;
|
|
45
|
+
country: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface Product {
|
|
49
|
+
id: string;
|
|
50
|
+
name: string;
|
|
51
|
+
description: string;
|
|
52
|
+
price: number;
|
|
53
|
+
category: string;
|
|
54
|
+
inStock: boolean;
|
|
55
|
+
quantity: number;
|
|
56
|
+
imageUrl: string;
|
|
57
|
+
createdAt: Date;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface Order {
|
|
61
|
+
id: string;
|
|
62
|
+
userId: string;
|
|
63
|
+
items: OrderItem[];
|
|
64
|
+
subtotal: number;
|
|
65
|
+
tax: number;
|
|
66
|
+
total: number;
|
|
67
|
+
status: 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled';
|
|
68
|
+
shippingAddress: Address;
|
|
69
|
+
createdAt: Date;
|
|
70
|
+
updatedAt: Date;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface OrderItem {
|
|
74
|
+
productId: string;
|
|
75
|
+
quantity: number;
|
|
76
|
+
price: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// FACTORY FUNCTIONS
|
|
81
|
+
// ============================================================================
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* User Factory
|
|
85
|
+
*
|
|
86
|
+
* Creates realistic user test data with sensible defaults
|
|
87
|
+
*/
|
|
88
|
+
export class UserFactory {
|
|
89
|
+
/**
|
|
90
|
+
* Create a single user
|
|
91
|
+
*
|
|
92
|
+
* @param overrides - Partial user object to override defaults
|
|
93
|
+
* @returns Complete user object
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* const admin = UserFactory.create({ role: 'admin' });
|
|
98
|
+
* const inactiveUser = UserFactory.create({ isActive: false });
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
static create(overrides: Partial<User> = {}): User {
|
|
102
|
+
const firstName = faker.person.firstName();
|
|
103
|
+
const lastName = faker.person.lastName();
|
|
104
|
+
const email =
|
|
105
|
+
overrides.email || faker.internet.email({ firstName, lastName });
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
id: faker.string.uuid(),
|
|
109
|
+
email,
|
|
110
|
+
username: faker.internet.userName({ firstName, lastName }),
|
|
111
|
+
firstName,
|
|
112
|
+
lastName,
|
|
113
|
+
role: 'user',
|
|
114
|
+
isActive: true,
|
|
115
|
+
createdAt: faker.date.past(),
|
|
116
|
+
updatedAt: faker.date.recent(),
|
|
117
|
+
...overrides,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Create multiple users
|
|
123
|
+
*
|
|
124
|
+
* @param count - Number of users to create
|
|
125
|
+
* @param overrides - Partial user object to override defaults for all users
|
|
126
|
+
* @returns Array of user objects
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* const users = UserFactory.createMany(5);
|
|
131
|
+
* const admins = UserFactory.createMany(3, { role: 'admin' });
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
static createMany(count: number, overrides: Partial<User> = {}): User[] {
|
|
135
|
+
return Array.from({ length: count }, () => this.create(overrides));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Create admin user
|
|
140
|
+
*
|
|
141
|
+
* @param overrides - Partial user object to override defaults
|
|
142
|
+
* @returns Admin user object
|
|
143
|
+
*/
|
|
144
|
+
static createAdmin(overrides: Partial<User> = {}): User {
|
|
145
|
+
return this.create({
|
|
146
|
+
role: 'admin',
|
|
147
|
+
...overrides,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Create user with complete profile
|
|
153
|
+
*
|
|
154
|
+
* @param overrides - Partial user object to override defaults
|
|
155
|
+
* @returns User with profile object
|
|
156
|
+
*/
|
|
157
|
+
static createWithProfile(overrides: Partial<User> = {}): User {
|
|
158
|
+
return this.create({
|
|
159
|
+
profile: {
|
|
160
|
+
bio: faker.person.bio(),
|
|
161
|
+
avatar: faker.image.avatar(),
|
|
162
|
+
phoneNumber: faker.phone.number(),
|
|
163
|
+
address: AddressFactory.create(),
|
|
164
|
+
},
|
|
165
|
+
...overrides,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Create inactive user
|
|
171
|
+
*
|
|
172
|
+
* @param overrides - Partial user object to override defaults
|
|
173
|
+
* @returns Inactive user object
|
|
174
|
+
*/
|
|
175
|
+
static createInactive(overrides: Partial<User> = {}): User {
|
|
176
|
+
return this.create({
|
|
177
|
+
isActive: false,
|
|
178
|
+
...overrides,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Address Factory
|
|
185
|
+
*
|
|
186
|
+
* Creates realistic address test data
|
|
187
|
+
*/
|
|
188
|
+
export class AddressFactory {
|
|
189
|
+
static create(overrides: Partial<Address> = {}): Address {
|
|
190
|
+
return {
|
|
191
|
+
street: faker.location.streetAddress(),
|
|
192
|
+
city: faker.location.city(),
|
|
193
|
+
state: faker.location.state(),
|
|
194
|
+
zipCode: faker.location.zipCode(),
|
|
195
|
+
country: faker.location.country(),
|
|
196
|
+
...overrides,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
static createUS(overrides: Partial<Address> = {}): Address {
|
|
201
|
+
return this.create({
|
|
202
|
+
country: 'United States',
|
|
203
|
+
zipCode: faker.location.zipCode('#####'),
|
|
204
|
+
state: faker.location.state({ abbreviated: true }),
|
|
205
|
+
...overrides,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Product Factory
|
|
212
|
+
*
|
|
213
|
+
* Creates realistic product test data
|
|
214
|
+
*/
|
|
215
|
+
export class ProductFactory {
|
|
216
|
+
static create(overrides: Partial<Product> = {}): Product {
|
|
217
|
+
return {
|
|
218
|
+
id: faker.string.uuid(),
|
|
219
|
+
name: faker.commerce.productName(),
|
|
220
|
+
description: faker.commerce.productDescription(),
|
|
221
|
+
price: parseFloat(faker.commerce.price()),
|
|
222
|
+
category: faker.commerce.department(),
|
|
223
|
+
inStock: true,
|
|
224
|
+
quantity: faker.number.int({ min: 0, max: 100 }),
|
|
225
|
+
imageUrl: faker.image.url(),
|
|
226
|
+
createdAt: faker.date.past(),
|
|
227
|
+
...overrides,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
static createMany(count: number, overrides: Partial<Product> = {}): Product[] {
|
|
232
|
+
return Array.from({ length: count }, () => this.create(overrides));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
static createOutOfStock(overrides: Partial<Product> = {}): Product {
|
|
236
|
+
return this.create({
|
|
237
|
+
inStock: false,
|
|
238
|
+
quantity: 0,
|
|
239
|
+
...overrides,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
static createExpensive(overrides: Partial<Product> = {}): Product {
|
|
244
|
+
return this.create({
|
|
245
|
+
price: faker.number.int({ min: 1000, max: 10000 }),
|
|
246
|
+
...overrides,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Order Factory
|
|
253
|
+
*
|
|
254
|
+
* Creates realistic order test data with items
|
|
255
|
+
*/
|
|
256
|
+
export class OrderFactory {
|
|
257
|
+
static create(overrides: Partial<Order> = {}): Order {
|
|
258
|
+
const items = overrides.items || [
|
|
259
|
+
{
|
|
260
|
+
productId: faker.string.uuid(),
|
|
261
|
+
quantity: faker.number.int({ min: 1, max: 5 }),
|
|
262
|
+
price: parseFloat(faker.commerce.price()),
|
|
263
|
+
},
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
|
267
|
+
const tax = subtotal * 0.08; // 8% tax
|
|
268
|
+
const total = subtotal + tax;
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
id: faker.string.uuid(),
|
|
272
|
+
userId: faker.string.uuid(),
|
|
273
|
+
items,
|
|
274
|
+
subtotal,
|
|
275
|
+
tax,
|
|
276
|
+
total,
|
|
277
|
+
status: 'pending',
|
|
278
|
+
shippingAddress: AddressFactory.createUS(),
|
|
279
|
+
createdAt: faker.date.recent(),
|
|
280
|
+
updatedAt: faker.date.recent(),
|
|
281
|
+
...overrides,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
static createMany(count: number, overrides: Partial<Order> = {}): Order[] {
|
|
286
|
+
return Array.from({ length: count }, () => this.create(overrides));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
static createWithItems(items: OrderItem[], overrides: Partial<Order> = {}): Order {
|
|
290
|
+
return this.create({ items, ...overrides });
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
static createShipped(overrides: Partial<Order> = {}): Order {
|
|
294
|
+
return this.create({
|
|
295
|
+
status: 'shipped',
|
|
296
|
+
...overrides,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
static createCancelled(overrides: Partial<Order> = {}): Order {
|
|
301
|
+
return this.create({
|
|
302
|
+
status: 'cancelled',
|
|
303
|
+
...overrides,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ============================================================================
|
|
309
|
+
// BUILDER PATTERN (Advanced)
|
|
310
|
+
// ============================================================================
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* User Builder
|
|
314
|
+
*
|
|
315
|
+
* Provides a fluent interface for building complex user objects
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* ```ts
|
|
319
|
+
* const user = new UserBuilder()
|
|
320
|
+
* .withEmail('admin@example.com')
|
|
321
|
+
* .withRole('admin')
|
|
322
|
+
* .withProfile()
|
|
323
|
+
* .build();
|
|
324
|
+
* ```
|
|
325
|
+
*/
|
|
326
|
+
export class UserBuilder {
|
|
327
|
+
private user: Partial<User> = {};
|
|
328
|
+
|
|
329
|
+
withId(id: string): this {
|
|
330
|
+
this.user.id = id;
|
|
331
|
+
return this;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
withEmail(email: string): this {
|
|
335
|
+
this.user.email = email;
|
|
336
|
+
return this;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
withUsername(username: string): this {
|
|
340
|
+
this.user.username = username;
|
|
341
|
+
return this;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
withName(firstName: string, lastName: string): this {
|
|
345
|
+
this.user.firstName = firstName;
|
|
346
|
+
this.user.lastName = lastName;
|
|
347
|
+
return this;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
withRole(role: User['role']): this {
|
|
351
|
+
this.user.role = role;
|
|
352
|
+
return this;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
withProfile(profile?: UserProfile): this {
|
|
356
|
+
this.user.profile = profile || {
|
|
357
|
+
bio: faker.person.bio(),
|
|
358
|
+
avatar: faker.image.avatar(),
|
|
359
|
+
phoneNumber: faker.phone.number(),
|
|
360
|
+
address: AddressFactory.create(),
|
|
361
|
+
};
|
|
362
|
+
return this;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
inactive(): this {
|
|
366
|
+
this.user.isActive = false;
|
|
367
|
+
return this;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
active(): this {
|
|
371
|
+
this.user.isActive = true;
|
|
372
|
+
return this;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
build(): User {
|
|
376
|
+
return UserFactory.create(this.user);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ============================================================================
|
|
381
|
+
// USAGE EXAMPLES
|
|
382
|
+
// ============================================================================
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Example: Simple user creation
|
|
386
|
+
*/
|
|
387
|
+
export function exampleSimpleUser() {
|
|
388
|
+
const user = UserFactory.create();
|
|
389
|
+
const admin = UserFactory.createAdmin();
|
|
390
|
+
const users = UserFactory.createMany(5);
|
|
391
|
+
|
|
392
|
+
return { user, admin, users };
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Example: Customized user creation
|
|
397
|
+
*/
|
|
398
|
+
export function exampleCustomUser() {
|
|
399
|
+
const user = UserFactory.create({
|
|
400
|
+
email: 'custom@example.com',
|
|
401
|
+
role: 'admin',
|
|
402
|
+
isActive: false,
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
return user;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Example: Builder pattern
|
|
410
|
+
*/
|
|
411
|
+
export function exampleBuilder() {
|
|
412
|
+
const user = new UserBuilder()
|
|
413
|
+
.withEmail('builder@example.com')
|
|
414
|
+
.withName('John', 'Doe')
|
|
415
|
+
.withRole('admin')
|
|
416
|
+
.withProfile()
|
|
417
|
+
.active()
|
|
418
|
+
.build();
|
|
419
|
+
|
|
420
|
+
return user;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Example: Order with products
|
|
425
|
+
*/
|
|
426
|
+
export function exampleOrder() {
|
|
427
|
+
// Create products
|
|
428
|
+
const products = ProductFactory.createMany(3);
|
|
429
|
+
|
|
430
|
+
// Create order items from products
|
|
431
|
+
const items: OrderItem[] = products.map((product) => ({
|
|
432
|
+
productId: product.id,
|
|
433
|
+
quantity: faker.number.int({ min: 1, max: 3 }),
|
|
434
|
+
price: product.price,
|
|
435
|
+
}));
|
|
436
|
+
|
|
437
|
+
// Create order with items
|
|
438
|
+
const order = OrderFactory.createWithItems(items);
|
|
439
|
+
|
|
440
|
+
return { products, order };
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// ============================================================================
|
|
444
|
+
// TEST USAGE
|
|
445
|
+
// ============================================================================
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Example test using factories
|
|
449
|
+
*/
|
|
450
|
+
import { describe, it, expect } from 'vitest';
|
|
451
|
+
|
|
452
|
+
describe('UserService (using factories)', () => {
|
|
453
|
+
it('should create user', () => {
|
|
454
|
+
// ARRANGE
|
|
455
|
+
const userData = UserFactory.create();
|
|
456
|
+
|
|
457
|
+
// ACT
|
|
458
|
+
const result = userService.create(userData);
|
|
459
|
+
|
|
460
|
+
// ASSERT
|
|
461
|
+
expect(result.id).toBeDefined();
|
|
462
|
+
expect(result.email).toBe(userData.email);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('should only allow admins to delete users', () => {
|
|
466
|
+
// ARRANGE
|
|
467
|
+
const admin = UserFactory.createAdmin();
|
|
468
|
+
const regularUser = UserFactory.create();
|
|
469
|
+
|
|
470
|
+
// ACT & ASSERT
|
|
471
|
+
expect(() => userService.deleteUser(regularUser.id, admin)).not.toThrow();
|
|
472
|
+
expect(() => userService.deleteUser(admin.id, regularUser)).toThrow('Unauthorized');
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('should calculate order total correctly', () => {
|
|
476
|
+
// ARRANGE
|
|
477
|
+
const order = OrderFactory.create({
|
|
478
|
+
items: [
|
|
479
|
+
{ productId: '1', quantity: 2, price: 50 },
|
|
480
|
+
{ productId: '2', quantity: 1, price: 30 },
|
|
481
|
+
],
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// ACT
|
|
485
|
+
const total = orderService.calculateTotal(order);
|
|
486
|
+
|
|
487
|
+
// ASSERT
|
|
488
|
+
expect(total).toBe(140.4); // (50*2 + 30*1) * 1.08 tax
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
// ============================================================================
|
|
493
|
+
// BEST PRACTICES
|
|
494
|
+
// ============================================================================
|
|
495
|
+
|
|
496
|
+
/*
|
|
497
|
+
✅ Use realistic data (faker.js)
|
|
498
|
+
✅ Provide sensible defaults
|
|
499
|
+
✅ Allow overrides for customization
|
|
500
|
+
✅ Type-safe with TypeScript
|
|
501
|
+
✅ Create helper methods (createAdmin, createInactive, etc.)
|
|
502
|
+
✅ Builder pattern for complex objects
|
|
503
|
+
✅ Consistent naming (create, createMany, createWith...)
|
|
504
|
+
✅ Document with JSDoc
|
|
505
|
+
✅ Export all factories for reuse
|
|
506
|
+
✅ Keep factories simple and focused
|
|
507
|
+
*/
|