@fougere/testing 0.3.0-alpha.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/LICENSE +21 -0
- package/README.md +43 -0
- package/dist/all.d.ts +33 -0
- package/dist/all.d.ts.map +1 -0
- package/dist/all.js +47 -0
- package/dist/all.js.map +1 -0
- package/dist/app.d.ts +68 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +65 -0
- package/dist/app.js.map +1 -0
- package/dist/comparison.d.ts +43 -0
- package/dist/comparison.d.ts.map +1 -0
- package/dist/comparison.js +182 -0
- package/dist/comparison.js.map +1 -0
- package/dist/derive.d.ts +12 -0
- package/dist/derive.d.ts.map +1 -0
- package/dist/derive.js +13 -0
- package/dist/derive.js.map +1 -0
- package/dist/doors.d.ts +37 -0
- package/dist/doors.d.ts.map +1 -0
- package/dist/doors.js +76 -0
- package/dist/doors.js.map +1 -0
- package/dist/gql.d.ts +67 -0
- package/dist/gql.d.ts.map +1 -0
- package/dist/gql.js +113 -0
- package/dist/gql.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/load.d.ts +34 -0
- package/dist/load.d.ts.map +1 -0
- package/dist/load.js +92 -0
- package/dist/load.js.map +1 -0
- package/dist/remotes.d.ts +49 -0
- package/dist/remotes.d.ts.map +1 -0
- package/dist/remotes.js +121 -0
- package/dist/remotes.js.map +1 -0
- package/dist/sample.d.ts +22 -0
- package/dist/sample.d.ts.map +1 -0
- package/dist/sample.js +61 -0
- package/dist/sample.js.map +1 -0
- package/dist/scope.d.ts +49 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +62 -0
- package/dist/scope.js.map +1 -0
- package/dist/stub.d.ts +44 -0
- package/dist/stub.d.ts.map +1 -0
- package/dist/stub.js +71 -0
- package/dist/stub.js.map +1 -0
- package/dist/sync.d.ts +46 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +79 -0
- package/dist/sync.js.map +1 -0
- package/dist/vitest.d.ts +22 -0
- package/dist/vitest.d.ts.map +1 -0
- package/dist/vitest.js +49 -0
- package/dist/vitest.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remotes.js","sourceRoot":"","sources":["../src/remotes.ts"],"names":[],"mappings":"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"}
|
package/dist/sample.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type SchemaView } from '@fougere/schema';
|
|
2
|
+
/**
|
|
3
|
+
* A body a client could legitimately send, built from what the entity declares.
|
|
4
|
+
*
|
|
5
|
+
* The shape IS JSON Schema, so the value itself is not ours to invent — `json-schema-faker`
|
|
6
|
+
* honours `minLength`, `enum`, `format`, `pattern`, `items` and `required`. What is ours is
|
|
7
|
+
* WHICH fields belong in a body, and that is `inputFields`: the one reader of the boundary
|
|
8
|
+
* and lifecycle axes the façade and the form already stand on. Re-deriving "not primary,
|
|
9
|
+
* not stamped, not read-only" here would make this a second opinion on the axes.
|
|
10
|
+
*/
|
|
11
|
+
export interface SampleOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Fixes what is generated. Defaults to a value derived from the entity name, so two
|
|
14
|
+
* runs agree and a failure is replayable — a body drawn afresh every time produces the
|
|
15
|
+
* test that fails once in twenty and cannot be reproduced.
|
|
16
|
+
*/
|
|
17
|
+
seed?: number;
|
|
18
|
+
}
|
|
19
|
+
/** How to reproduce the last generated body, in the words that reproduce it. */
|
|
20
|
+
export declare function replaySeed(): string;
|
|
21
|
+
export declare function sampleInput(entity: SchemaView, given?: Record<string, unknown>, options?: SampleOptions): Record<string, unknown>;
|
|
22
|
+
//# sourceMappingURL=sample.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sample.d.ts","sourceRoot":"","sources":["../src/sample.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8C,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG9F;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AA8BD,gFAAgF;AAChF,wBAAgB,UAAU,IAAI,MAAM,CAInC;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,EAClB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,OAAO,GAAE,aAAkB,GAC1B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyBzB"}
|
package/dist/sample.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { inputFields, Role } from '@fougere/schema';
|
|
2
|
+
import { generateSync } from 'json-schema-faker';
|
|
3
|
+
/** Stable across runs and across machines: the entity name is the only input. */
|
|
4
|
+
function seedOf(name) {
|
|
5
|
+
let hash = 0;
|
|
6
|
+
for (let i = 0; i < name.length; i++)
|
|
7
|
+
hash = (hash * 31 + name.charCodeAt(i)) | 0;
|
|
8
|
+
return Math.abs(hash) || 1;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A relation has no value of its own to invent — `ref(Author)` names a row that must
|
|
12
|
+
* exist, and a made-up id points at nothing. So it is REFUSED by name rather than
|
|
13
|
+
* omitted: a body silently missing a required reference is a body the judge rejects for
|
|
14
|
+
* a reason that has nothing to do with the test.
|
|
15
|
+
*/
|
|
16
|
+
function referencesIn(fields) {
|
|
17
|
+
return Object.entries(fields)
|
|
18
|
+
.filter(([, field]) => Role.of(field).isReference)
|
|
19
|
+
.map(([name]) => name);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The seed the last sample used, so a failure can say how to replay it.
|
|
23
|
+
*
|
|
24
|
+
* A stable seed nobody can read is a stable seed for nothing: the value has to reach the
|
|
25
|
+
* person looking at the red line. Held here rather than returned, so the signature stays
|
|
26
|
+
* the body a caller wanted.
|
|
27
|
+
*/
|
|
28
|
+
let lastSeed;
|
|
29
|
+
/** How to reproduce the last generated body, in the words that reproduce it. */
|
|
30
|
+
export function replaySeed() {
|
|
31
|
+
return lastSeed
|
|
32
|
+
? `sampleInput(${lastSeed.entity}, {}, { seed: ${lastSeed.seed} })`
|
|
33
|
+
: 'nothing has been sampled yet';
|
|
34
|
+
}
|
|
35
|
+
export function sampleInput(entity, given = {}, options = {}) {
|
|
36
|
+
const fields = inputFields(entity.getFields());
|
|
37
|
+
const missing = referencesIn(fields).filter((name) => !(name in given));
|
|
38
|
+
if (missing.length > 0) {
|
|
39
|
+
throw new Error(`[sampleInput] ${missing.join(', ')} ${missing.length > 1 ? 'are references' : 'is a reference'} — `
|
|
40
|
+
+ 'a generated id points at no row. Pass the ids: '
|
|
41
|
+
+ `sampleInput(Entity, { ${missing.map((n) => `${n}: '…'`).join(', ')} }).`);
|
|
42
|
+
}
|
|
43
|
+
const seed = options.seed ?? seedOf(entity.name ?? 'anonymous');
|
|
44
|
+
lastSeed = { entity: entity.name ?? 'Entity', seed };
|
|
45
|
+
const body = {};
|
|
46
|
+
let nth = 0;
|
|
47
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
48
|
+
if (name in given) {
|
|
49
|
+
body[name] = given[name];
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
// One seed per field, derived from one seed per entity: two fields of the same shape
|
|
53
|
+
// would otherwise carry the same value, and a test asserting on `title` would pass
|
|
54
|
+
// while reading `body`.
|
|
55
|
+
// A `Shape` IS a JSON Schema; the two packages declare the same concept and only
|
|
56
|
+
// disagree on `readonly`, which no value crosses.
|
|
57
|
+
body[name] = generateSync(field.shape, { seed: seed + nth++ });
|
|
58
|
+
}
|
|
59
|
+
return body;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=sample.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sample.js","sourceRoot":"","sources":["../src/sample.ts"],"names":[],"mappings":"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"}
|
package/dist/scope.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a test file's position states about its subject.
|
|
3
|
+
*
|
|
4
|
+
* The same reading the scan already performs on `entities/` and `handlers/`: a directory
|
|
5
|
+
* is a declaration. A file under `fronds/blog/tests/` says its subject is `blog`, so that
|
|
6
|
+
* frond is real and its neighbours are not — which is a TOPOLOGY, the very thing
|
|
7
|
+
* `remotes:` states in production, and not a mode of testing.
|
|
8
|
+
*
|
|
9
|
+
* The sub-directory below `tests/` carries nothing. A name like `it('refuses a payment')`
|
|
10
|
+
* is prose, and prose deciding how an app is wired is the hidden runtime the doctrine
|
|
11
|
+
* refuses; a path is a position, which a reader sees by looking at where the file sits.
|
|
12
|
+
*/
|
|
13
|
+
export interface Scope {
|
|
14
|
+
/** The project the app boots from — where `fronds/` and `fougere.config.ts` live. */
|
|
15
|
+
root: string;
|
|
16
|
+
/** The frond under test. Absent means every frond is real: several of them, together. */
|
|
17
|
+
frond?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The frond a path sits in, or nothing.
|
|
21
|
+
*
|
|
22
|
+
* The LAST `fronds/` segment wins: a frond may hold a synced copy of a neighbour under
|
|
23
|
+
* `.fougere/remotes/`, and a test that ever lands beside one is about the inner frond.
|
|
24
|
+
*/
|
|
25
|
+
export declare function frondOf(path: string, frondsDir?: string): string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Where the project starts: the first ancestor holding a config or a `fronds/`.
|
|
28
|
+
*
|
|
29
|
+
* The config is probed FIRST, which is what lets the rest of this file ask it where fronds
|
|
30
|
+
* live: a project that renamed the directory still declares one, and only a project with
|
|
31
|
+
* no config at all is found by the convention.
|
|
32
|
+
*/
|
|
33
|
+
export declare function rootOf(path: string): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* The scope a test file declares by where it sits.
|
|
36
|
+
*
|
|
37
|
+
* Returns nothing when the file sits outside any project — a caller then states `root`
|
|
38
|
+
* itself, which is what this package's own tests do against their fixtures.
|
|
39
|
+
*/
|
|
40
|
+
export declare function scopeOf(path: string): Promise<Scope | undefined>;
|
|
41
|
+
/**
|
|
42
|
+
* The path of the running test file, from vitest.
|
|
43
|
+
*
|
|
44
|
+
* Read rather than guessed: `expect.getState()` is vitest's own API. Handed in by the
|
|
45
|
+
* caller because this module is ESM and cannot `require`, and because a package that
|
|
46
|
+
* imports vitest at the top level stops being loadable outside a test run.
|
|
47
|
+
*/
|
|
48
|
+
export declare function scopeOfRun(testPath: string | undefined): Promise<Scope | undefined>;
|
|
49
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,KAAK;IACpB,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,MAAmC,GAAG,MAAM,GAAG,SAAS,CAMxG;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CASvD;AAED;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAQtE;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAEzF"}
|
package/dist/scope.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, sep } from 'node:path';
|
|
3
|
+
import { DEFAULT_CONVENTIONS, loadConfig, resolveConventions } from '@fougere/core/node';
|
|
4
|
+
/**
|
|
5
|
+
* The frond a path sits in, or nothing.
|
|
6
|
+
*
|
|
7
|
+
* The LAST `fronds/` segment wins: a frond may hold a synced copy of a neighbour under
|
|
8
|
+
* `.fougere/remotes/`, and a test that ever lands beside one is about the inner frond.
|
|
9
|
+
*/
|
|
10
|
+
export function frondOf(path, frondsDir = DEFAULT_CONVENTIONS.fronds) {
|
|
11
|
+
const parts = path.split(sep);
|
|
12
|
+
const at = parts.lastIndexOf(frondsDir);
|
|
13
|
+
if (at === -1 || at + 1 >= parts.length)
|
|
14
|
+
return undefined;
|
|
15
|
+
const name = parts[at + 1];
|
|
16
|
+
return name && !name.endsWith('.ts') ? name : undefined;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Where the project starts: the first ancestor holding a config or a `fronds/`.
|
|
20
|
+
*
|
|
21
|
+
* The config is probed FIRST, which is what lets the rest of this file ask it where fronds
|
|
22
|
+
* live: a project that renamed the directory still declares one, and only a project with
|
|
23
|
+
* no config at all is found by the convention.
|
|
24
|
+
*/
|
|
25
|
+
export function rootOf(path) {
|
|
26
|
+
let at = dirname(path);
|
|
27
|
+
let previous = '';
|
|
28
|
+
while (at !== previous) {
|
|
29
|
+
if (existsSync(join(at, 'fougere.config.ts')) || existsSync(join(at, DEFAULT_CONVENTIONS.fronds)))
|
|
30
|
+
return at;
|
|
31
|
+
previous = at;
|
|
32
|
+
at = dirname(at);
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The scope a test file declares by where it sits.
|
|
38
|
+
*
|
|
39
|
+
* Returns nothing when the file sits outside any project — a caller then states `root`
|
|
40
|
+
* itself, which is what this package's own tests do against their fixtures.
|
|
41
|
+
*/
|
|
42
|
+
export async function scopeOf(path) {
|
|
43
|
+
const root = rootOf(path);
|
|
44
|
+
if (!root)
|
|
45
|
+
return undefined;
|
|
46
|
+
// The root is known, so its config can say what the fronds directory is called before
|
|
47
|
+
// the position is read against it.
|
|
48
|
+
const { fronds } = resolveConventions((await loadConfig(root)).conventions);
|
|
49
|
+
const frond = frondOf(path, fronds);
|
|
50
|
+
return { root, ...(frond ? { frond } : {}) };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The path of the running test file, from vitest.
|
|
54
|
+
*
|
|
55
|
+
* Read rather than guessed: `expect.getState()` is vitest's own API. Handed in by the
|
|
56
|
+
* caller because this module is ESM and cannot `require`, and because a package that
|
|
57
|
+
* imports vitest at the top level stops being loadable outside a test run.
|
|
58
|
+
*/
|
|
59
|
+
export async function scopeOfRun(testPath) {
|
|
60
|
+
return testPath ? scopeOf(testPath) : undefined;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAqBzF;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,SAAS,GAAW,mBAAmB,CAAC,MAAM;IAClF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC1D,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAC3B,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClB,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;QACvB,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,mBAAmB,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC;YAAE,OAAO,EAAE,CAAC;QAC7G,QAAQ,GAAG,EAAE,CAAC;QACd,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAY;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,sFAAsF;IACtF,mCAAmC;IACnC,MAAM,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC,CAAC,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAC5E,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC/C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAA4B;IAC3D,OAAO,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAClD,CAAC"}
|
package/dist/stub.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type Mock } from 'vitest';
|
|
2
|
+
import type { App } from '@fougere/core';
|
|
3
|
+
/** Anything a provider can be declared as: a class the container knows how to build. */
|
|
4
|
+
export type Port = abstract new (...args: never[]) => unknown;
|
|
5
|
+
/** The double handed in place of a port — one spy per method the port declares. */
|
|
6
|
+
export type Stub<T> = {
|
|
7
|
+
[K in keyof T]: T[K] extends (...args: never[]) => unknown ? Mock : T[K];
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* The methods a port declares, read from the prototype chain at runtime.
|
|
11
|
+
*
|
|
12
|
+
* Not written by hand and not read from the AST: `ProviderEntry` keeps `ctor`, `deps` and
|
|
13
|
+
* `filePath` and never the methods, while the prototype has them all along. So a double
|
|
14
|
+
* carries exactly what the port carries and gains a method the day the port does — which
|
|
15
|
+
* is the failure `ports.test.ts` records, a `charge is not a function` from a stand-in
|
|
16
|
+
* that did not carry what its type promised.
|
|
17
|
+
*
|
|
18
|
+
* Walks the chain because a port may itself extend one; stops at `Object.prototype`,
|
|
19
|
+
* whose members belong to no port.
|
|
20
|
+
*/
|
|
21
|
+
export declare function methodsOf(port: Port): string[];
|
|
22
|
+
/**
|
|
23
|
+
* A double for a port: every method present, every call recorded, nothing returned.
|
|
24
|
+
*
|
|
25
|
+
* What it RETURNS is not derivable and the value is the caller's to state — a service's
|
|
26
|
+
* return type is a bare TypeScript type, erased at runtime, with no declared fields for
|
|
27
|
+
* anything to build from. The same line the whole package sits on: what Fougere's
|
|
28
|
+
* vocabulary declares can be derived, arbitrary code cannot.
|
|
29
|
+
*/
|
|
30
|
+
export declare function stubOf<T>(port: Port): Stub<T>;
|
|
31
|
+
/**
|
|
32
|
+
* Put doubles in front of ports, in every frond scope that answers under their name.
|
|
33
|
+
*
|
|
34
|
+
* After the boot rather than through `ports:`, because the container resolves lazily: a
|
|
35
|
+
* provider is built on first `resolve`, so a value registered before any call is the one
|
|
36
|
+
* a handler receives. `registerValue` also marks it as not the container's to dispose,
|
|
37
|
+
* which is right — the test made it.
|
|
38
|
+
*
|
|
39
|
+
* A port nobody answers under is REFUSED by name, for the reason `ports:` refuses a key
|
|
40
|
+
* that matched nothing: a double that silently stands in front of no one reads as a test
|
|
41
|
+
* that covered a case it never reached.
|
|
42
|
+
*/
|
|
43
|
+
export declare function installStubs(app: App, ports: Port[]): Map<Port, Stub<unknown>>;
|
|
44
|
+
//# sourceMappingURL=stub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stub.d.ts","sourceRoot":"","sources":["../src/stub.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEvC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEzC,wFAAwF;AACxF,MAAM,MAAM,IAAI,GAAG,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC;AAE9D,mFAAmF;AACnF,MAAM,MAAM,IAAI,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC;AAEnG;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAY9C;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAI7C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAiB9E"}
|
package/dist/stub.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
/**
|
|
3
|
+
* The methods a port declares, read from the prototype chain at runtime.
|
|
4
|
+
*
|
|
5
|
+
* Not written by hand and not read from the AST: `ProviderEntry` keeps `ctor`, `deps` and
|
|
6
|
+
* `filePath` and never the methods, while the prototype has them all along. So a double
|
|
7
|
+
* carries exactly what the port carries and gains a method the day the port does — which
|
|
8
|
+
* is the failure `ports.test.ts` records, a `charge is not a function` from a stand-in
|
|
9
|
+
* that did not carry what its type promised.
|
|
10
|
+
*
|
|
11
|
+
* Walks the chain because a port may itself extend one; stops at `Object.prototype`,
|
|
12
|
+
* whose members belong to no port.
|
|
13
|
+
*/
|
|
14
|
+
export function methodsOf(port) {
|
|
15
|
+
const found = new Set();
|
|
16
|
+
let proto = port.prototype;
|
|
17
|
+
while (proto && proto !== Object.prototype) {
|
|
18
|
+
for (const name of Object.getOwnPropertyNames(proto)) {
|
|
19
|
+
if (name === 'constructor')
|
|
20
|
+
continue;
|
|
21
|
+
const declared = Object.getOwnPropertyDescriptor(proto, name);
|
|
22
|
+
if (typeof declared?.value === 'function')
|
|
23
|
+
found.add(name);
|
|
24
|
+
}
|
|
25
|
+
proto = Object.getPrototypeOf(proto);
|
|
26
|
+
}
|
|
27
|
+
return [...found];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A double for a port: every method present, every call recorded, nothing returned.
|
|
31
|
+
*
|
|
32
|
+
* What it RETURNS is not derivable and the value is the caller's to state — a service's
|
|
33
|
+
* return type is a bare TypeScript type, erased at runtime, with no declared fields for
|
|
34
|
+
* anything to build from. The same line the whole package sits on: what Fougere's
|
|
35
|
+
* vocabulary declares can be derived, arbitrary code cannot.
|
|
36
|
+
*/
|
|
37
|
+
export function stubOf(port) {
|
|
38
|
+
const double = {};
|
|
39
|
+
for (const method of methodsOf(port))
|
|
40
|
+
double[method] = vi.fn();
|
|
41
|
+
return double;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Put doubles in front of ports, in every frond scope that answers under their name.
|
|
45
|
+
*
|
|
46
|
+
* After the boot rather than through `ports:`, because the container resolves lazily: a
|
|
47
|
+
* provider is built on first `resolve`, so a value registered before any call is the one
|
|
48
|
+
* a handler receives. `registerValue` also marks it as not the container's to dispose,
|
|
49
|
+
* which is right — the test made it.
|
|
50
|
+
*
|
|
51
|
+
* A port nobody answers under is REFUSED by name, for the reason `ports:` refuses a key
|
|
52
|
+
* that matched nothing: a double that silently stands in front of no one reads as a test
|
|
53
|
+
* that covered a case it never reached.
|
|
54
|
+
*/
|
|
55
|
+
export function installStubs(app, ports) {
|
|
56
|
+
const doubles = new Map();
|
|
57
|
+
const scopes = app.fronds.map((frond) => app.resolve(`frond:${frond.name}`));
|
|
58
|
+
for (const port of ports) {
|
|
59
|
+
const answering = scopes.filter((scope) => scope.has(port.name));
|
|
60
|
+
if (answering.length === 0) {
|
|
61
|
+
throw new Error(`[stub] ${port.name} — nothing answers under that name in any frond. `
|
|
62
|
+
+ 'A port is a class a provider extends; a class nobody extends is an ordinary service.');
|
|
63
|
+
}
|
|
64
|
+
const double = stubOf(port);
|
|
65
|
+
for (const scope of answering)
|
|
66
|
+
scope.registerValue(port.name, double);
|
|
67
|
+
doubles.set(port, double);
|
|
68
|
+
}
|
|
69
|
+
return doubles;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=stub.js.map
|
package/dist/stub.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stub.js","sourceRoot":"","sources":["../src/stub.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAa,MAAM,QAAQ,CAAC;AAUvC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CAAC,IAAU;IAClC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,IAAI,KAAK,GAAkB,IAAI,CAAC,SAAmB,CAAC;IACpD,OAAO,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;YACrD,IAAI,IAAI,KAAK,aAAa;gBAAE,SAAS;YACrC,MAAM,QAAQ,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAC9D,IAAI,OAAO,QAAQ,EAAE,KAAK,KAAK,UAAU;gBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC7D,CAAC;QACD,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAkB,CAAC;IACxD,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC;AACpB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,MAAM,CAAI,IAAU;IAClC,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,KAAK,MAAM,MAAM,IAAI,SAAS,CAAC,IAAI,CAAC;QAAE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;IAC/D,OAAO,MAAiB,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAAC,GAAQ,EAAE,KAAa;IAClD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC/C,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAY,SAAS,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAExF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACjE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACb,UAAU,IAAI,CAAC,IAAI,mDAAmD;kBACpE,sFAAsF,CACzF,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5B,KAAK,MAAM,KAAK,IAAI,SAAS;YAAE,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/sync.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe as describeSchema, type Change } from '@fougere/schema';
|
|
2
|
+
import type { IdentityCard } from '@fougere/core';
|
|
3
|
+
/** One entry of `.fougere/remotes.json`, written by `fougere sync`. */
|
|
4
|
+
export interface SyncedRemote {
|
|
5
|
+
name: string;
|
|
6
|
+
url: string;
|
|
7
|
+
/** Where the synced classes were written. */
|
|
8
|
+
path: string;
|
|
9
|
+
}
|
|
10
|
+
/** What separates a consumer's synced copy from what the producer serves today. */
|
|
11
|
+
export interface SyncDrift {
|
|
12
|
+
frond: string;
|
|
13
|
+
/** An entity the consumer holds that the producer no longer serves. */
|
|
14
|
+
gone: string[];
|
|
15
|
+
/** A shape that moved under a name the consumer still holds. */
|
|
16
|
+
moved: {
|
|
17
|
+
entity: string;
|
|
18
|
+
changes: Change[];
|
|
19
|
+
}[];
|
|
20
|
+
}
|
|
21
|
+
/** The remotes a project synced, read from the file `fougere sync` writes. */
|
|
22
|
+
export declare function syncedRemotes(root: string): Promise<SyncedRemote[]>;
|
|
23
|
+
/**
|
|
24
|
+
* The shapes a consumer holds for one remote frond.
|
|
25
|
+
*
|
|
26
|
+
* Rebuilt from the classes rather than read back: `fougere sync` writes entities and an
|
|
27
|
+
* index and does NOT keep the card it received. So the consumer's copy of the OPERATIONS
|
|
28
|
+
* has no local counterpart at all, and only the shapes can be compared — which is the
|
|
29
|
+
* narrower half, and the one that breaks a caller silently.
|
|
30
|
+
*
|
|
31
|
+
* Closing that would mean `sync` writing the card beside the classes; it is a change to
|
|
32
|
+
* the CLI, not to this file.
|
|
33
|
+
*/
|
|
34
|
+
export declare function heldShapes(remote: SyncedRemote): Promise<Map<string, ReturnType<typeof describeSchema>>>;
|
|
35
|
+
/**
|
|
36
|
+
* What the consumer's copy no longer matches.
|
|
37
|
+
*
|
|
38
|
+
* Compared by the shape's own `title` — the entity's name as the descriptor carries it —
|
|
39
|
+
* rather than by the door's, because a door is addressed in lowercase (`post`) and a class
|
|
40
|
+
* is not (`Post`), and matching those two by hand is where a name convention gets copied
|
|
41
|
+
* a third time.
|
|
42
|
+
*/
|
|
43
|
+
export declare function syncDriftOf(held: Map<string, ReturnType<typeof describeSchema>>, card: IdentityCard, frond: string): SyncDrift;
|
|
44
|
+
/** Whether the copy still matches. */
|
|
45
|
+
export declare function inSync(drift: SyncDrift): boolean;
|
|
46
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,IAAI,cAAc,EAAQ,KAAK,MAAM,EAAmB,MAAM,iBAAiB,CAAC;AACjG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,mFAAmF;AACnF,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,gEAAgE;IAChE,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;CAChD;AAED,8EAA8E;AAC9E,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CASzE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAW9G;AAYD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,EACpD,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,MAAM,GACZ,SAAS,CAWX;AAED,sCAAsC;AACtC,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAEhD"}
|
package/dist/sync.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { describe as describeSchema, diff } from '@fougere/schema';
|
|
5
|
+
/** The remotes a project synced, read from the file `fougere sync` writes. */
|
|
6
|
+
export async function syncedRemotes(root) {
|
|
7
|
+
try {
|
|
8
|
+
const raw = await readFile(join(root, '.fougere', 'remotes.json'), 'utf8');
|
|
9
|
+
const parsed = JSON.parse(raw);
|
|
10
|
+
return Object.entries(parsed).map(([name, one]) => ({ name, ...one }));
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
// No file is the ordinary case: an app with no remote synced nothing.
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The shapes a consumer holds for one remote frond.
|
|
19
|
+
*
|
|
20
|
+
* Rebuilt from the classes rather than read back: `fougere sync` writes entities and an
|
|
21
|
+
* index and does NOT keep the card it received. So the consumer's copy of the OPERATIONS
|
|
22
|
+
* has no local counterpart at all, and only the shapes can be compared — which is the
|
|
23
|
+
* narrower half, and the one that breaks a caller silently.
|
|
24
|
+
*
|
|
25
|
+
* Closing that would mean `sync` writing the card beside the classes; it is a change to
|
|
26
|
+
* the CLI, not to this file.
|
|
27
|
+
*/
|
|
28
|
+
export async function heldShapes(remote) {
|
|
29
|
+
const held = new Map();
|
|
30
|
+
const index = pathToFileURL(join(remote.path, 'index.ts')).href;
|
|
31
|
+
const module = await import(index);
|
|
32
|
+
for (const [name, exported] of Object.entries(module)) {
|
|
33
|
+
const entity = exported;
|
|
34
|
+
if (typeof entity !== 'function' || typeof entity.getFields !== 'function')
|
|
35
|
+
continue;
|
|
36
|
+
held.set(name, describeSchema(entity));
|
|
37
|
+
}
|
|
38
|
+
return held;
|
|
39
|
+
}
|
|
40
|
+
/** The shapes a card announces, by the name a door carries. */
|
|
41
|
+
function servedShapes(card, frond) {
|
|
42
|
+
const served = new Map();
|
|
43
|
+
for (const one of card.fronds) {
|
|
44
|
+
if (one.name !== frond)
|
|
45
|
+
continue;
|
|
46
|
+
for (const door of one.doors)
|
|
47
|
+
if (door.schema)
|
|
48
|
+
served.set(door.schema.title ?? door.name, door.schema);
|
|
49
|
+
}
|
|
50
|
+
return served;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* What the consumer's copy no longer matches.
|
|
54
|
+
*
|
|
55
|
+
* Compared by the shape's own `title` — the entity's name as the descriptor carries it —
|
|
56
|
+
* rather than by the door's, because a door is addressed in lowercase (`post`) and a class
|
|
57
|
+
* is not (`Post`), and matching those two by hand is where a name convention gets copied
|
|
58
|
+
* a third time.
|
|
59
|
+
*/
|
|
60
|
+
export function syncDriftOf(held, card, frond) {
|
|
61
|
+
const served = servedShapes(card, frond);
|
|
62
|
+
const drift = { frond, gone: [], moved: [] };
|
|
63
|
+
for (const [name, mine] of held) {
|
|
64
|
+
const theirs = served.get(name) ?? served.get(name.toLowerCase());
|
|
65
|
+
if (!theirs) {
|
|
66
|
+
drift.gone.push(name);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const moved = diff(mine, theirs);
|
|
70
|
+
if (moved.changes.length > 0)
|
|
71
|
+
drift.moved.push({ entity: name, changes: moved.changes });
|
|
72
|
+
}
|
|
73
|
+
return drift;
|
|
74
|
+
}
|
|
75
|
+
/** Whether the copy still matches. */
|
|
76
|
+
export function inSync(drift) {
|
|
77
|
+
return drift.gone.length === 0 && drift.moved.length === 0;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=sync.js.map
|
package/dist/sync.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"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"}
|
package/dist/vitest.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vitest configuration a Fougere project needs, so nobody has to discover it.
|
|
3
|
+
*
|
|
4
|
+
* One thing to know, and it is not obvious: `graphql` guards its types with `instanceOf`,
|
|
5
|
+
* so a schema built by one loaded copy is refused by another with *"from another module
|
|
6
|
+
* or realm"*. Under vitest a module is either transformed by Vite or externalized and
|
|
7
|
+
* loaded by node, and Pothos ending up on one side while `graphql()` sits on the other
|
|
8
|
+
* makes two copies of the same file. The alias pins the path, and the inlining makes the
|
|
9
|
+
* packages that touch it take that path — neither alone is enough, which is what made
|
|
10
|
+
* this cost an afternoon.
|
|
11
|
+
*
|
|
12
|
+
* An app that serves no GraphQL pays nothing for this: the alias resolves to whatever
|
|
13
|
+
* `graphql` the project has, and no `graphql` means the option is simply unused.
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* // vitest.config.ts
|
|
17
|
+
* import { fougereTest } from '@fougere/testing/vitest';
|
|
18
|
+
* export default fougereTest();
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function fougereTest(overrides?: Record<string, unknown>): Record<string, unknown>;
|
|
22
|
+
//# sourceMappingURL=vitest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CAAC,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA2B5F"}
|
package/dist/vitest.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
/**
|
|
3
|
+
* The vitest configuration a Fougere project needs, so nobody has to discover it.
|
|
4
|
+
*
|
|
5
|
+
* One thing to know, and it is not obvious: `graphql` guards its types with `instanceOf`,
|
|
6
|
+
* so a schema built by one loaded copy is refused by another with *"from another module
|
|
7
|
+
* or realm"*. Under vitest a module is either transformed by Vite or externalized and
|
|
8
|
+
* loaded by node, and Pothos ending up on one side while `graphql()` sits on the other
|
|
9
|
+
* makes two copies of the same file. The alias pins the path, and the inlining makes the
|
|
10
|
+
* packages that touch it take that path — neither alone is enough, which is what made
|
|
11
|
+
* this cost an afternoon.
|
|
12
|
+
*
|
|
13
|
+
* An app that serves no GraphQL pays nothing for this: the alias resolves to whatever
|
|
14
|
+
* `graphql` the project has, and no `graphql` means the option is simply unused.
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* // vitest.config.ts
|
|
18
|
+
* import { fougereTest } from '@fougere/testing/vitest';
|
|
19
|
+
* export default fougereTest();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function fougereTest(overrides = {}) {
|
|
23
|
+
const require = createRequire(import.meta.url);
|
|
24
|
+
let graphqlDir;
|
|
25
|
+
try {
|
|
26
|
+
graphqlDir = require.resolve('graphql').replace(/\/index\.js$/, '');
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// No GraphQL in this project — nothing to pin.
|
|
30
|
+
}
|
|
31
|
+
const base = {
|
|
32
|
+
resolve: {
|
|
33
|
+
...(graphqlDir ? { alias: { graphql: graphqlDir }, dedupe: ['graphql'] } : {}),
|
|
34
|
+
},
|
|
35
|
+
test: {
|
|
36
|
+
// Both spellings of where a test lives: beside the frond it is about, and above
|
|
37
|
+
// the fronds when it is about several of them.
|
|
38
|
+
include: ['tests/**/*.test.ts', 'fronds/*/tests/**/*.test.ts'],
|
|
39
|
+
server: { deps: { inline: [/@fougere\//, '@pothos/core'] } },
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
...base,
|
|
44
|
+
...overrides,
|
|
45
|
+
resolve: { ...base.resolve, ...(overrides.resolve ?? {}) },
|
|
46
|
+
test: { ...base.test, ...(overrides.test ?? {}) },
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=vitest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,WAAW,CAAC,SAAS,GAA4B,EAAE;IACjE,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,UAA8B,CAAC;IACnC,IAAI,CAAC;QACH,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,+CAA+C;IACjD,CAAC;IAED,MAAM,IAAI,GAAG;QACX,OAAO,EAAE;YACP,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC/E;QACD,IAAI,EAAE;YACJ,gFAAgF;YAChF,+CAA+C;YAC/C,OAAO,EAAE,CAAC,oBAAoB,EAAE,6BAA6B,CAAC;YAC9D,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,YAAY,EAAE,cAAc,CAAC,EAAE,EAAE;SAC7D;KACF,CAAC;IAEF,OAAO;QACL,GAAG,IAAI;QACP,GAAG,SAAS;QACZ,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC,OAAiB,IAAI,EAAE,CAAC,EAAE;QACpE,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,SAAS,CAAC,IAAc,IAAI,EAAE,CAAC,EAAE;KAC5D,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fougere/testing",
|
|
3
|
+
"version": "0.3.0-alpha.0",
|
|
4
|
+
"description": "Tests derived from the declaration — cases, stubs and a gradient of realities.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"fougere",
|
|
7
|
+
"typescript",
|
|
8
|
+
"framework",
|
|
9
|
+
"testing",
|
|
10
|
+
"json-schema"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/chok/fougere.git",
|
|
16
|
+
"directory": "packages/testing"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"main": "dist/index.js",
|
|
20
|
+
"types": "dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./vitest": {
|
|
27
|
+
"types": "./dist/vitest.d.ts",
|
|
28
|
+
"import": "./dist/vitest.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"json-schema-faker": "^0.6.3",
|
|
36
|
+
"@fougere/container": "0.3.0-alpha.0",
|
|
37
|
+
"@fougere/defaults": "0.3.0-alpha.0",
|
|
38
|
+
"@fougere/schema": "0.3.0-alpha.0",
|
|
39
|
+
"@fougere/core": "0.3.0-alpha.0",
|
|
40
|
+
"@fougere/transport-http": "0.3.0-alpha.0",
|
|
41
|
+
"@fougere/app": "0.3.0-alpha.0"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"vitest": ">=2",
|
|
48
|
+
"@fougere/adapter-graphql": "0.3.0-alpha.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"vitest": {
|
|
52
|
+
"optional": true
|
|
53
|
+
},
|
|
54
|
+
"@fougere/adapter-graphql": {
|
|
55
|
+
"optional": true
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"vitest": "^4.1.0",
|
|
60
|
+
"@fougere/adapter-graphql": "0.3.0-alpha.0"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "rm -rf dist && tsc",
|
|
64
|
+
"typecheck": "tsc --noEmit -p tsconfig.test.json",
|
|
65
|
+
"test": "vitest run",
|
|
66
|
+
"test:watch": "vitest"
|
|
67
|
+
}
|
|
68
|
+
}
|