@studiosonrai/nestjs-testfx 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.
Files changed (36) hide show
  1. package/README.md +666 -0
  2. package/dist/src/factories/base.factory.d.ts +49 -0
  3. package/dist/src/factories/base.factory.js +75 -0
  4. package/dist/src/factories/base.factory.js.map +1 -0
  5. package/dist/src/factories/example.factory.d.ts +44 -0
  6. package/dist/src/factories/example.factory.js +117 -0
  7. package/dist/src/factories/example.factory.js.map +1 -0
  8. package/dist/src/factories/index.d.ts +2 -0
  9. package/dist/src/factories/index.js +10 -0
  10. package/dist/src/factories/index.js.map +1 -0
  11. package/dist/src/fixtures/example.fixture.d.ts +28 -0
  12. package/dist/src/fixtures/example.fixture.js +112 -0
  13. package/dist/src/fixtures/example.fixture.js.map +1 -0
  14. package/dist/src/fixtures/index.d.ts +1 -0
  15. package/dist/src/fixtures/index.js +13 -0
  16. package/dist/src/fixtures/index.js.map +1 -0
  17. package/dist/src/helpers/database-test.helper.d.ts +29 -0
  18. package/dist/src/helpers/database-test.helper.js +154 -0
  19. package/dist/src/helpers/database-test.helper.js.map +1 -0
  20. package/dist/src/helpers/index.d.ts +1 -0
  21. package/dist/src/helpers/index.js +6 -0
  22. package/dist/src/helpers/index.js.map +1 -0
  23. package/dist/src/index.d.ts +4 -0
  24. package/dist/src/index.js +23 -0
  25. package/dist/src/index.js.map +1 -0
  26. package/dist/src/jest.setup.template.d.ts +9 -0
  27. package/dist/src/jest.setup.template.js +31 -0
  28. package/dist/src/jest.setup.template.js.map +1 -0
  29. package/dist/src/matchers/entity-comparator.d.ts +28 -0
  30. package/dist/src/matchers/entity-comparator.js +163 -0
  31. package/dist/src/matchers/entity-comparator.js.map +1 -0
  32. package/dist/src/matchers/index.d.ts +1 -0
  33. package/dist/src/matchers/index.js +7 -0
  34. package/dist/src/matchers/index.js.map +1 -0
  35. package/dist/tsconfig.tsbuildinfo +1 -0
  36. package/package.json +51 -0
