@geekmidas/testkit 0.0.17 → 0.1.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/README.md +302 -199
- package/dist/{Factory-CRquB4vz.d.mts → Factory-Cmr3s3-s.d.mts} +2 -2
- package/dist/Factory.d.mts +2 -2
- package/dist/{KyselyFactory-DRQ83r0o.d.cts → KyselyFactory-BFygzOlO.d.cts} +21 -8
- package/dist/{KyselyFactory-BDS_QqRT.d.mts → KyselyFactory-BTdygZ-i.d.mts} +23 -10
- package/dist/{KyselyFactory-BcYkC0t2.mjs → KyselyFactory-CXY5gJk2.mjs} +25 -12
- package/dist/KyselyFactory-CXY5gJk2.mjs.map +1 -0
- package/dist/{KyselyFactory-Cf0o2YxO.cjs → KyselyFactory-DaaCykWP.cjs} +25 -12
- package/dist/KyselyFactory-DaaCykWP.cjs.map +1 -0
- package/dist/KyselyFactory.cjs +1 -1
- package/dist/KyselyFactory.d.cts +1 -1
- package/dist/KyselyFactory.d.mts +3 -3
- package/dist/KyselyFactory.mjs +1 -1
- package/dist/{ObjectionFactory-C3tHvX1d.d.mts → ObjectionFactory-BagGjikT.d.mts} +26 -13
- package/dist/{ObjectionFactory-C4X78k0B.d.cts → ObjectionFactory-CeSIN3kZ.d.cts} +24 -11
- package/dist/{ObjectionFactory-CDriunkS.cjs → ObjectionFactory-Eb04AOnv.cjs} +28 -15
- package/dist/ObjectionFactory-Eb04AOnv.cjs.map +1 -0
- package/dist/{ObjectionFactory-8hebmnai.mjs → ObjectionFactory-zf2fLKrL.mjs} +28 -15
- package/dist/ObjectionFactory-zf2fLKrL.mjs.map +1 -0
- package/dist/ObjectionFactory.cjs +1 -1
- package/dist/ObjectionFactory.d.cts +1 -1
- package/dist/ObjectionFactory.d.mts +3 -3
- package/dist/ObjectionFactory.mjs +1 -1
- package/dist/{VitestKyselyTransactionIsolator-COCVfvfr.d.mts → VitestKyselyTransactionIsolator-4HOeLQ0d.d.cts} +2 -2
- package/dist/{VitestKyselyTransactionIsolator-Cst3vFjb.cjs → VitestKyselyTransactionIsolator-DX_VPKS-.cjs} +2 -2
- package/dist/{VitestKyselyTransactionIsolator-Cst3vFjb.cjs.map → VitestKyselyTransactionIsolator-DX_VPKS-.cjs.map} +1 -1
- package/dist/{VitestKyselyTransactionIsolator-DYUYVEh9.d.cts → VitestKyselyTransactionIsolator-DnyZMaA-.d.mts} +2 -2
- package/dist/{VitestKyselyTransactionIsolator-BxjlD1YM.mjs → VitestKyselyTransactionIsolator-XDL3ngs_.mjs} +2 -2
- package/dist/{VitestKyselyTransactionIsolator-BxjlD1YM.mjs.map → VitestKyselyTransactionIsolator-XDL3ngs_.mjs.map} +1 -1
- package/dist/VitestKyselyTransactionIsolator.cjs +2 -2
- package/dist/VitestKyselyTransactionIsolator.d.cts +2 -2
- package/dist/VitestKyselyTransactionIsolator.d.mts +2 -2
- package/dist/VitestKyselyTransactionIsolator.mjs +2 -2
- package/dist/{VitestObjectionTransactionIsolator-b973r9O1.d.mts → VitestObjectionTransactionIsolator-COVDlpEo.d.cts} +2 -2
- package/dist/{VitestObjectionTransactionIsolator-DzeF4UAq.cjs → VitestObjectionTransactionIsolator-D_tlOtq8.cjs} +2 -2
- package/dist/{VitestObjectionTransactionIsolator-DzeF4UAq.cjs.map → VitestObjectionTransactionIsolator-D_tlOtq8.cjs.map} +1 -1
- package/dist/{VitestObjectionTransactionIsolator-BU-jXEhz.mjs → VitestObjectionTransactionIsolator-_EhJKu_O.mjs} +2 -2
- package/dist/{VitestObjectionTransactionIsolator-BU-jXEhz.mjs.map → VitestObjectionTransactionIsolator-_EhJKu_O.mjs.map} +1 -1
- package/dist/{VitestObjectionTransactionIsolator-CJ4ds5Qv.d.cts → VitestObjectionTransactionIsolator-lZUSz1w0.d.mts} +2 -2
- package/dist/VitestObjectionTransactionIsolator.cjs +2 -2
- package/dist/VitestObjectionTransactionIsolator.d.cts +2 -2
- package/dist/VitestObjectionTransactionIsolator.d.mts +2 -2
- package/dist/VitestObjectionTransactionIsolator.mjs +2 -2
- package/dist/{VitestTransactionIsolator-CskiiJbW.mjs → VitestTransactionIsolator-BIaMs4c2.mjs} +40 -2
- package/dist/VitestTransactionIsolator-BIaMs4c2.mjs.map +1 -0
- package/dist/{VitestTransactionIsolator-BQ5FpLtC.cjs → VitestTransactionIsolator-BKIrj3Uy.cjs} +45 -1
- package/dist/VitestTransactionIsolator-BKIrj3Uy.cjs.map +1 -0
- package/dist/{VitestTransactionIsolator-DdLNODZg.d.cts → VitestTransactionIsolator-CyG_i_Nj.d.cts} +61 -3
- package/dist/{VitestTransactionIsolator-CsfJBxcb.d.mts → VitestTransactionIsolator-DWDbnITQ.d.mts} +61 -3
- package/dist/VitestTransactionIsolator.cjs +3 -2
- package/dist/VitestTransactionIsolator.d.cts +2 -2
- package/dist/VitestTransactionIsolator.d.mts +2 -2
- package/dist/VitestTransactionIsolator.mjs +2 -2
- package/dist/better-auth.cjs +8 -11
- package/dist/better-auth.cjs.map +1 -1
- package/dist/better-auth.d.cts +2 -2
- package/dist/better-auth.d.mts +2 -2
- package/dist/better-auth.mjs +8 -11
- package/dist/better-auth.mjs.map +1 -1
- package/dist/{directory-B4oYx02C.d.mts → directory-BXavAeJZ.d.mts} +3 -3
- package/dist/{directory-BUcnztHI.d.cts → directory-DlkPEzL4.d.cts} +3 -3
- package/dist/{faker-Br8MzXil.d.mts → faker-DHh7xs4u.d.mts} +3 -3
- package/dist/faker.d.mts +1 -1
- package/dist/kysely.cjs +58 -4
- package/dist/kysely.cjs.map +1 -1
- package/dist/kysely.d.cts +58 -5
- package/dist/kysely.d.mts +59 -6
- package/dist/kysely.mjs +57 -5
- package/dist/kysely.mjs.map +1 -1
- package/dist/objection.cjs +54 -4
- package/dist/objection.cjs.map +1 -1
- package/dist/objection.d.cts +54 -5
- package/dist/objection.d.mts +55 -6
- package/dist/objection.mjs +53 -5
- package/dist/objection.mjs.map +1 -1
- package/dist/os/directory.d.cts +1 -1
- package/dist/os/directory.d.mts +1 -1
- package/dist/os/index.d.cts +1 -1
- package/dist/os/index.d.mts +1 -1
- package/package.json +7 -3
- package/src/KyselyFactory.ts +29 -16
- package/src/ObjectionFactory.ts +34 -19
- package/src/VitestTransactionIsolator.ts +110 -2
- package/src/__tests__/KyselyFactory.spec.ts +10 -10
- package/src/__tests__/ObjectionFactory.spec.ts +9 -12
- package/src/__tests__/integration.spec.ts +171 -14
- package/src/better-auth.ts +13 -15
- package/src/kysely.ts +66 -0
- package/src/objection.ts +61 -0
- package/dist/KyselyFactory-BcYkC0t2.mjs.map +0 -1
- package/dist/KyselyFactory-Cf0o2YxO.cjs.map +0 -1
- package/dist/ObjectionFactory-8hebmnai.mjs.map +0 -1
- package/dist/ObjectionFactory-CDriunkS.cjs.map +0 -1
- package/dist/VitestTransactionIsolator-BQ5FpLtC.cjs.map +0 -1
- package/dist/VitestTransactionIsolator-CskiiJbW.mjs.map +0 -1
package/dist/objection.d.mts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import "./faker-
|
|
2
|
-
import "./Factory-
|
|
3
|
-
import { ObjectionFactory } from "./ObjectionFactory-
|
|
1
|
+
import { FakerFactory, faker } from "./faker-DHh7xs4u.mjs";
|
|
2
|
+
import "./Factory-Cmr3s3-s.mjs";
|
|
3
|
+
import { ObjectionFactory } from "./ObjectionFactory-BagGjikT.mjs";
|
|
4
4
|
import "./PostgresMigrator-DQaRxoaY.mjs";
|
|
5
5
|
import { PostgresObjectionMigrator } from "./PostgresObjectionMigrator-D_hCcrQu.mjs";
|
|
6
|
-
import { DatabaseConnection, IsolationLevel } from "./VitestTransactionIsolator-
|
|
7
|
-
import { VitestObjectionTransactionIsolator } from "./VitestObjectionTransactionIsolator-
|
|
6
|
+
import { DatabaseConnection, DatabaseFixtures, FixtureCreators, IsolationLevel } from "./VitestTransactionIsolator-DWDbnITQ.mjs";
|
|
7
|
+
import { VitestObjectionTransactionIsolator } from "./VitestObjectionTransactionIsolator-lZUSz1w0.mjs";
|
|
8
8
|
import { TestAPI } from "vitest";
|
|
9
9
|
import { Knex } from "knex";
|
|
10
10
|
|
|
@@ -84,6 +84,55 @@ import { Knex } from "knex";
|
|
|
84
84
|
declare function wrapVitestObjectionTransaction(api: TestAPI, conn: DatabaseConnection<Knex>, setup?: (trx: Knex.Transaction) => Promise<void>, level?: IsolationLevel): TestAPI<{
|
|
85
85
|
trx: Knex.Transaction<any, any[]>;
|
|
86
86
|
}>;
|
|
87
|
+
/**
|
|
88
|
+
* Extends an Objection.js transaction-wrapped test with additional fixtures.
|
|
89
|
+
* Each fixture receives the transaction and can create dependencies like factories or repositories.
|
|
90
|
+
*
|
|
91
|
+
* @template Extended - The type of additional fixtures to provide
|
|
92
|
+
* @param wrappedTest - The base wrapped test from wrapVitestObjectionTransaction
|
|
93
|
+
* @param fixtures - Object mapping fixture names to creator functions
|
|
94
|
+
* @returns An extended test API with both trx and the additional fixtures
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* import { test } from 'vitest';
|
|
99
|
+
* import { wrapVitestObjectionTransaction, extendWithFixtures, ObjectionFactory } from '@geekmidas/testkit/objection';
|
|
100
|
+
* import { User } from './models';
|
|
101
|
+
*
|
|
102
|
+
* // Define your builders
|
|
103
|
+
* const builders = {
|
|
104
|
+
* user: ObjectionFactory.createBuilder(User, ({ faker }) => ({
|
|
105
|
+
* name: faker.person.fullName(),
|
|
106
|
+
* email: faker.internet.email(),
|
|
107
|
+
* })),
|
|
108
|
+
* };
|
|
109
|
+
*
|
|
110
|
+
* // Create base wrapped test
|
|
111
|
+
* const baseTest = wrapVitestObjectionTransaction(test, knex, createTestTables);
|
|
112
|
+
*
|
|
113
|
+
* // Extend with fixtures - each fixture receives the transaction
|
|
114
|
+
* const it = extendWithFixtures<{ factory: ObjectionFactory<typeof builders, {}> }>(
|
|
115
|
+
* baseTest,
|
|
116
|
+
* {
|
|
117
|
+
* factory: (trx) => new ObjectionFactory(builders, {}, trx),
|
|
118
|
+
* }
|
|
119
|
+
* );
|
|
120
|
+
*
|
|
121
|
+
* // Use in tests - both trx and factory are available
|
|
122
|
+
* it('should create user with factory', async ({ trx, factory }) => {
|
|
123
|
+
* const user = await factory.insert('user', { name: 'Test User' });
|
|
124
|
+
* expect(user.id).toBeDefined();
|
|
125
|
+
*
|
|
126
|
+
* // Verify in database
|
|
127
|
+
* const found = await User.query(trx).findById(user.id);
|
|
128
|
+
* expect(found?.name).toBe('Test User');
|
|
129
|
+
* });
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
declare function extendWithFixtures<Extended extends Record<string, unknown>, T extends ReturnType<TestAPI['extend']> = ReturnType<TestAPI['extend']>>(wrappedTest: T, fixtures: FixtureCreators<Knex.Transaction, Extended>): T & {
|
|
133
|
+
<C extends object>(name: string, fn: (context: DatabaseFixtures<Knex.Transaction<any, any[]>, object> & Extended & C) => Promise<void>): void;
|
|
134
|
+
<C extends object>(name: string, options: object, fn: (context: DatabaseFixtures<Knex.Transaction<any, any[]>, object> & Extended & C) => Promise<void>): void;
|
|
135
|
+
};
|
|
87
136
|
//#endregion
|
|
88
|
-
export { IsolationLevel, ObjectionFactory, PostgresObjectionMigrator, VitestObjectionTransactionIsolator, wrapVitestObjectionTransaction };
|
|
137
|
+
export { FakerFactory, FixtureCreators, IsolationLevel, ObjectionFactory, PostgresObjectionMigrator, VitestObjectionTransactionIsolator, extendWithFixtures, faker, wrapVitestObjectionTransaction };
|
|
89
138
|
//# sourceMappingURL=objection.d.mts.map
|
package/dist/objection.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import "./Factory-z2m01hMj.mjs";
|
|
2
|
-
import "./faker-BGKYFoCT.mjs";
|
|
3
|
-
import { ObjectionFactory } from "./ObjectionFactory-
|
|
2
|
+
import { faker } from "./faker-BGKYFoCT.mjs";
|
|
3
|
+
import { ObjectionFactory } from "./ObjectionFactory-zf2fLKrL.mjs";
|
|
4
4
|
import "./PostgresMigrator-DbuJGAVy.mjs";
|
|
5
5
|
import { PostgresObjectionMigrator } from "./PostgresObjectionMigrator-DPj2pOpX.mjs";
|
|
6
|
-
import { IsolationLevel } from "./VitestTransactionIsolator-
|
|
7
|
-
import { VitestObjectionTransactionIsolator } from "./VitestObjectionTransactionIsolator-
|
|
6
|
+
import { IsolationLevel, extendWithFixtures } from "./VitestTransactionIsolator-BIaMs4c2.mjs";
|
|
7
|
+
import { VitestObjectionTransactionIsolator } from "./VitestObjectionTransactionIsolator-_EhJKu_O.mjs";
|
|
8
8
|
|
|
9
9
|
//#region src/objection.ts
|
|
10
10
|
/**
|
|
@@ -82,7 +82,55 @@ function wrapVitestObjectionTransaction(api, conn, setup, level = IsolationLevel
|
|
|
82
82
|
const wrapper = new VitestObjectionTransactionIsolator(api);
|
|
83
83
|
return wrapper.wrapVitestWithTransaction(conn, setup, level);
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Extends an Objection.js transaction-wrapped test with additional fixtures.
|
|
87
|
+
* Each fixture receives the transaction and can create dependencies like factories or repositories.
|
|
88
|
+
*
|
|
89
|
+
* @template Extended - The type of additional fixtures to provide
|
|
90
|
+
* @param wrappedTest - The base wrapped test from wrapVitestObjectionTransaction
|
|
91
|
+
* @param fixtures - Object mapping fixture names to creator functions
|
|
92
|
+
* @returns An extended test API with both trx and the additional fixtures
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* import { test } from 'vitest';
|
|
97
|
+
* import { wrapVitestObjectionTransaction, extendWithFixtures, ObjectionFactory } from '@geekmidas/testkit/objection';
|
|
98
|
+
* import { User } from './models';
|
|
99
|
+
*
|
|
100
|
+
* // Define your builders
|
|
101
|
+
* const builders = {
|
|
102
|
+
* user: ObjectionFactory.createBuilder(User, ({ faker }) => ({
|
|
103
|
+
* name: faker.person.fullName(),
|
|
104
|
+
* email: faker.internet.email(),
|
|
105
|
+
* })),
|
|
106
|
+
* };
|
|
107
|
+
*
|
|
108
|
+
* // Create base wrapped test
|
|
109
|
+
* const baseTest = wrapVitestObjectionTransaction(test, knex, createTestTables);
|
|
110
|
+
*
|
|
111
|
+
* // Extend with fixtures - each fixture receives the transaction
|
|
112
|
+
* const it = extendWithFixtures<{ factory: ObjectionFactory<typeof builders, {}> }>(
|
|
113
|
+
* baseTest,
|
|
114
|
+
* {
|
|
115
|
+
* factory: (trx) => new ObjectionFactory(builders, {}, trx),
|
|
116
|
+
* }
|
|
117
|
+
* );
|
|
118
|
+
*
|
|
119
|
+
* // Use in tests - both trx and factory are available
|
|
120
|
+
* it('should create user with factory', async ({ trx, factory }) => {
|
|
121
|
+
* const user = await factory.insert('user', { name: 'Test User' });
|
|
122
|
+
* expect(user.id).toBeDefined();
|
|
123
|
+
*
|
|
124
|
+
* // Verify in database
|
|
125
|
+
* const found = await User.query(trx).findById(user.id);
|
|
126
|
+
* expect(found?.name).toBe('Test User');
|
|
127
|
+
* });
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
function extendWithFixtures$1(wrappedTest, fixtures) {
|
|
131
|
+
return extendWithFixtures(wrappedTest, fixtures);
|
|
132
|
+
}
|
|
85
133
|
|
|
86
134
|
//#endregion
|
|
87
|
-
export { IsolationLevel, ObjectionFactory, PostgresObjectionMigrator, VitestObjectionTransactionIsolator, wrapVitestObjectionTransaction };
|
|
135
|
+
export { IsolationLevel, ObjectionFactory, PostgresObjectionMigrator, VitestObjectionTransactionIsolator, extendWithFixtures$1 as extendWithFixtures, faker, wrapVitestObjectionTransaction };
|
|
88
136
|
//# sourceMappingURL=objection.mjs.map
|
package/dist/objection.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"objection.mjs","names":["api: TestAPI","conn: DatabaseConnection<Knex>","setup?: (trx: Knex.Transaction) => Promise<void>","level: IsolationLevel"],"sources":["../src/objection.ts"],"sourcesContent":["import type { Knex } from 'knex';\nimport type { TestAPI } from 'vitest';\nimport { VitestObjectionTransactionIsolator } from './VitestObjectionTransactionIsolator';\nimport {\n type DatabaseConnection,\n IsolationLevel,\n} from './VitestTransactionIsolator';\n\n/**\n * Objection.js-specific exports for test utilities.\n * Provides factory implementation for creating test data with Objection.js ORM\n * and transaction isolation for test suites.\n */\n\nexport { ObjectionFactory } from './ObjectionFactory';\nexport { VitestObjectionTransactionIsolator } from './VitestObjectionTransactionIsolator';\nexport { IsolationLevel } from './VitestTransactionIsolator';\nexport { PostgresObjectionMigrator } from './PostgresObjectionMigrator';\n\n/**\n * Creates a wrapped Vitest test API with automatic transaction rollback for Objection.js.\n * Each test runs in an isolated database transaction that is rolled back after completion.\n * This ensures tests don't affect each other's data and run faster than truncating tables.\n *\n * @param api - The Vitest test API (usually `test` from vitest)\n * @param conn - The Knex database connection instance\n * @param setup - Optional setup function to run before each test in the transaction\n * @param level - Transaction isolation level (defaults to REPEATABLE_READ)\n * @returns A wrapped test API that provides transaction isolation\n *\n * @example\n * ```typescript\n * import { test } from 'vitest';\n * import { wrapVitestObjectionTransaction } from '@geekmidas/testkit/objection';\n * import { knex } from './database';\n * import { User, Post } from './models';\n *\n * // Create isolated test with automatic rollback\n * const isolatedTest = wrapVitestObjectionTransaction(test, knex);\n *\n * // Use in tests - each test gets its own transaction\n * isolatedTest('should create user', async ({ trx }) => {\n * const user = await User.query(trx)\n * .insert({ name: 'Test User', email: 'test@example.com' });\n *\n * expect(user).toBeDefined();\n * // User is automatically rolled back after test\n * });\n *\n * // With setup function for common test data\n * const testWithSetup = wrapVitestObjectionTransaction(\n * test,\n * knex,\n * async (trx) => {\n * // Create common test data\n * await knex('settings')\n * .transacting(trx)\n * .insert({ key: 'test_mode', value: 'true' });\n * }\n * );\n *\n * testWithSetup('should have test settings', async ({ trx }) => {\n * const setting = await knex('settings')\n * .transacting(trx)\n * .where('key', 'test_mode')\n * .first();\n *\n * expect(setting?.value).toBe('true');\n * });\n *\n * // Example with factory and transaction\n * const isolatedTest = wrapVitestObjectionTransaction(test, knex);\n * const factory = new ObjectionFactory(builders, seeds, knex);\n *\n * isolatedTest('creates related data', async ({ trx }) => {\n * // Factory can use the transaction\n * const user = await User.query(trx).insert({ name: 'Author' });\n * const posts = await Post.query(trx).insert([\n * { title: 'Post 1', userId: user.id },\n * { title: 'Post 2', userId: user.id }\n * ]);\n *\n * const userWithPosts = await User.query(trx)\n * .findById(user.id)\n * .withGraphFetched('posts');\n *\n * expect(userWithPosts.posts).toHaveLength(2);\n * });\n * ```\n */\nexport function wrapVitestObjectionTransaction(\n api: TestAPI,\n conn: DatabaseConnection<Knex>,\n setup?: (trx: Knex.Transaction) => Promise<void>,\n level: IsolationLevel = IsolationLevel.REPEATABLE_READ,\n) {\n const wrapper = new VitestObjectionTransactionIsolator(api);\n\n return wrapper.wrapVitestWithTransaction(conn, setup, level);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"objection.mjs","names":["api: TestAPI","conn: DatabaseConnection<Knex>","setup?: (trx: Knex.Transaction) => Promise<void>","level: IsolationLevel","extendWithFixtures","wrappedTest: T","fixtures: FixtureCreators<Knex.Transaction, Extended>"],"sources":["../src/objection.ts"],"sourcesContent":["import type { Knex } from 'knex';\nimport type { TestAPI } from 'vitest';\nimport { VitestObjectionTransactionIsolator } from './VitestObjectionTransactionIsolator';\nimport {\n type DatabaseConnection,\n type FixtureCreators,\n IsolationLevel,\n extendWithFixtures as baseExtendWithFixtures,\n} from './VitestTransactionIsolator';\n\n/**\n * Objection.js-specific exports for test utilities.\n * Provides factory implementation for creating test data with Objection.js ORM\n * and transaction isolation for test suites.\n */\n\nexport { ObjectionFactory } from './ObjectionFactory';\nexport { VitestObjectionTransactionIsolator } from './VitestObjectionTransactionIsolator';\nexport { IsolationLevel } from './VitestTransactionIsolator';\nexport { PostgresObjectionMigrator } from './PostgresObjectionMigrator';\nexport type { FixtureCreators } from './VitestTransactionIsolator';\n\n// Re-export faker and FakerFactory for type portability in declaration files\nexport { faker, type FakerFactory } from './faker';\n\n/**\n * Creates a wrapped Vitest test API with automatic transaction rollback for Objection.js.\n * Each test runs in an isolated database transaction that is rolled back after completion.\n * This ensures tests don't affect each other's data and run faster than truncating tables.\n *\n * @param api - The Vitest test API (usually `test` from vitest)\n * @param conn - The Knex database connection instance\n * @param setup - Optional setup function to run before each test in the transaction\n * @param level - Transaction isolation level (defaults to REPEATABLE_READ)\n * @returns A wrapped test API that provides transaction isolation\n *\n * @example\n * ```typescript\n * import { test } from 'vitest';\n * import { wrapVitestObjectionTransaction } from '@geekmidas/testkit/objection';\n * import { knex } from './database';\n * import { User, Post } from './models';\n *\n * // Create isolated test with automatic rollback\n * const isolatedTest = wrapVitestObjectionTransaction(test, knex);\n *\n * // Use in tests - each test gets its own transaction\n * isolatedTest('should create user', async ({ trx }) => {\n * const user = await User.query(trx)\n * .insert({ name: 'Test User', email: 'test@example.com' });\n *\n * expect(user).toBeDefined();\n * // User is automatically rolled back after test\n * });\n *\n * // With setup function for common test data\n * const testWithSetup = wrapVitestObjectionTransaction(\n * test,\n * knex,\n * async (trx) => {\n * // Create common test data\n * await knex('settings')\n * .transacting(trx)\n * .insert({ key: 'test_mode', value: 'true' });\n * }\n * );\n *\n * testWithSetup('should have test settings', async ({ trx }) => {\n * const setting = await knex('settings')\n * .transacting(trx)\n * .where('key', 'test_mode')\n * .first();\n *\n * expect(setting?.value).toBe('true');\n * });\n *\n * // Example with factory and transaction\n * const isolatedTest = wrapVitestObjectionTransaction(test, knex);\n * const factory = new ObjectionFactory(builders, seeds, knex);\n *\n * isolatedTest('creates related data', async ({ trx }) => {\n * // Factory can use the transaction\n * const user = await User.query(trx).insert({ name: 'Author' });\n * const posts = await Post.query(trx).insert([\n * { title: 'Post 1', userId: user.id },\n * { title: 'Post 2', userId: user.id }\n * ]);\n *\n * const userWithPosts = await User.query(trx)\n * .findById(user.id)\n * .withGraphFetched('posts');\n *\n * expect(userWithPosts.posts).toHaveLength(2);\n * });\n * ```\n */\nexport function wrapVitestObjectionTransaction(\n api: TestAPI,\n conn: DatabaseConnection<Knex>,\n setup?: (trx: Knex.Transaction) => Promise<void>,\n level: IsolationLevel = IsolationLevel.REPEATABLE_READ,\n) {\n const wrapper = new VitestObjectionTransactionIsolator(api);\n\n return wrapper.wrapVitestWithTransaction(conn, setup, level);\n}\n\n/**\n * Extends an Objection.js transaction-wrapped test with additional fixtures.\n * Each fixture receives the transaction and can create dependencies like factories or repositories.\n *\n * @template Extended - The type of additional fixtures to provide\n * @param wrappedTest - The base wrapped test from wrapVitestObjectionTransaction\n * @param fixtures - Object mapping fixture names to creator functions\n * @returns An extended test API with both trx and the additional fixtures\n *\n * @example\n * ```typescript\n * import { test } from 'vitest';\n * import { wrapVitestObjectionTransaction, extendWithFixtures, ObjectionFactory } from '@geekmidas/testkit/objection';\n * import { User } from './models';\n *\n * // Define your builders\n * const builders = {\n * user: ObjectionFactory.createBuilder(User, ({ faker }) => ({\n * name: faker.person.fullName(),\n * email: faker.internet.email(),\n * })),\n * };\n *\n * // Create base wrapped test\n * const baseTest = wrapVitestObjectionTransaction(test, knex, createTestTables);\n *\n * // Extend with fixtures - each fixture receives the transaction\n * const it = extendWithFixtures<{ factory: ObjectionFactory<typeof builders, {}> }>(\n * baseTest,\n * {\n * factory: (trx) => new ObjectionFactory(builders, {}, trx),\n * }\n * );\n *\n * // Use in tests - both trx and factory are available\n * it('should create user with factory', async ({ trx, factory }) => {\n * const user = await factory.insert('user', { name: 'Test User' });\n * expect(user.id).toBeDefined();\n *\n * // Verify in database\n * const found = await User.query(trx).findById(user.id);\n * expect(found?.name).toBe('Test User');\n * });\n * ```\n */\nexport function extendWithFixtures<\n Extended extends Record<string, unknown>,\n T extends ReturnType<TestAPI['extend']> = ReturnType<TestAPI['extend']>,\n>(wrappedTest: T, fixtures: FixtureCreators<Knex.Transaction, Extended>) {\n return baseExtendWithFixtures<Knex.Transaction, Extended, T>(\n wrappedTest,\n fixtures,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgGA,SAAgB,+BACdA,KACAC,MACAC,OACAC,QAAwB,eAAe,iBACvC;CACA,MAAM,UAAU,IAAI,mCAAmC;AAEvD,QAAO,QAAQ,0BAA0B,MAAM,OAAO,MAAM;AAC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CD,SAAgBC,qBAGdC,aAAgBC,UAAuD;AACvE,QAAO,mBACL,aACA,SACD;AACF"}
|
package/dist/os/directory.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { DirectoryFixtures, itWithDir } from "../directory-
|
|
1
|
+
import { DirectoryFixtures, itWithDir } from "../directory-DlkPEzL4.cjs";
|
|
2
2
|
export { DirectoryFixtures, itWithDir };
|
package/dist/os/directory.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { DirectoryFixtures, itWithDir } from "../directory-
|
|
1
|
+
import { DirectoryFixtures, itWithDir } from "../directory-BXavAeJZ.mjs";
|
|
2
2
|
export { DirectoryFixtures, itWithDir };
|
package/dist/os/index.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { itWithDir } from "../directory-
|
|
1
|
+
import { itWithDir } from "../directory-DlkPEzL4.cjs";
|
|
2
2
|
export { itWithDir };
|
package/dist/os/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { itWithDir } from "../directory-
|
|
1
|
+
import { itWithDir } from "../directory-BXavAeJZ.mjs";
|
|
2
2
|
export { itWithDir };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geekmidas/testkit",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -47,11 +47,15 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@faker-js/faker": "~9.9.0",
|
|
50
|
-
"@geekmidas/logger": "0.0
|
|
50
|
+
"@geekmidas/logger": "0.1.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/pg": "~8.15.4"
|
|
54
54
|
},
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "https://github.com/geekmidas/toolbox"
|
|
58
|
+
},
|
|
55
59
|
"publishConfig": {
|
|
56
60
|
"registry": "https://registry.npmjs.org/",
|
|
57
61
|
"access": "public"
|
|
@@ -65,6 +69,6 @@
|
|
|
65
69
|
"vitest": "~3.2.4",
|
|
66
70
|
"@types/aws-lambda": ">=8.10.92",
|
|
67
71
|
"better-auth": ">=1.3.34",
|
|
68
|
-
"@geekmidas/envkit": "0.0
|
|
72
|
+
"@geekmidas/envkit": "0.1.0"
|
|
69
73
|
}
|
|
70
74
|
}
|
package/src/KyselyFactory.ts
CHANGED
|
@@ -25,13 +25,13 @@ import { type FakerFactory, faker } from './faker.ts';
|
|
|
25
25
|
*
|
|
26
26
|
* // Create builders
|
|
27
27
|
* const builders = {
|
|
28
|
-
* user: KyselyFactory.createBuilder<Database, 'users'>('users', (attrs,
|
|
28
|
+
* user: KyselyFactory.createBuilder<Database, 'users'>('users', ({ attrs, faker }) => ({
|
|
29
29
|
* id: faker.string.uuid(),
|
|
30
30
|
* name: faker.person.fullName(),
|
|
31
31
|
* email: faker.internet.email(),
|
|
32
32
|
* ...attrs
|
|
33
33
|
* })),
|
|
34
|
-
* post: KyselyFactory.createBuilder<Database, 'posts'>('posts', (attrs) => ({
|
|
34
|
+
* post: KyselyFactory.createBuilder<Database, 'posts'>('posts', ({ attrs }) => ({
|
|
35
35
|
* title: 'Test Post',
|
|
36
36
|
* content: 'Test content',
|
|
37
37
|
* ...attrs
|
|
@@ -88,15 +88,15 @@ export class KyselyFactory<
|
|
|
88
88
|
* @template Result - The result type (defaults to Selectable of the table)
|
|
89
89
|
*
|
|
90
90
|
* @param table - The name of the database table
|
|
91
|
-
* @param
|
|
91
|
+
* @param defaults - Optional function to provide default values (receives destructured context)
|
|
92
92
|
* @param autoInsert - Whether to automatically insert the record (default: true)
|
|
93
93
|
* @returns A builder function that creates and optionally inserts records
|
|
94
94
|
*
|
|
95
95
|
* @example
|
|
96
96
|
* ```typescript
|
|
97
|
-
* // Create a simple builder with defaults
|
|
97
|
+
* // Create a simple builder with defaults - destructure only what you need
|
|
98
98
|
* const userBuilder = KyselyFactory.createBuilder<DB, 'users'>('users',
|
|
99
|
-
* (attrs,
|
|
99
|
+
* ({ attrs, faker }) => ({
|
|
100
100
|
* id: faker.string.uuid(),
|
|
101
101
|
* name: faker.person.fullName(),
|
|
102
102
|
* email: faker.internet.email(),
|
|
@@ -105,9 +105,17 @@ export class KyselyFactory<
|
|
|
105
105
|
* })
|
|
106
106
|
* );
|
|
107
107
|
*
|
|
108
|
+
* // Only need faker? Just destructure that
|
|
109
|
+
* const leaveTypeBuilder = KyselyFactory.createBuilder<DB, 'leaveTypes'>('leaveTypes',
|
|
110
|
+
* ({ faker }) => ({
|
|
111
|
+
* name: faker.helpers.arrayElement(['Annual', 'Sick', 'Maternity']),
|
|
112
|
+
* code: faker.string.alpha({ length: 3, casing: 'upper' }),
|
|
113
|
+
* })
|
|
114
|
+
* );
|
|
115
|
+
*
|
|
108
116
|
* // Create a builder that doesn't auto-insert (useful for nested inserts)
|
|
109
117
|
* const addressBuilder = KyselyFactory.createBuilder<DB, 'addresses'>('addresses',
|
|
110
|
-
* (attrs) => ({
|
|
118
|
+
* ({ attrs }) => ({
|
|
111
119
|
* street: '123 Main St',
|
|
112
120
|
* city: 'Anytown',
|
|
113
121
|
* ...attrs
|
|
@@ -126,12 +134,12 @@ export class KyselyFactory<
|
|
|
126
134
|
Result = Selectable<DB[TableName]>,
|
|
127
135
|
>(
|
|
128
136
|
table: TableName,
|
|
129
|
-
|
|
130
|
-
attrs: Attrs
|
|
131
|
-
factory: Factory
|
|
132
|
-
db: Kysely<DB
|
|
133
|
-
faker: FakerFactory
|
|
134
|
-
) =>
|
|
137
|
+
defaults?: (context: {
|
|
138
|
+
attrs: Attrs;
|
|
139
|
+
factory: Factory;
|
|
140
|
+
db: Kysely<DB>;
|
|
141
|
+
faker: FakerFactory;
|
|
142
|
+
}) =>
|
|
135
143
|
| Partial<Insertable<DB[TableName]>>
|
|
136
144
|
| Promise<Partial<Insertable<DB[TableName]>>>,
|
|
137
145
|
autoInsert?: boolean,
|
|
@@ -145,15 +153,20 @@ export class KyselyFactory<
|
|
|
145
153
|
attrs: Attrs,
|
|
146
154
|
factory: Factory,
|
|
147
155
|
db: Kysely<DB>,
|
|
148
|
-
|
|
156
|
+
fakerInstance: FakerFactory,
|
|
149
157
|
) => {
|
|
150
158
|
// Start with attributes
|
|
151
159
|
let data: Partial<Insertable<DB[TableName]>> = { ...attrs };
|
|
152
160
|
|
|
153
161
|
// Apply defaults
|
|
154
|
-
if (
|
|
155
|
-
const
|
|
156
|
-
|
|
162
|
+
if (defaults) {
|
|
163
|
+
const defaultValues = await defaults({
|
|
164
|
+
attrs,
|
|
165
|
+
factory,
|
|
166
|
+
db,
|
|
167
|
+
faker: fakerInstance,
|
|
168
|
+
});
|
|
169
|
+
data = { ...defaultValues, ...data };
|
|
157
170
|
}
|
|
158
171
|
|
|
159
172
|
// Handle insertion based on autoInsert flag
|
package/src/ObjectionFactory.ts
CHANGED
|
@@ -19,17 +19,17 @@ import { type FakerFactory, faker } from './faker.ts';
|
|
|
19
19
|
*
|
|
20
20
|
* // Create builders
|
|
21
21
|
* const builders = {
|
|
22
|
-
* user: (attrs) =>
|
|
22
|
+
* user: ObjectionFactory.createBuilder(User, ({ attrs, faker }) => ({
|
|
23
23
|
* id: faker.string.uuid(),
|
|
24
24
|
* name: faker.person.fullName(),
|
|
25
25
|
* email: faker.internet.email(),
|
|
26
26
|
* ...attrs
|
|
27
|
-
* }),
|
|
28
|
-
* post: (attrs) =>
|
|
27
|
+
* })),
|
|
28
|
+
* post: ObjectionFactory.createBuilder(Post, ({ attrs }) => ({
|
|
29
29
|
* title: 'Test Post',
|
|
30
30
|
* content: 'Test content',
|
|
31
31
|
* ...attrs
|
|
32
|
-
* })
|
|
32
|
+
* })),
|
|
33
33
|
* };
|
|
34
34
|
*
|
|
35
35
|
* // Create factory instance
|
|
@@ -65,15 +65,15 @@ export class ObjectionFactory<
|
|
|
65
65
|
* @template Result - The result type (defaults to the model instance)
|
|
66
66
|
*
|
|
67
67
|
* @param ModelClass - The Objection.js Model class
|
|
68
|
-
* @param
|
|
68
|
+
* @param defaults - Optional function to provide default values (receives destructured context)
|
|
69
69
|
* @param autoInsert - Whether to automatically insert the record (default: true)
|
|
70
70
|
* @returns A builder function that creates and optionally inserts records
|
|
71
71
|
*
|
|
72
72
|
* @example
|
|
73
73
|
* ```typescript
|
|
74
|
-
* // Create a simple builder with defaults
|
|
74
|
+
* // Create a simple builder with defaults - destructure only what you need
|
|
75
75
|
* const userBuilder = ObjectionFactory.createBuilder(User,
|
|
76
|
-
* (attrs,
|
|
76
|
+
* ({ attrs, faker }) => ({
|
|
77
77
|
* id: faker.string.uuid(),
|
|
78
78
|
* name: faker.person.fullName(),
|
|
79
79
|
* email: faker.internet.email(),
|
|
@@ -82,9 +82,17 @@ export class ObjectionFactory<
|
|
|
82
82
|
* })
|
|
83
83
|
* );
|
|
84
84
|
*
|
|
85
|
+
* // Only need faker? Just destructure that
|
|
86
|
+
* const leaveTypeBuilder = ObjectionFactory.createBuilder(LeaveType,
|
|
87
|
+
* ({ faker }) => ({
|
|
88
|
+
* name: faker.helpers.arrayElement(['Annual', 'Sick', 'Maternity']),
|
|
89
|
+
* code: faker.string.alpha({ length: 3, casing: 'upper' }),
|
|
90
|
+
* })
|
|
91
|
+
* );
|
|
92
|
+
*
|
|
85
93
|
* // Create a builder that doesn't auto-insert (useful for nested inserts)
|
|
86
94
|
* const addressBuilder = ObjectionFactory.createBuilder(Address,
|
|
87
|
-
* (attrs) => ({
|
|
95
|
+
* ({ attrs }) => ({
|
|
88
96
|
* street: '123 Main St',
|
|
89
97
|
* city: 'Anytown',
|
|
90
98
|
* ...attrs
|
|
@@ -94,7 +102,7 @@ export class ObjectionFactory<
|
|
|
94
102
|
*
|
|
95
103
|
* // Use with relations
|
|
96
104
|
* const postBuilder = ObjectionFactory.createBuilder(Post,
|
|
97
|
-
* async (attrs, factory) => ({
|
|
105
|
+
* async ({ attrs, factory, faker }) => ({
|
|
98
106
|
* title: faker.lorem.sentence(),
|
|
99
107
|
* content: faker.lorem.paragraphs(),
|
|
100
108
|
* authorId: attrs.authorId || (await factory.insert('user')).id,
|
|
@@ -110,12 +118,14 @@ export class ObjectionFactory<
|
|
|
110
118
|
Result = InstanceType<TModel>,
|
|
111
119
|
>(
|
|
112
120
|
ModelClass: TModel,
|
|
113
|
-
|
|
114
|
-
attrs: Attrs
|
|
115
|
-
factory: Factory
|
|
116
|
-
db: Knex
|
|
117
|
-
faker: FakerFactory
|
|
118
|
-
) =>
|
|
121
|
+
defaults?: (context: {
|
|
122
|
+
attrs: Attrs;
|
|
123
|
+
factory: Factory;
|
|
124
|
+
db: Knex;
|
|
125
|
+
faker: FakerFactory;
|
|
126
|
+
}) =>
|
|
127
|
+
| Partial<InstanceType<TModel>>
|
|
128
|
+
| Promise<Partial<InstanceType<TModel>>>,
|
|
119
129
|
autoInsert?: boolean,
|
|
120
130
|
): (
|
|
121
131
|
attrs: Attrs,
|
|
@@ -127,15 +137,20 @@ export class ObjectionFactory<
|
|
|
127
137
|
attrs: Attrs,
|
|
128
138
|
factory: Factory,
|
|
129
139
|
db: Knex,
|
|
130
|
-
|
|
140
|
+
fakerInstance: FakerFactory,
|
|
131
141
|
) => {
|
|
132
142
|
// Start with attributes
|
|
133
143
|
let data: Partial<InstanceType<TModel>> = { ...attrs };
|
|
134
144
|
|
|
135
145
|
// Apply defaults
|
|
136
|
-
if (
|
|
137
|
-
const
|
|
138
|
-
|
|
146
|
+
if (defaults) {
|
|
147
|
+
const defaultValues = await defaults({
|
|
148
|
+
attrs,
|
|
149
|
+
factory,
|
|
150
|
+
db,
|
|
151
|
+
faker: fakerInstance,
|
|
152
|
+
});
|
|
153
|
+
data = { ...defaultValues, ...data };
|
|
139
154
|
}
|
|
140
155
|
|
|
141
156
|
// Create model instance
|
|
@@ -5,8 +5,9 @@ import type { TestAPI } from 'vitest';
|
|
|
5
5
|
* Used with Vitest's test.extend() API to inject transactions into tests.
|
|
6
6
|
*
|
|
7
7
|
* @template Transaction - The transaction type specific to the database driver
|
|
8
|
+
* @template Extended - Additional context properties provided by the extend function
|
|
8
9
|
*/
|
|
9
|
-
export interface DatabaseFixtures<Transaction> {
|
|
10
|
+
export interface DatabaseFixtures<Transaction, Extended = object> {
|
|
10
11
|
/**
|
|
11
12
|
* The database transaction available to the test.
|
|
12
13
|
* All database operations should use this transaction to ensure proper rollback.
|
|
@@ -14,6 +15,31 @@ export interface DatabaseFixtures<Transaction> {
|
|
|
14
15
|
trx: Transaction;
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Combined fixtures type that merges the base transaction fixture with extended context.
|
|
20
|
+
*/
|
|
21
|
+
export type ExtendedDatabaseFixtures<
|
|
22
|
+
Transaction,
|
|
23
|
+
Extended = object,
|
|
24
|
+
> = DatabaseFixtures<Transaction> & Extended;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Function type for extending test context with additional properties.
|
|
28
|
+
* Receives the transaction and returns additional context to be merged with { trx }.
|
|
29
|
+
*
|
|
30
|
+
* @template Transaction - The transaction type
|
|
31
|
+
* @template Extended - The type of additional context to provide
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const extendContext: ExtendContextFn<Transaction<DB>, { factory: KyselyFactory }> =
|
|
36
|
+
* (trx) => ({ factory: new KyselyFactory(builders, seeds, trx) });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export type ExtendContextFn<Transaction, Extended> = (
|
|
40
|
+
trx: Transaction,
|
|
41
|
+
) => Extended | Promise<Extended>;
|
|
42
|
+
|
|
17
43
|
/**
|
|
18
44
|
* PostgreSQL transaction isolation levels.
|
|
19
45
|
* Controls the visibility of concurrent transactions.
|
|
@@ -123,7 +149,7 @@ export abstract class VitestPostgresTransactionIsolator<TConn, Transaction> {
|
|
|
123
149
|
) {
|
|
124
150
|
return this.api.extend<DatabaseFixtures<Transaction>>({
|
|
125
151
|
// This fixture automatically provides a transaction to each test
|
|
126
|
-
trx: async ({}, use) => {
|
|
152
|
+
trx: async ({}, use: (value: Transaction) => Promise<void>) => {
|
|
127
153
|
// Create a custom error class for rollback
|
|
128
154
|
class TestRollback extends Error {
|
|
129
155
|
constructor() {
|
|
@@ -168,3 +194,85 @@ export abstract class VitestPostgresTransactionIsolator<TConn, Transaction> {
|
|
|
168
194
|
|
|
169
195
|
export type DatabaseConnectionFn<Conn> = () => Conn | Promise<Conn>;
|
|
170
196
|
export type DatabaseConnection<Conn> = DatabaseConnectionFn<Conn>;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Type for fixture creator functions that depend on the transaction.
|
|
200
|
+
* Each function receives the transaction and returns the fixture value.
|
|
201
|
+
*/
|
|
202
|
+
export type FixtureCreators<
|
|
203
|
+
Transaction,
|
|
204
|
+
Extended extends Record<string, unknown>,
|
|
205
|
+
> = {
|
|
206
|
+
[K in keyof Extended]: (
|
|
207
|
+
trx: Transaction,
|
|
208
|
+
) => Extended[K] | Promise<Extended[K]>;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Extends a wrapped test API with additional fixtures that depend on the transaction.
|
|
213
|
+
* This allows composing test context with factories, repositories, or other helpers.
|
|
214
|
+
*
|
|
215
|
+
* @template Transaction - The transaction type
|
|
216
|
+
* @template Extended - The type of additional context to provide
|
|
217
|
+
* @param wrappedTest - The base wrapped test from wrapVitestWithTransaction
|
|
218
|
+
* @param fixtures - Object mapping fixture names to creator functions
|
|
219
|
+
* @returns An extended test API with both trx and the additional fixtures
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```typescript
|
|
223
|
+
* import { wrapVitestKyselyTransaction, extendWithFixtures } from '@geekmidas/testkit/kysely';
|
|
224
|
+
*
|
|
225
|
+
* // Create base wrapped test
|
|
226
|
+
* const baseTest = wrapVitestKyselyTransaction(test, db, createTestTables);
|
|
227
|
+
*
|
|
228
|
+
* // Extend with fixtures
|
|
229
|
+
* const it = extendWithFixtures(baseTest, {
|
|
230
|
+
* factory: (trx) => new KyselyFactory(builders, seeds, trx),
|
|
231
|
+
* userRepo: (trx) => new UserRepository(trx),
|
|
232
|
+
* });
|
|
233
|
+
*
|
|
234
|
+
* // Use in tests - trx and all fixtures are available
|
|
235
|
+
* it('should create user with factory', async ({ trx, factory, userRepo }) => {
|
|
236
|
+
* const user = await factory.insert('user', { name: 'Test' });
|
|
237
|
+
* expect(user).toBeDefined();
|
|
238
|
+
* });
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
export function extendWithFixtures<
|
|
242
|
+
Transaction,
|
|
243
|
+
Extended extends Record<string, unknown>,
|
|
244
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
245
|
+
T extends ReturnType<TestAPI['extend']> = any,
|
|
246
|
+
>(
|
|
247
|
+
wrappedTest: T,
|
|
248
|
+
fixtures: FixtureCreators<Transaction, Extended>,
|
|
249
|
+
): T & {
|
|
250
|
+
<C extends object>(
|
|
251
|
+
name: string,
|
|
252
|
+
fn: (
|
|
253
|
+
context: DatabaseFixtures<Transaction> & Extended & C,
|
|
254
|
+
) => Promise<void>,
|
|
255
|
+
): void;
|
|
256
|
+
<C extends object>(
|
|
257
|
+
name: string,
|
|
258
|
+
options: object,
|
|
259
|
+
fn: (
|
|
260
|
+
context: DatabaseFixtures<Transaction> & Extended & C,
|
|
261
|
+
) => Promise<void>,
|
|
262
|
+
): void;
|
|
263
|
+
} {
|
|
264
|
+
// Build fixture definitions for Vitest's extend API
|
|
265
|
+
const fixtureDefinitions: Record<string, any> = {};
|
|
266
|
+
|
|
267
|
+
for (const [key, creator] of Object.entries(fixtures)) {
|
|
268
|
+
fixtureDefinitions[key] = async (
|
|
269
|
+
{ trx }: { trx: Transaction },
|
|
270
|
+
use: (value: unknown) => Promise<void>,
|
|
271
|
+
) => {
|
|
272
|
+
const value = await (creator as (trx: Transaction) => unknown)(trx);
|
|
273
|
+
await use(value);
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return (wrappedTest as any).extend(fixtureDefinitions);
|
|
278
|
+
}
|
|
@@ -24,7 +24,7 @@ describe('KyselyFactory', () => {
|
|
|
24
24
|
async ({ trx }) => {
|
|
25
25
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
26
26
|
'users',
|
|
27
|
-
async (attrs) => ({
|
|
27
|
+
async ({ attrs }) => ({
|
|
28
28
|
name: 'John Doe',
|
|
29
29
|
email: `user${Date.now()}@example.com`,
|
|
30
30
|
createdAt: new Date(),
|
|
@@ -57,7 +57,7 @@ describe('KyselyFactory', () => {
|
|
|
57
57
|
async ({ trx }) => {
|
|
58
58
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
59
59
|
'users',
|
|
60
|
-
async (attrs) => ({
|
|
60
|
+
async ({ attrs }) => ({
|
|
61
61
|
name: 'John Doe',
|
|
62
62
|
email: `user${Date.now()}@example.com`,
|
|
63
63
|
createdAt: new Date(),
|
|
@@ -98,7 +98,7 @@ describe('KyselyFactory', () => {
|
|
|
98
98
|
|
|
99
99
|
const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
|
|
100
100
|
'posts',
|
|
101
|
-
async (attrs, factory) => {
|
|
101
|
+
async ({ attrs, factory }) => {
|
|
102
102
|
// Create a user if userId not provided
|
|
103
103
|
if (!attrs.userId) {
|
|
104
104
|
const user = await factory.insert('user');
|
|
@@ -156,7 +156,7 @@ describe('KyselyFactory', () => {
|
|
|
156
156
|
async ({ trx }) => {
|
|
157
157
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
158
158
|
'users',
|
|
159
|
-
async (
|
|
159
|
+
async () => ({
|
|
160
160
|
name: 'John Doe',
|
|
161
161
|
email: `user${Date.now()}-${Math.random()}@example.com`,
|
|
162
162
|
createdAt: new Date(),
|
|
@@ -189,7 +189,7 @@ describe('KyselyFactory', () => {
|
|
|
189
189
|
async ({ trx }) => {
|
|
190
190
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
191
191
|
'users',
|
|
192
|
-
async (
|
|
192
|
+
async () => ({
|
|
193
193
|
email: `user${Date.now()}-${Math.random()}@example.com`,
|
|
194
194
|
createdAt: new Date(),
|
|
195
195
|
}),
|
|
@@ -233,7 +233,7 @@ describe('KyselyFactory', () => {
|
|
|
233
233
|
let counter = 0;
|
|
234
234
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
235
235
|
'users',
|
|
236
|
-
async (
|
|
236
|
+
async () => {
|
|
237
237
|
// Simulate async operation
|
|
238
238
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
239
239
|
counter++;
|
|
@@ -307,7 +307,7 @@ describe('KyselyFactory', () => {
|
|
|
307
307
|
async ({ trx }) => {
|
|
308
308
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
309
309
|
'users',
|
|
310
|
-
async (attrs) => ({
|
|
310
|
+
async ({ attrs }) => ({
|
|
311
311
|
name: 'John Doe',
|
|
312
312
|
email: `user${Date.now()}@example.com`,
|
|
313
313
|
createdAt: new Date(),
|
|
@@ -364,7 +364,7 @@ describe('KyselyFactory', () => {
|
|
|
364
364
|
async ({ trx }) => {
|
|
365
365
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
366
366
|
'users',
|
|
367
|
-
async (attrs) => ({
|
|
367
|
+
async ({ attrs }) => ({
|
|
368
368
|
name: 'John Doe',
|
|
369
369
|
email: `user${Date.now()}@example.com`,
|
|
370
370
|
createdAt: new Date(),
|
|
@@ -400,7 +400,7 @@ describe('KyselyFactory', () => {
|
|
|
400
400
|
async ({ trx }) => {
|
|
401
401
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
402
402
|
'users',
|
|
403
|
-
async (attrs) => ({
|
|
403
|
+
async ({ attrs }) => ({
|
|
404
404
|
name: 'John Doe',
|
|
405
405
|
email: `user${Date.now()}@example.com`,
|
|
406
406
|
createdAt: new Date(),
|
|
@@ -409,7 +409,7 @@ describe('KyselyFactory', () => {
|
|
|
409
409
|
|
|
410
410
|
const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
|
|
411
411
|
'posts',
|
|
412
|
-
async (
|
|
412
|
+
async ({ factory }) => {
|
|
413
413
|
const user = await factory.insert('user');
|
|
414
414
|
return {
|
|
415
415
|
title: 'Default Post',
|