@danceroutine/tango-testing 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/dist/assertions/assertions.d.ts +7 -0
- package/dist/assertions/assertions.js +8 -0
- package/dist/assertions/index.d.ts +4 -0
- package/dist/assertions/index.js +3 -0
- package/dist/assertions-CN6KxXhH.js +15 -0
- package/dist/assertions-CN6KxXhH.js.map +1 -0
- package/dist/chunk-BkvOhyD0.js +12 -0
- package/dist/factories/ModelDataFactory.d.ts +18 -0
- package/dist/factories/ModelDataFactory.js +33 -0
- package/dist/factories/index.d.ts +4 -0
- package/dist/factories/index.js +3 -0
- package/dist/factories-CCAZ6E-g.js +40 -0
- package/dist/factories-CCAZ6E-g.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/integration/HarnessStrategyRegistry.d.ts +10 -0
- package/dist/integration/TestHarness.d.ts +17 -0
- package/dist/integration/config.d.ts +7 -0
- package/dist/integration/domain/Dialect.d.ts +4 -0
- package/dist/integration/domain/HarnessStrategy.d.ts +17 -0
- package/dist/integration/domain/IntegrationHarness.d.ts +21 -0
- package/dist/integration/domain/ResetMode.d.ts +5 -0
- package/dist/integration/domain/index.d.ts +7 -0
- package/dist/integration/index.d.ts +11 -0
- package/dist/integration/index.js +3 -0
- package/dist/integration/migrations/ApplyAndVerifyMigrations.d.ts +13 -0
- package/dist/integration/migrations/AssertMigrationPlan.d.ts +6 -0
- package/dist/integration/migrations/IntrospectSchema.d.ts +2 -0
- package/dist/integration/migrations/index.d.ts +6 -0
- package/dist/integration/orm.d.ts +9 -0
- package/dist/integration/orm.js +39 -0
- package/dist/integration/strategies/PostgresHarnessStrategy.d.ts +10 -0
- package/dist/integration/strategies/PostgresHarnessStrategy.js +95 -0
- package/dist/integration/strategies/SqliteHarnessStrategy.d.ts +10 -0
- package/dist/integration-CDdpboYz.js +378 -0
- package/dist/integration-CDdpboYz.js.map +1 -0
- package/dist/mocks/DBClient.d.ts +9 -0
- package/dist/mocks/DBClient.js +1 -0
- package/dist/mocks/MockQuerySetResult.d.ts +12 -0
- package/dist/mocks/MockQuerySetResult.js +1 -0
- package/dist/mocks/RepositoryLike.d.ts +12 -0
- package/dist/mocks/RepositoryLike.js +1 -0
- package/dist/mocks/aMockDBClient.d.ts +2 -0
- package/dist/mocks/aMockDBClient.js +13 -0
- package/dist/mocks/aMockQuerySet.d.ts +2 -0
- package/dist/mocks/aMockQuerySet.js +15 -0
- package/dist/mocks/aMockRepository.d.ts +2 -0
- package/dist/mocks/aMockRepository.js +20 -0
- package/dist/mocks/index.d.ts +9 -0
- package/dist/mocks/index.js +6 -0
- package/dist/mocks/types.d.ts +33 -0
- package/dist/mocks-qo-1vCez.js +72 -0
- package/dist/mocks-qo-1vCez.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/vitest/index.d.ts +4 -0
- package/dist/vitest/index.js +2 -0
- package/dist/vitest/registerVitestTango.d.ts +39 -0
- package/dist/vitest/registerVitestTango.js +90 -0
- package/dist/vitest-PxMJue7R.js +81 -0
- package/dist/vitest-PxMJue7R.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { __export } from "./chunk-BkvOhyD0.js";
|
|
2
|
+
|
|
3
|
+
//#region src/mocks/aMockDBClient.ts
|
|
4
|
+
function aMockDBClient(overrides = {}) {
|
|
5
|
+
const client = {
|
|
6
|
+
query: async () => ({ rows: [] }),
|
|
7
|
+
begin: async () => {},
|
|
8
|
+
commit: async () => {},
|
|
9
|
+
rollback: async () => {},
|
|
10
|
+
close: async () => {}
|
|
11
|
+
};
|
|
12
|
+
return {
|
|
13
|
+
...client,
|
|
14
|
+
...overrides
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/mocks/aMockQuerySet.ts
|
|
20
|
+
function aMockQuerySet(overrides = {}) {
|
|
21
|
+
const queryset = {
|
|
22
|
+
filter: () => queryset,
|
|
23
|
+
orderBy: () => queryset,
|
|
24
|
+
limit: () => queryset,
|
|
25
|
+
offset: () => queryset,
|
|
26
|
+
fetch: async () => ({
|
|
27
|
+
results: [],
|
|
28
|
+
nextCursor: null
|
|
29
|
+
}),
|
|
30
|
+
fetchOne: async () => null,
|
|
31
|
+
count: async () => 0
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
...queryset,
|
|
35
|
+
...overrides
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/mocks/aMockRepository.ts
|
|
41
|
+
function aMockRepository(overrides = {}) {
|
|
42
|
+
const defaultQuerySet = aMockQuerySet();
|
|
43
|
+
const repository = {
|
|
44
|
+
meta: {
|
|
45
|
+
table: "mock_table",
|
|
46
|
+
pk: "id",
|
|
47
|
+
columns: {}
|
|
48
|
+
},
|
|
49
|
+
query: () => defaultQuerySet,
|
|
50
|
+
findById: async () => null,
|
|
51
|
+
create: async (input) => input,
|
|
52
|
+
update: async (_id, patch) => patch,
|
|
53
|
+
delete: async () => {}
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
...repository,
|
|
57
|
+
...overrides
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/mocks/index.ts
|
|
63
|
+
var mocks_exports = {};
|
|
64
|
+
__export(mocks_exports, {
|
|
65
|
+
aMockDBClient: () => aMockDBClient,
|
|
66
|
+
aMockQuerySet: () => aMockQuerySet,
|
|
67
|
+
aMockRepository: () => aMockRepository
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
//#endregion
|
|
71
|
+
export { aMockDBClient, aMockQuerySet, aMockRepository, mocks_exports };
|
|
72
|
+
//# sourceMappingURL=mocks-qo-1vCez.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mocks-qo-1vCez.js","names":["overrides: Partial<DBClient>","client: DBClient","overrides: Partial<MockQuerySetResult<TModel>>","queryset: MockQuerySetResult<TModel>","overrides: Partial<RepositoryLike<TModel>>","repository: Partial<RepositoryLike<TModel>>","input: Partial<TModel>","_id: string","patch: Partial<TModel>"],"sources":["../src/mocks/aMockDBClient.ts","../src/mocks/aMockQuerySet.ts","../src/mocks/aMockRepository.ts","../src/mocks/index.ts"],"sourcesContent":["import type { DBClient } from './types';\n\nexport function aMockDBClient(overrides: Partial<DBClient> = {}): DBClient {\n const client: DBClient = {\n query: async () => ({ rows: [] }),\n begin: async () => {},\n commit: async () => {},\n rollback: async () => {},\n close: async () => {},\n };\n\n return {\n ...client,\n ...overrides,\n };\n}\n","import type { MockQuerySetResult } from './types';\n\nexport function aMockQuerySet<TModel extends Record<string, unknown>>(\n overrides: Partial<MockQuerySetResult<TModel>> = {}\n): MockQuerySetResult<TModel> {\n const queryset: MockQuerySetResult<TModel> = {\n filter: () => queryset,\n orderBy: () => queryset,\n limit: () => queryset,\n offset: () => queryset,\n fetch: async () => ({ results: [], nextCursor: null }),\n fetchOne: async () => null,\n count: async () => 0,\n };\n\n return {\n ...queryset,\n ...overrides,\n };\n}\n","import { aMockQuerySet } from './aMockQuerySet';\nimport type { RepositoryLike } from './types';\n\nexport function aMockRepository<TModel extends Record<string, unknown>>(\n overrides: Partial<RepositoryLike<TModel>> = {}\n): RepositoryLike<TModel> {\n const defaultQuerySet = aMockQuerySet<TModel>();\n\n const repository: Partial<RepositoryLike<TModel>> = {\n meta: {\n table: 'mock_table',\n pk: 'id' as keyof TModel & string,\n columns: {},\n },\n query: () => defaultQuerySet,\n findById: async () => null,\n create: async (input: Partial<TModel>) => input as TModel,\n update: async (_id: string, patch: Partial<TModel>) => patch as TModel,\n delete: async () => {},\n };\n\n return {\n ...repository,\n ...overrides,\n } as RepositoryLike<TModel>;\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { aMockDBClient } from './aMockDBClient';\nexport { aMockQuerySet } from './aMockQuerySet';\nexport { aMockRepository } from './aMockRepository';\nexport type { DBClient, MockQuerySetResult, RepositoryLike } from './types';\n"],"mappings":";;;AAEO,SAAS,cAAcA,YAA+B,CAAE,GAAY;CACvE,MAAMC,SAAmB;EACrB,OAAO,aAAa,EAAE,MAAM,CAAE,EAAE;EAChC,OAAO,YAAY,CAAE;EACrB,QAAQ,YAAY,CAAE;EACtB,UAAU,YAAY,CAAE;EACxB,OAAO,YAAY,CAAE;CACxB;AAED,QAAO;EACH,GAAG;EACH,GAAG;CACN;AACJ;;;;ACbM,SAAS,cACZC,YAAiD,CAAE,GACzB;CAC1B,MAAMC,WAAuC;EACzC,QAAQ,MAAM;EACd,SAAS,MAAM;EACf,OAAO,MAAM;EACb,QAAQ,MAAM;EACd,OAAO,aAAa;GAAE,SAAS,CAAE;GAAE,YAAY;EAAM;EACrD,UAAU,YAAY;EACtB,OAAO,YAAY;CACtB;AAED,QAAO;EACH,GAAG;EACH,GAAG;CACN;AACJ;;;;AChBM,SAAS,gBACZC,YAA6C,CAAE,GACzB;CACtB,MAAM,kBAAkB,eAAuB;CAE/C,MAAMC,aAA8C;EAChD,MAAM;GACF,OAAO;GACP,IAAI;GACJ,SAAS,CAAE;EACd;EACD,OAAO,MAAM;EACb,UAAU,YAAY;EACtB,QAAQ,OAAOC,UAA2B;EAC1C,QAAQ,OAAOC,KAAaC,UAA2B;EACvD,QAAQ,YAAY,CAAE;CACzB;AAED,QAAO;EACH,GAAG;EACH,GAAG;CACN;AACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION = "0.1.0";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ApplyAndVerifyMigrationsOptions, type AssertMigrationPlanOptions, type HarnessStrategyRegistry, type IntegrationHarness } from '../integration';
|
|
2
|
+
interface Parseable {
|
|
3
|
+
parse(data: unknown): unknown;
|
|
4
|
+
}
|
|
5
|
+
export interface TangoVitestHelpers {
|
|
6
|
+
useHarness(input: IntegrationHarness | (() => IntegrationHarness | Promise<IntegrationHarness>)): Promise<IntegrationHarness>;
|
|
7
|
+
getTestHarness(): IntegrationHarness;
|
|
8
|
+
getRegistry(): HarnessStrategyRegistry;
|
|
9
|
+
assertMigrationPlan(options: AssertMigrationPlanOptions & {
|
|
10
|
+
harness?: IntegrationHarness;
|
|
11
|
+
}): Promise<string>;
|
|
12
|
+
applyAndVerifyMigrations(options: ApplyAndVerifyMigrationsOptions & {
|
|
13
|
+
harness?: IntegrationHarness;
|
|
14
|
+
}): Promise<{
|
|
15
|
+
statuses: {
|
|
16
|
+
id: string;
|
|
17
|
+
applied: boolean;
|
|
18
|
+
}[];
|
|
19
|
+
}>;
|
|
20
|
+
introspectSchema(harness?: IntegrationHarness): Promise<unknown>;
|
|
21
|
+
seedTable<T extends Record<string, unknown>>(table: string, rows: T[], harness?: IntegrationHarness): Promise<void>;
|
|
22
|
+
createRepositoryFixture<TModel extends Record<string, unknown>>(options: {
|
|
23
|
+
meta: import('@danceroutine/tango-orm/query').RepoMeta;
|
|
24
|
+
harness?: IntegrationHarness;
|
|
25
|
+
}): import('@danceroutine/tango-orm').Repository<TModel>;
|
|
26
|
+
expectQueryResult<T>(actual: Promise<T> | T, expected: T): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
declare module 'vitest' {
|
|
29
|
+
interface Assertion<T> {
|
|
30
|
+
toMatchSchema(schema: Parseable): void;
|
|
31
|
+
}
|
|
32
|
+
interface AsymmetricMatchersContaining {
|
|
33
|
+
toMatchSchema(schema: Parseable): void;
|
|
34
|
+
}
|
|
35
|
+
interface VitestUtils {
|
|
36
|
+
tango: TangoVitestHelpers;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vitest custom matchers and helpers for Tango.
|
|
3
|
+
*
|
|
4
|
+
* Import this module in your Vitest setup file:
|
|
5
|
+
*
|
|
6
|
+
* ```typescript
|
|
7
|
+
* import '@danceroutine/tango-testing/vitest';
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
import { expect, vi } from 'vitest';
|
|
11
|
+
import { TestHarness, applyAndVerifyMigrations as applyAndVerifyMigrationsFn, assertMigrationPlan as assertMigrationPlanFn, createRepositoryFixture as createRepositoryFixtureFn, expectQueryResult as expectQueryResultFn, introspectSchema as introspectSchemaFn, seedTable as seedTableFn, } from '../integration';
|
|
12
|
+
function isError(value) {
|
|
13
|
+
return (typeof value === 'object' &&
|
|
14
|
+
value !== null &&
|
|
15
|
+
typeof value.name === 'string' &&
|
|
16
|
+
typeof value.message === 'string');
|
|
17
|
+
}
|
|
18
|
+
let activeHarness = null;
|
|
19
|
+
async function resolveHarness(input) {
|
|
20
|
+
if (typeof input === 'function') {
|
|
21
|
+
return input();
|
|
22
|
+
}
|
|
23
|
+
return input;
|
|
24
|
+
}
|
|
25
|
+
const tangoHelpers = {
|
|
26
|
+
async useHarness(input) {
|
|
27
|
+
const harness = await resolveHarness(input);
|
|
28
|
+
await harness.setup();
|
|
29
|
+
activeHarness = harness;
|
|
30
|
+
return harness;
|
|
31
|
+
},
|
|
32
|
+
getTestHarness() {
|
|
33
|
+
if (!activeHarness) {
|
|
34
|
+
throw new Error('No active test harness. Call vi.tango.useHarness(...) in beforeAll first.');
|
|
35
|
+
}
|
|
36
|
+
return activeHarness;
|
|
37
|
+
},
|
|
38
|
+
getRegistry() {
|
|
39
|
+
return TestHarness.getRegistry();
|
|
40
|
+
},
|
|
41
|
+
async assertMigrationPlan(options) {
|
|
42
|
+
const harness = options.harness ?? tangoHelpers.getTestHarness();
|
|
43
|
+
return assertMigrationPlanFn(harness, {
|
|
44
|
+
migrationsDir: options.migrationsDir,
|
|
45
|
+
expectSqlContains: options.expectSqlContains,
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
async applyAndVerifyMigrations(options) {
|
|
49
|
+
const harness = options.harness ?? tangoHelpers.getTestHarness();
|
|
50
|
+
return applyAndVerifyMigrationsFn(harness, {
|
|
51
|
+
migrationsDir: options.migrationsDir,
|
|
52
|
+
toId: options.toId,
|
|
53
|
+
expectedAppliedIds: options.expectedAppliedIds,
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
async introspectSchema(harness) {
|
|
57
|
+
return introspectSchemaFn(harness ?? tangoHelpers.getTestHarness());
|
|
58
|
+
},
|
|
59
|
+
async seedTable(table, rows, harness) {
|
|
60
|
+
await seedTableFn(harness ?? tangoHelpers.getTestHarness(), table, rows);
|
|
61
|
+
},
|
|
62
|
+
createRepositoryFixture(options) {
|
|
63
|
+
return createRepositoryFixtureFn({
|
|
64
|
+
harness: options.harness ?? tangoHelpers.getTestHarness(),
|
|
65
|
+
meta: options.meta,
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
async expectQueryResult(actual, expected) {
|
|
69
|
+
await expectQueryResultFn(actual, expected);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
expect.extend({
|
|
73
|
+
toMatchSchema(received, schema) {
|
|
74
|
+
try {
|
|
75
|
+
schema.parse(received);
|
|
76
|
+
return {
|
|
77
|
+
pass: true,
|
|
78
|
+
message: () => 'expected data not to match schema',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
const detail = isError(error) ? error.message : String(error);
|
|
83
|
+
return {
|
|
84
|
+
pass: false,
|
|
85
|
+
message: () => `expected data to match schema\n\n${detail}`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
vi.tango = tangoHelpers;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { TestHarness, applyAndVerifyMigrations, assertMigrationPlan, createRepositoryFixture, expectQueryResult, introspectSchema, seedTable } from "./integration-CDdpboYz.js";
|
|
2
|
+
import { expect, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
//#region src/vitest/registerVitestTango.ts
|
|
5
|
+
function isError(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && typeof value.name === "string" && typeof value.message === "string";
|
|
7
|
+
}
|
|
8
|
+
let activeHarness = null;
|
|
9
|
+
async function resolveHarness(input) {
|
|
10
|
+
if (typeof input === "function") return input();
|
|
11
|
+
return input;
|
|
12
|
+
}
|
|
13
|
+
const tangoHelpers = {
|
|
14
|
+
async useHarness(input) {
|
|
15
|
+
const harness = await resolveHarness(input);
|
|
16
|
+
await harness.setup();
|
|
17
|
+
activeHarness = harness;
|
|
18
|
+
return harness;
|
|
19
|
+
},
|
|
20
|
+
getTestHarness() {
|
|
21
|
+
if (!activeHarness) throw new Error("No active test harness. Call vi.tango.useHarness(...) in beforeAll first.");
|
|
22
|
+
return activeHarness;
|
|
23
|
+
},
|
|
24
|
+
getRegistry() {
|
|
25
|
+
return TestHarness.getRegistry();
|
|
26
|
+
},
|
|
27
|
+
async assertMigrationPlan(options) {
|
|
28
|
+
const harness = options.harness ?? tangoHelpers.getTestHarness();
|
|
29
|
+
return assertMigrationPlan(harness, {
|
|
30
|
+
migrationsDir: options.migrationsDir,
|
|
31
|
+
expectSqlContains: options.expectSqlContains
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
async applyAndVerifyMigrations(options) {
|
|
35
|
+
const harness = options.harness ?? tangoHelpers.getTestHarness();
|
|
36
|
+
return applyAndVerifyMigrations(harness, {
|
|
37
|
+
migrationsDir: options.migrationsDir,
|
|
38
|
+
toId: options.toId,
|
|
39
|
+
expectedAppliedIds: options.expectedAppliedIds
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
async introspectSchema(harness) {
|
|
43
|
+
return introspectSchema(harness ?? tangoHelpers.getTestHarness());
|
|
44
|
+
},
|
|
45
|
+
async seedTable(table, rows, harness) {
|
|
46
|
+
await seedTable(harness ?? tangoHelpers.getTestHarness(), table, rows);
|
|
47
|
+
},
|
|
48
|
+
createRepositoryFixture(options) {
|
|
49
|
+
return createRepositoryFixture({
|
|
50
|
+
harness: options.harness ?? tangoHelpers.getTestHarness(),
|
|
51
|
+
meta: options.meta
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
async expectQueryResult(actual, expected) {
|
|
55
|
+
await expectQueryResult(actual, expected);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
expect.extend({ toMatchSchema(received, schema) {
|
|
59
|
+
try {
|
|
60
|
+
schema.parse(received);
|
|
61
|
+
return {
|
|
62
|
+
pass: true,
|
|
63
|
+
message: () => "expected data not to match schema"
|
|
64
|
+
};
|
|
65
|
+
} catch (error) {
|
|
66
|
+
const detail = isError(error) ? error.message : String(error);
|
|
67
|
+
return {
|
|
68
|
+
pass: false,
|
|
69
|
+
message: () => `expected data to match schema\n\n${detail}`
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
} });
|
|
73
|
+
vi.tango = tangoHelpers;
|
|
74
|
+
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/vitest/index.ts
|
|
77
|
+
var vitest_exports = {};
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
export { vitest_exports };
|
|
81
|
+
//# sourceMappingURL=vitest-PxMJue7R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest-PxMJue7R.js","names":["value: unknown","activeHarness: IntegrationHarness | null","input: IntegrationHarness | (() => IntegrationHarness | Promise<IntegrationHarness>)","tangoHelpers: TangoVitestHelpers","harness?: IntegrationHarness","table: string","rows: T[]","options: {\n meta: import('@danceroutine/tango-orm/query').RepoMeta;\n harness?: IntegrationHarness;\n }","actual: Promise<T> | T","expected: T","received: unknown","schema: Parseable"],"sources":["../src/vitest/registerVitestTango.ts","../src/vitest/index.ts"],"sourcesContent":["/**\n * Vitest custom matchers and helpers for Tango.\n *\n * Import this module in your Vitest setup file:\n *\n * ```typescript\n * import '@danceroutine/tango-testing/vitest';\n * ```\n */\nimport { expect, vi } from 'vitest';\nimport {\n TestHarness,\n applyAndVerifyMigrations as applyAndVerifyMigrationsFn,\n assertMigrationPlan as assertMigrationPlanFn,\n createRepositoryFixture as createRepositoryFixtureFn,\n expectQueryResult as expectQueryResultFn,\n introspectSchema as introspectSchemaFn,\n seedTable as seedTableFn,\n type ApplyAndVerifyMigrationsOptions,\n type AssertMigrationPlanOptions,\n type HarnessStrategyRegistry,\n type IntegrationHarness,\n} from '../integration';\n\ninterface Parseable {\n parse(data: unknown): unknown;\n}\n\nfunction isError(value: unknown): value is Error {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { name?: unknown }).name === 'string' &&\n typeof (value as { message?: unknown }).message === 'string'\n );\n}\n\nlet activeHarness: IntegrationHarness | null = null;\n\nasync function resolveHarness(\n input: IntegrationHarness | (() => IntegrationHarness | Promise<IntegrationHarness>)\n): Promise<IntegrationHarness> {\n if (typeof input === 'function') {\n return input();\n }\n return input;\n}\n\nexport interface TangoVitestHelpers {\n useHarness(\n input: IntegrationHarness | (() => IntegrationHarness | Promise<IntegrationHarness>)\n ): Promise<IntegrationHarness>;\n getTestHarness(): IntegrationHarness;\n getRegistry(): HarnessStrategyRegistry;\n assertMigrationPlan(options: AssertMigrationPlanOptions & { harness?: IntegrationHarness }): Promise<string>;\n applyAndVerifyMigrations(\n options: ApplyAndVerifyMigrationsOptions & { harness?: IntegrationHarness }\n ): Promise<{ statuses: { id: string; applied: boolean }[] }>;\n introspectSchema(harness?: IntegrationHarness): Promise<unknown>;\n seedTable<T extends Record<string, unknown>>(table: string, rows: T[], harness?: IntegrationHarness): Promise<void>;\n createRepositoryFixture<TModel extends Record<string, any>>(options: {\n meta: import('@danceroutine/tango-orm/query').RepoMeta;\n harness?: IntegrationHarness;\n }): import('@danceroutine/tango-orm').Repository<TModel>;\n expectQueryResult<T>(actual: Promise<T> | T, expected: T): Promise<void>;\n}\n\nconst tangoHelpers: TangoVitestHelpers = {\n async useHarness(input): Promise<IntegrationHarness> {\n const harness = await resolveHarness(input);\n await harness.setup();\n activeHarness = harness;\n return harness;\n },\n getTestHarness(): IntegrationHarness {\n if (!activeHarness) {\n throw new Error('No active test harness. Call vi.tango.useHarness(...) in beforeAll first.');\n }\n return activeHarness;\n },\n getRegistry(): HarnessStrategyRegistry {\n return TestHarness.getRegistry();\n },\n async assertMigrationPlan(options): Promise<string> {\n const harness = options.harness ?? tangoHelpers.getTestHarness();\n return assertMigrationPlanFn(harness, {\n migrationsDir: options.migrationsDir,\n expectSqlContains: options.expectSqlContains,\n });\n },\n async applyAndVerifyMigrations(options): Promise<{ statuses: { id: string; applied: boolean }[] }> {\n const harness = options.harness ?? tangoHelpers.getTestHarness();\n return applyAndVerifyMigrationsFn(harness, {\n migrationsDir: options.migrationsDir,\n toId: options.toId,\n expectedAppliedIds: options.expectedAppliedIds,\n });\n },\n async introspectSchema(harness?: IntegrationHarness): Promise<unknown> {\n return introspectSchemaFn(harness ?? tangoHelpers.getTestHarness());\n },\n async seedTable<T extends Record<string, unknown>>(\n table: string,\n rows: T[],\n harness?: IntegrationHarness\n ): Promise<void> {\n await seedTableFn(harness ?? tangoHelpers.getTestHarness(), table, rows);\n },\n createRepositoryFixture<TModel extends Record<string, any>>(options: {\n meta: import('@danceroutine/tango-orm/query').RepoMeta;\n harness?: IntegrationHarness;\n }): import('@danceroutine/tango-orm').Repository<TModel> {\n return createRepositoryFixtureFn<TModel>({\n harness: options.harness ?? tangoHelpers.getTestHarness(),\n meta: options.meta,\n });\n },\n async expectQueryResult<T>(actual: Promise<T> | T, expected: T): Promise<void> {\n await expectQueryResultFn(actual, expected);\n },\n};\n\nexpect.extend({\n toMatchSchema(received: unknown, schema: Parseable) {\n try {\n schema.parse(received);\n return {\n pass: true,\n message: () => 'expected data not to match schema',\n };\n } catch (error) {\n const detail = isError(error) ? error.message : String(error);\n return {\n pass: false,\n message: () => `expected data to match schema\\n\\n${detail}`,\n };\n }\n },\n});\n\n(vi as unknown as { tango?: TangoVitestHelpers }).tango = tangoHelpers;\n\ndeclare module 'vitest' {\n interface Assertion<T> {\n toMatchSchema(schema: Parseable): void;\n }\n\n interface AsymmetricMatchersContaining {\n toMatchSchema(schema: Parseable): void;\n }\n\n interface VitestUtils {\n tango: TangoVitestHelpers;\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport * from './registerVitestTango';\n"],"mappings":";;;;AA4BA,SAAS,QAAQA,OAAgC;AAC7C,eACW,UAAU,YACjB,UAAU,eACF,MAA6B,SAAS,mBACtC,MAAgC,YAAY;AAE3D;AAED,IAAIC,gBAA2C;AAE/C,eAAe,eACXC,OAC2B;AAC3B,YAAW,UAAU,WACjB,QAAO,OAAO;AAElB,QAAO;AACV;AAqBD,MAAMC,eAAmC;CACrC,MAAM,WAAW,OAAoC;EACjD,MAAM,UAAU,MAAM,eAAe,MAAM;AAC3C,QAAM,QAAQ,OAAO;AACrB,kBAAgB;AAChB,SAAO;CACV;CACD,iBAAqC;AACjC,OAAK,cACD,OAAM,IAAI,MAAM;AAEpB,SAAO;CACV;CACD,cAAuC;AACnC,SAAO,YAAY,aAAa;CACnC;CACD,MAAM,oBAAoB,SAA0B;EAChD,MAAM,UAAU,QAAQ,WAAW,aAAa,gBAAgB;AAChE,SAAO,oBAAsB,SAAS;GAClC,eAAe,QAAQ;GACvB,mBAAmB,QAAQ;EAC9B,EAAC;CACL;CACD,MAAM,yBAAyB,SAAoE;EAC/F,MAAM,UAAU,QAAQ,WAAW,aAAa,gBAAgB;AAChE,SAAO,yBAA2B,SAAS;GACvC,eAAe,QAAQ;GACvB,MAAM,QAAQ;GACd,oBAAoB,QAAQ;EAC/B,EAAC;CACL;CACD,MAAM,iBAAiBC,SAAgD;AACnE,SAAO,iBAAmB,WAAW,aAAa,gBAAgB,CAAC;CACtE;CACD,MAAM,UACFC,OACAC,MACAF,SACa;AACb,QAAM,UAAY,WAAW,aAAa,gBAAgB,EAAE,OAAO,KAAK;CAC3E;CACD,wBAA4DG,SAGH;AACrD,SAAO,wBAAkC;GACrC,SAAS,QAAQ,WAAW,aAAa,gBAAgB;GACzD,MAAM,QAAQ;EACjB,EAAC;CACL;CACD,MAAM,kBAAqBC,QAAwBC,UAA4B;AAC3E,QAAM,kBAAoB,QAAQ,SAAS;CAC9C;AACJ;AAED,OAAO,OAAO,EACV,cAAcC,UAAmBC,QAAmB;AAChD,KAAI;AACA,SAAO,MAAM,SAAS;AACtB,SAAO;GACH,MAAM;GACN,SAAS,MAAM;EAClB;CACJ,SAAQ,OAAO;EACZ,MAAM,SAAS,QAAQ,MAAM,GAAG,MAAM,UAAU,OAAO,MAAM;AAC7D,SAAO;GACH,MAAM;GACN,SAAS,OAAO,mCAAmC,OAAO;EAC7D;CACJ;AACJ,EACJ,EAAC;AAEF,GAAkD,QAAQ"}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@danceroutine/tango-testing",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Testing utilities for Tango",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./integration": {
|
|
14
|
+
"types": "./dist/integration/index.d.ts",
|
|
15
|
+
"import": "./dist/integration/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./vitest": {
|
|
18
|
+
"types": "./dist/vitest/index.d.ts",
|
|
19
|
+
"import": "./dist/vitest/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./mocks": {
|
|
22
|
+
"types": "./dist/mocks/index.d.ts",
|
|
23
|
+
"import": "./dist/mocks/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./factories": {
|
|
26
|
+
"types": "./dist/factories/index.d.ts",
|
|
27
|
+
"import": "./dist/factories/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./assertions": {
|
|
30
|
+
"types": "./dist/assertions/index.d.ts",
|
|
31
|
+
"import": "./dist/assertions/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsdown",
|
|
39
|
+
"test": "vitest run --coverage",
|
|
40
|
+
"test:watch": "vitest",
|
|
41
|
+
"typecheck": "tsc --noEmit"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"tango",
|
|
45
|
+
"testing",
|
|
46
|
+
"test",
|
|
47
|
+
"utilities"
|
|
48
|
+
],
|
|
49
|
+
"author": "Pedro Del Moral Lopez",
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "https://github.com/danceroutine/tango.git",
|
|
54
|
+
"directory": "packages/testing"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@danceroutine/tango-config": "workspace:*",
|
|
58
|
+
"@danceroutine/tango-migrations": "workspace:*",
|
|
59
|
+
"@danceroutine/tango-orm": "workspace:*"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"vitest": "^4.0.0"
|
|
63
|
+
},
|
|
64
|
+
"peerDependenciesMeta": {
|
|
65
|
+
"vitest": {
|
|
66
|
+
"optional": true
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@types/node": "^22.9.0",
|
|
71
|
+
"tsdown": "^0.4.0",
|
|
72
|
+
"typescript": "^5.6.3",
|
|
73
|
+
"vitest": "^4.0.6",
|
|
74
|
+
"zod": "^4.0.0"
|
|
75
|
+
}
|
|
76
|
+
}
|