package/README.md ADDED
@@ -0,0 +1,666 @@
1
+ # nestjs-testfx
2
+
3
+ Shared testing utilities for NestJS + TypeORM applications. This package provides a robust foundation for writing API tests with database integration.
4
+
5
+ ## Features
6
+
7
+ - **DatabaseTestHelper**: Transaction-based test isolation and database cleanup
8
+ - **BaseFactory**: Abstract factory pattern for generating test data with Faker.js
9
+ - **EntityComparator**: Deep entity comparison with custom Jest matchers
10
+ - **Fixtures**: Pre-defined static test data with loader utilities
11
+ - **Jest Setup Template**: Ready-to-use Jest configuration with safety checks
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install nestjs-testfx --save-dev
17
+ ```
18
+
19
+ ### Peer Dependencies
20
+
21
+ Ensure you have these peer dependencies installed:
22
+
23
+ ```bash
24
+ npm install --save-dev \
25
+ @faker-js/faker \
26
+ @nestjs/common \
27
+ @nestjs/config \
28
+ @nestjs/testing \
29
+ @nestjs/typeorm \
30
+ jest \
31
+ typeorm
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ### 1. Set Up Jest Configuration
37
+
38
+ Copy the Jest setup template to your project:
39
+
40
+ ```typescript
41
+ // src/testing/jest.setup.ts
42
+ import { config } from 'dotenv';
43
+ import { createEntityEquivalentMatcher } from 'nestjs-testfx';
44
+
45
+ config({ path: '.test.env' });
46
+
47
+ jest.setTimeout(30000);
48
+
49
+ beforeAll(() => {
50
+ if (process.env.NODE_ENV !== 'test') {
51
+ throw new Error('Tests must run with NODE_ENV=test');
52
+ }
53
+ });
54
+
55
+ expect.extend({
56
+ toBeEntityEquivalent: createEntityEquivalentMatcher(),
57
+ });
58
+ ```
59
+
60
+ Update your `jest.config.js`:
61
+
62
+ ```javascript
63
+ module.exports = {
64
+ setupFilesAfterEnv: ['<rootDir>/src/testing/jest.setup.ts'],
65
+ // ... other config
66
+ };
67
+ ```
68
+
69
+ ### 2. Create Your DataSource
70
+
71
+ ```typescript
72
+ // src/testing/test-data-source.ts
73
+ import { DataSource } from 'typeorm';
74
+
75
+ export const testDataSource = new DataSource({
76
+ type: 'postgres', // or 'mssql', 'mysql'
77
+ host: process.env.DB_HOST,
78
+ port: parseInt(process.env.DB_PORT || '5432'),
79
+ username: process.env.DB_USERNAME,
80
+ password: process.env.DB_PASSWORD,
81
+ database: process.env.DB_DATABASE,
82
+ entities: ['src/**/*.entity.ts'],
83
+ synchronize: false,
84
+ });
85
+ ```
86
+
87
+ ### 3. Create Factories for Your Entities
88
+
89
+ ```typescript
90
+ // src/testing/factories/user.factory.ts
91
+ import { DataSource } from 'typeorm';
92
+ import { BaseFactory } from 'nestjs-testfx';
93
+ import { User } from '../../entities/user.entity';
94
+
95
+ export class UserFactory extends BaseFactory<User> {
96
+ constructor(dataSource: DataSource) {
97
+ super(dataSource, User);
98
+ }
99
+
100
+ build(overrides?: Partial<User>): User {
101
+ const fake = this.generateFakeData();
102
+
103
+ return this.createInstance(this.applyOverrides({
104
+ email: fake.email,
105
+ firstName: fake.firstName,
106
+ lastName: fake.lastName,
107
+ isActive: true,
108
+ createdAt: new Date(),
109
+ }, overrides));
110
+ }
111
+
112
+ async createAdmin(overrides?: Partial<User>): Promise<User> {
113
+ return this.create({
114
+ role: 'admin',
115
+ isActive: true,
116
+ ...overrides,
117
+ });
118
+ }
119
+ }
120
+ ```
121
+
122
+ ### 4. Write Your First Test
123
+
124
+ ```typescript
125
+ // src/users/users.service.spec.ts
126
+ import { DatabaseTestHelper } from 'nestjs-testfx';
127
+ import { testDataSource } from '../testing/test-data-source';
128
+ import { UserFactory } from '../testing/factories/user.factory';
129
+ import { UsersService } from './users.service';
130
+
131
+ describe('UsersService', () => {
132
+ let databaseHelper: DatabaseTestHelper;
133
+ let userFactory: UserFactory;
134
+ let service: UsersService;
135
+
136
+ beforeAll(async () => {
137
+ databaseHelper = await DatabaseTestHelper.fromDataSource(testDataSource, {
138
+ cleanupOrder: ['Order', 'User'],
139
+ dialect: 'postgres',
140
+ });
141
+ userFactory = new UserFactory(databaseHelper.getDataSource());
142
+ service = new UsersService(databaseHelper.getRepository(User));
143
+ });
144
+
145
+ afterAll(async () => {
146
+ await databaseHelper.close();
147
+ });
148
+
149
+ beforeEach(async () => {
150
+ await databaseHelper.cleanDatabase(['User']);
151
+ });
152
+
153
+ it('should find user by email', async () => {
154
+ const user = await userFactory.create({ email: 'test@example.com' });
155
+
156
+ const found = await service.findByEmail('test@example.com');
157
+
158
+ expect(found).toBeDefined();
159
+ expect(found?.id).toBe(user.id);
160
+ });
161
+ });
162
+ ```
163
+
164
+ ## API Reference
165
+
166
+ ### DatabaseTestHelper
167
+
168
+ Manages database connections and provides test isolation utilities.
169
+
170
+ ```typescript
171
+ import { DatabaseTestHelper } from 'nestjs-testfx';
172
+
173
+ // Create from existing DataSource
174
+ const helper = await DatabaseTestHelper.fromDataSource(dataSource, {
175
+ cleanupOrder: ['OrderItem', 'Order', 'User'],
176
+ dialect: 'postgres', // 'mssql' | 'postgres' | 'mysql'
177
+ });
178
+
179
+ // Transaction-based isolation
180
+ await helper.startTransaction();
181
+ const manager = helper.getTransactionManager();
182
+ await manager.save(User, userData);
183
+ await helper.rollbackTransaction(); // Undo all changes
184
+
185
+ // Clean specific tables
186
+ await helper.cleanDatabase(['User', 'Order']);
187
+
188
+ // Query helpers
189
+ const exists = await helper.entityExists(User, { email: 'test@example.com' });
190
+ const count = await helper.countEntities(User, { isActive: true });
191
+
192
+ // Execute in separate transaction (auto-commits)
193
+ const user = await helper.inNewTransaction(async (manager) => {
194
+ return manager.save(User, { email: 'test@example.com' });
195
+ });
196
+
197
+ // Close connection
198
+ await helper.close();
199
+ ```
200
+
201
+ ### BaseFactory
202
+
203
+ Abstract base class for entity factories.
204
+
205
+ ```typescript
206
+ import { BaseFactory, FactoryOptions } from 'nestjs-testfx';
207
+
208
+ class ProductFactory extends BaseFactory<Product> {
209
+ constructor(dataSource: DataSource) {
210
+ super(dataSource, Product);
211
+ }
212
+
213
+ build(overrides?: Partial<Product>): Product {
214
+ const fake = this.generateFakeData();
215
+ return this.createInstance(this.applyOverrides({
216
+ name: fake.name,
217
+ price: fake.decimal,
218
+ inStock: true,
219
+ }, overrides));
220
+ }
221
+
222
+ // Convenience methods
223
+ async createOutOfStock(overrides?: Partial<Product>): Promise<Product> {
224
+ return this.create({ inStock: false, ...overrides });
225
+ }
226
+ }
227
+
228
+ // Usage
229
+ const factory = new ProductFactory(dataSource);
230
+
231
+ // Build without saving
232
+ const product = factory.build({ name: 'Widget' });
233
+
234
+ // Create and save
235
+ const saved = await factory.create({ name: 'Gadget' });
236
+
237
+ // Create multiple
238
+ const products = await factory.createMany(10, { inStock: true });
239
+
240
+ // Create within transaction
241
+ const product = await factory.create(
242
+ { name: 'Test' },
243
+ { manager: transactionManager }
244
+ );
245
+
246
+ // Build without saving
247
+ const instance = await factory.create({}, { save: false });
248
+ ```
249
+
250
+ #### Available Fake Data
251
+
252
+ `generateFakeData()` provides:
253
+
254
+ | Property | Description |
255
+ |----------|-------------|
256
+ | `id` | Random integer (1-999999) |
257
+ | `email` | Random email address |
258
+ | `firstName` | Random first name |
259
+ | `lastName` | Random last name |
260
+ | `phone` | Random phone number |
261
+ | `address` | Random street address |
262
+ | `city` | Random city name |
263
+ | `state` | Random state/province |
264
+ | `postalCode` | Random zip/postal code |
265
+ | `country` | Random country name |
266
+ | `name` | Random company name |
267
+ | `description` | Random paragraph |
268
+ | `createdAt` | Random past date |
269
+ | `updatedAt` | Random recent date |
270
+ | `boolean` | Random boolean |
271
+ | `number` | Random integer (1-1000) |
272
+ | `decimal` | Random float (0-100, 2 decimals) |
273
+ | `datetime` | Random future date |
274
+ | `text` | Random sentences |
275
+ | `url` | Random URL |
276
+ | `priority` | 'low' \| 'medium' \| 'high' |
277
+ | `status` | 'active' \| 'inactive' \| 'pending' |
278
+
279
+ ### EntityComparator
280
+
281
+ Deep entity comparison with custom options.
282
+
283
+ ```typescript
284
+ import { EntityComparator } from 'nestjs-testfx';
285
+
286
+ // Basic comparison
287
+ const result = EntityComparator.compare(actual, expected);
288
+ console.log(result.areEqual); // true/false
289
+
290
+ // Ignore auto-generated fields
291
+ const result = EntityComparator.compare(savedUser, {
292
+ email: 'test@example.com',
293
+ firstName: 'John',
294
+ }, {
295
+ ignoreFields: ['id', 'createdAt', 'updatedAt'],
296
+ });
297
+
298
+ // Custom comparators
299
+ const result = EntityComparator.compare(entity1, entity2, {
300
+ customComparators: {
301
+ // Compare dates by day only
302
+ scheduledDate: (a, b) =>
303
+ new Date(a).toDateString() === new Date(b).toDateString(),
304
+ // Compare prices within tolerance
305
+ price: (a, b) => Math.abs(a - b) < 0.01,
306
+ },
307
+ });
308
+
309
+ // Compare subset of fields
310
+ const result = EntityComparator.compareSubset(fullEntity, {
311
+ email: 'test@example.com',
312
+ name: 'John',
313
+ });
314
+
315
+ // Format for error messages
316
+ if (!result.areEqual) {
317
+ console.log(EntityComparator.formatComparisonResult(result));
318
+ }
319
+ ```
320
+
321
+ ### Jest Custom Matcher
322
+
323
+ ```typescript
324
+ // In jest.setup.ts
325
+ import { createEntityEquivalentMatcher } from 'nestjs-testfx';
326
+
327
+ expect.extend({
328
+ toBeEntityEquivalent: createEntityEquivalentMatcher(),
329
+ });
330
+
331
+ // In tests
332
+ expect(actualUser).toBeEntityEquivalent(expectedUser);
333
+
334
+ expect(savedEntity).toBeEntityEquivalent(expectedData, {
335
+ ignoreFields: ['id', 'createdAt', 'updatedAt'],
336
+ });
337
+ ```
338
+
339
+ ### Fixtures
340
+
341
+ Pre-defined static test data.
342
+
343
+ ```typescript
344
+ // Define fixtures
345
+ export const ROLE_FIXTURES = {
346
+ ADMIN: { id: 1, name: 'Admin', description: 'Administrator' },
347
+ USER: { id: 2, name: 'User', description: 'Standard user' },
348
+ };
349
+
350
+ export function getAllRoleFixtures() {
351
+ return Object.values(ROLE_FIXTURES);
352
+ }
353
+
354
+ export function getRoleById(id: number) {
355
+ return Object.values(ROLE_FIXTURES).find(r => r.id === id);
356
+ }
357
+
358
+ // Load fixtures
359
+ class FixtureLoader {
360
+ constructor(private manager: EntityManager) {}
361
+
362
+ async loadRoles() {
363
+ for (const role of getAllRoleFixtures()) {
364
+ await this.manager.save(Role, role);
365
+ }
366
+ }
367
+ }
368
+ ```
369
+
370
+ ## Complete Test Example
371
+
372
+ ```typescript
373
+ import {
374
+ DatabaseTestHelper,
375
+ EntityComparator,
376
+ } from 'nestjs-testfx';
377
+ import { testDataSource } from '../testing/test-data-source';
378
+ import { UserFactory } from '../testing/factories/user.factory';
379
+ import { OrderFactory } from '../testing/factories/order.factory';
380
+ import { OrderService } from './order.service';
381
+
382
+ describe('OrderService', () => {
383
+ let databaseHelper: DatabaseTestHelper;
384
+ let userFactory: UserFactory;
385
+ let orderFactory: OrderFactory;
386
+ let service: OrderService;
387
+
388
+ beforeAll(async () => {
389
+ databaseHelper = await DatabaseTestHelper.fromDataSource(testDataSource, {
390
+ cleanupOrder: ['OrderItem', 'Order', 'User'],
391
+ dialect: 'postgres',
392
+ });
393
+
394
+ userFactory = new UserFactory(databaseHelper.getDataSource());
395
+ orderFactory = new OrderFactory(databaseHelper.getDataSource());
396
+
397
+ service = new OrderService(
398
+ databaseHelper.getRepository(Order),
399
+ databaseHelper.getRepository(User),
400
+ );
401
+ });
402
+
403
+ afterAll(async () => {
404
+ await databaseHelper.close();
405
+ });
406
+
407
+ beforeEach(async () => {
408
+ await databaseHelper.cleanDatabase(['OrderItem', 'Order', 'User']);
409
+ });
410
+
411
+ describe('createOrder', () => {
412
+ it('should create order for user', async () => {
413
+ // Arrange
414
+ const user = await userFactory.create({ email: 'buyer@example.com' });
415
+
416
+ // Act
417
+ const order = await service.createOrder(user.id, {
418
+ items: [{ productId: 1, quantity: 2 }],
419
+ });
420
+
421
+ // Assert
422
+ expect(order.id).toBeDefined();
423
+ expect(order.userId).toBe(user.id);
424
+ expect(order.status).toBe('pending');
425
+ });
426
+
427
+ it('should calculate total correctly', async () => {
428
+ const user = await userFactory.create();
429
+ const order = await service.createOrder(user.id, {
430
+ items: [
431
+ { productId: 1, quantity: 2, price: 10 },
432
+ { productId: 2, quantity: 1, price: 25 },
433
+ ],
434
+ });
435
+
436
+ expect(order.total).toBe(45); // (2 * 10) + (1 * 25)
437
+ });
438
+ });
439
+
440
+ describe('cancelOrder', () => {
441
+ it('should cancel pending order', async () => {
442
+ const user = await userFactory.create();
443
+ const order = await orderFactory.createPending(user.id);
444
+
445
+ const cancelled = await service.cancelOrder(order.id);
446
+
447
+ expect(cancelled.status).toBe('cancelled');
448
+ });
449
+
450
+ it('should not cancel completed order', async () => {
451
+ const user = await userFactory.create();
452
+ const order = await orderFactory.createCompleted(user.id);
453
+
454
+ await expect(service.cancelOrder(order.id)).rejects.toThrow(
455
+ 'Cannot cancel completed order'
456
+ );
457
+ });
458
+ });
459
+
460
+ describe('with transactions', () => {
461
+ it('should rollback on error', async () => {
462
+ const user = await userFactory.create();
463
+
464
+ await expect(
465
+ service.createOrderWithPayment(user.id, {
466
+ items: [{ productId: 1, quantity: 1 }],
467
+ paymentMethod: 'invalid',
468
+ })
469
+ ).rejects.toThrow();
470
+
471
+ // Verify no order was created
472
+ const count = await databaseHelper.countEntities(Order, { userId: user.id });
473
+ expect(count).toBe(0);
474
+ });
475
+ });
476
+ });
477
+ ```
478
+
479
+ ## Factory Manager Pattern
480
+
481
+ For larger projects, use a factory manager to centralize factory access:
482
+
483
+ ```typescript
484
+ // src/testing/factories/index.ts
485
+ import { DataSource } from 'typeorm';
486
+ import { UserFactory } from './user.factory';
487
+ import { OrderFactory } from './order.factory';
488
+ import { ProductFactory } from './product.factory';
489
+
490
+ export class FactoryManager {
491
+ public readonly user: UserFactory;
492
+ public readonly order: OrderFactory;
493
+ public readonly product: ProductFactory;
494
+
495
+ constructor(dataSource: DataSource) {
496
+ this.user = new UserFactory(dataSource);
497
+ this.order = new OrderFactory(dataSource);
498
+ this.product = new ProductFactory(dataSource);
499
+ }
500
+ }
501
+
502
+ // Usage in tests
503
+ describe('Service', () => {
504
+ let factories: FactoryManager;
505
+
506
+ beforeAll(async () => {
507
+ factories = new FactoryManager(dataSource);
508
+ });
509
+
510
+ it('test', async () => {
511
+ const user = await factories.user.create();
512
+ const order = await factories.order.createForUser(user.id);
513
+ });
514
+ });
515
+ ```
516
+
517
+ ## Test Data Helper Pattern
518
+
519
+ For complex test scenarios, create a test data helper:
520
+
521
+ ```typescript
522
+ // src/testing/helpers/test-data.helper.ts
523
+ import { DatabaseTestHelper } from 'nestjs-testfx';
524
+ import { FactoryManager } from '../factories';
525
+ import { ROLE_FIXTURES } from '../fixtures';
526
+
527
+ export class TestDataHelper {
528
+ constructor(
529
+ private databaseHelper: DatabaseTestHelper,
530
+ private factories: FactoryManager,
531
+ ) {}
532
+
533
+ async setupFixtures(manager?: EntityManager) {
534
+ const em = manager || this.databaseHelper.getDataSource().manager;
535
+ for (const role of Object.values(ROLE_FIXTURES)) {
536
+ await em.save(Role, role);
537
+ }
538
+ }
539
+
540
+ async createTestScenario(name: string) {
541
+ switch (name) {
542
+ case 'e-commerce':
543
+ return this.createEcommerceScenario();
544
+ case 'multi-tenant':
545
+ return this.createMultiTenantScenario();
546
+ default:
547
+ throw new Error(`Unknown scenario: ${name}`);
548
+ }
549
+ }
550
+
551
+ private async createEcommerceScenario() {
552
+ await this.setupFixtures();
553
+
554
+ const admin = await this.factories.user.createAdmin();
555
+ const customers = await this.factories.user.createMany(5);
556
+ const products = await this.factories.product.createMany(20);
557
+
558
+ return { admin, customers, products };
559
+ }
560
+ }
561
+ ```
562
+
563
+ ## Environment Configuration
564
+
565
+ Create a `.test.env` or `.ci.env` file:
566
+
567
+ ```env
568
+ NODE_ENV=test
569
+ DB_HOST=localhost
570
+ DB_PORT=5432
571
+ DB_DATABASE=myapp_test
572
+ DB_USERNAME=postgres
573
+ DB_PASSWORD=postgres
574
+
575
+ # Optional: reduce log noise
576
+ LOG_LEVEL=warn
577
+ ```
578
+
579
+ ## Best Practices
580
+
581
+ ### 1. Clean Tables in Correct Order
582
+
583
+ ```typescript
584
+ // Define cleanup order respecting foreign keys
585
+ const cleanupOrder = [
586
+ 'OrderItem', // References Order
587
+ 'Order', // References User
588
+ 'UserRole', // References User and Role
589
+ 'User', // Parent
590
+ 'Role', // Parent
591
+ ];
592
+
593
+ const helper = await DatabaseTestHelper.fromDataSource(dataSource, {
594
+ cleanupOrder,
595
+ });
596
+ ```
597
+
598
+ ### 2. Use Transaction Isolation for Speed
599
+
600
+ ```typescript
601
+ beforeEach(async () => {
602
+ await databaseHelper.startTransaction();
603
+ });
604
+
605
+ afterEach(async () => {
606
+ await databaseHelper.rollbackTransaction();
607
+ });
608
+ ```
609
+
610
+ ### 3. Create Meaningful Factory Methods
611
+
612
+ ```typescript
613
+ class OrderFactory extends BaseFactory<Order> {
614
+ // Good: Descriptive, purpose-clear methods
615
+ async createPendingForNewCustomer() { ... }
616
+ async createCompletedWithItems(itemCount: number) { ... }
617
+ async createHighValueOrder(minAmount: number) { ... }
618
+ }
619
+ ```
620
+
621
+ ### 4. Use Fixtures for Reference Data
622
+
623
+ ```typescript
624
+ // Use fixtures for data with known IDs
625
+ const userRole = ROLE_FIXTURES.USER;
626
+
627
+ // Use factories for variable test data
628
+ const user = await userFactory.create({ roleId: userRole.id });
629
+ ```
630
+
631
+ ## Troubleshooting
632
+
633
+ ### "Tests must use a test database"
634
+
635
+ Ensure your `.test.env` has a database name containing 'test' or 'ci':
636
+
637
+ ```env
638
+ DB_DATABASE=myapp_test # ✓
639
+ DB_DATABASE=myapp_ci # ✓
640
+ DB_DATABASE=myapp # ✗ Will fail safety check
641
+ ```
642
+
643
+ ### Foreign Key Constraint Errors
644
+
645
+ Update `cleanupOrder` to delete child tables before parents:
646
+
647
+ ```typescript
648
+ const cleanupOrder = [
649
+ 'ChildTable', // First
650
+ 'ParentTable', // Last
651
+ ];
652
+ ```
653
+
654
+ ### Transaction Already Started
655
+
656
+ Ensure you call `rollbackTransaction()` or `commitTransaction()` before starting a new one:
657
+
658
+ ```typescript
659
+ afterEach(async () => {
660
+ await databaseHelper.rollbackTransaction();
661
+ });
662
+ ```
663
+
664
+ ## License
665
+
666
+ MIT
@@ -0,0 +1,49 @@
1
+ import { Repository, EntityManager, DataSource, ObjectLiteral } from 'typeorm';
2
+ export interface FactoryOptions {
3
+ save?: boolean;
4
+ manager?: EntityManager;
5
+ count?: number;
6
+ }
7
+ export interface EntityFactory<T> {
8
+ build(overrides?: Partial<T>): T;
9
+ create(overrides?: Partial<T>, options?: FactoryOptions): Promise<T>;
10
+ createMany(count: number, overrides?: Partial<T>, options?: FactoryOptions): Promise<T[]>;
11
+ }
12
+ export declare abstract class BaseFactory<T extends ObjectLiteral> implements EntityFactory<T> {
13
+ protected dataSource: DataSource;
14
+ protected repository: Repository<T>;
15
+ protected entityClass: new () => T;
16
+ constructor(dataSource: DataSource, entityClass: new () => T);
17
+ abstract build(overrides?: Partial<T>): T;
18
+ create(overrides?: Partial<T>, options?: FactoryOptions): Promise<T>;
19
+ createMany(count: number, overrides?: Partial<T>, options?: FactoryOptions): Promise<T[]>;
20
+ protected generateFakeData(): {
21
+ id: number;
22
+ email: string;
23
+ firstName: string;
24
+ lastName: string;
25
+ phone: string;
26
+ address: string;
27
+ city: string;
28
+ state: string;
29
+ postalCode: string;
30
+ country: string;
31
+ name: string;
32
+ description: string;
33
+ createdAt: Date;
34
+ updatedAt: Date;
35
+ boolean: boolean;
36
+ number: number;
37
+ decimal: number;
38
+ datetime: Date;
39
+ text: string;
40
+ url: string;
41
+ priority: "low" | "medium" | "high";
42
+ status: "active" | "inactive" | "pending";
43
+ code: "installation" | "inspection" | "maintenance" | "repair";
44
+ randomMinutes: number;
45
+ };
46
+ protected applyOverrides<K>(baseData: K, overrides?: Partial<K>): K;
47
+ protected createInstance(data: Partial<T>): T;
48
+ protected getRepository(): Repository<T>;
49
+ }