@glubean/graphql 0.1.7 → 0.2.2
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 +433 -0
- package/dist/contract/adapter.d.ts +27 -0
- package/dist/contract/adapter.d.ts.map +1 -0
- package/dist/contract/adapter.js +513 -0
- package/dist/contract/adapter.js.map +1 -0
- package/dist/contract/factory.d.ts +34 -0
- package/dist/contract/factory.d.ts.map +1 -0
- package/dist/contract/factory.js +94 -0
- package/dist/contract/factory.js.map +1 -0
- package/dist/contract/index.d.ts +16 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +15 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/matchers.d.ts +110 -0
- package/dist/contract/matchers.d.ts.map +1 -0
- package/dist/contract/matchers.js +244 -0
- package/dist/contract/matchers.js.map +1 -0
- package/dist/contract/types.d.ts +358 -0
- package/dist/contract/types.d.ts.map +1 -0
- package/dist/contract/types.js +41 -0
- package/dist/contract/types.js.map +1 -0
- package/dist/index.d.ts +50 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +69 -9
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/contract/factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL contract surface for @glubean/graphql.
|
|
3
|
+
*
|
|
4
|
+
* This module is **side-effect-free** — it only re-exports the adapter,
|
|
5
|
+
* factory, types, and the matcher collection so the manifest in
|
|
6
|
+
* `../index.ts` can reference them.
|
|
7
|
+
*
|
|
8
|
+
* Projects install the manifest via `installPlugin(graphqlPlugin)` (typically
|
|
9
|
+
* driven by `bootstrap()` loading `glubean.setup.ts`). There is no more
|
|
10
|
+
* "import the package and matchers auto-appear" path.
|
|
11
|
+
*/
|
|
12
|
+
export { graphqlAdapter } from "./adapter.js";
|
|
13
|
+
export { createGraphqlFactory, createGraphqlRoot } from "./factory.js";
|
|
14
|
+
export { graphqlMatchers } from "./matchers.js";
|
|
15
|
+
export type { GraphqlContractCase, GraphqlContractDefaults, GraphqlContractExample, GraphqlContractExpect, GraphqlContractFactory, GraphqlContractMeta, GraphqlContractRoot, GraphqlContractSafeMeta, GraphqlContractSpec, GraphqlCaseResult, GraphqlErrorsExpect, GraphqlFlowCaseOutput, GraphqlPayloadSchemas, GraphqlSafeSchemas, GraphqlTypeDef, GraphqlTypeDefs, InferGraphqlVariables, InferGraphqlResponse, } from "./types.js";
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL contract surface for @glubean/graphql.
|
|
3
|
+
*
|
|
4
|
+
* This module is **side-effect-free** — it only re-exports the adapter,
|
|
5
|
+
* factory, types, and the matcher collection so the manifest in
|
|
6
|
+
* `../index.ts` can reference them.
|
|
7
|
+
*
|
|
8
|
+
* Projects install the manifest via `installPlugin(graphqlPlugin)` (typically
|
|
9
|
+
* driven by `bootstrap()` loading `glubean.setup.ts`). There is no more
|
|
10
|
+
* "import the package and matchers auto-appear" path.
|
|
11
|
+
*/
|
|
12
|
+
export { graphqlAdapter } from "./adapter.js";
|
|
13
|
+
export { createGraphqlFactory, createGraphqlRoot } from "./factory.js";
|
|
14
|
+
export { graphqlMatchers } from "./matchers.js";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL custom matchers for `ctx.expect()`.
|
|
3
|
+
*
|
|
4
|
+
* Installed on `Expectation.prototype` when `installPlugin(graphqlPlugin)`
|
|
5
|
+
* runs (typically via `glubean.setup.ts` → `bootstrap()`). The
|
|
6
|
+
* `declare module "@glubean/sdk/expect"` block below publishes ambient
|
|
7
|
+
* types so `ctx.expect(res).toHaveGraphqlData(...)` is fully typed wherever
|
|
8
|
+
* `@glubean/graphql` is referenced, even if the actual runtime install
|
|
9
|
+
* happens elsewhere in the project.
|
|
10
|
+
*
|
|
11
|
+
* Matchers work on any object that carries GraphQL response shape, including:
|
|
12
|
+
* - `GraphQLResult<T>` from `@glubean/graphql` transport
|
|
13
|
+
* (`gql.query(...)` / `gql.mutate(...)`)
|
|
14
|
+
* - `GraphqlCaseResult<T>` from the contract adapter
|
|
15
|
+
* (verify callback / flow `out` lens input)
|
|
16
|
+
*
|
|
17
|
+
* Both expose:
|
|
18
|
+
* {
|
|
19
|
+
* data: T | null,
|
|
20
|
+
* errors?: GraphQLError[],
|
|
21
|
+
* extensions?: Record<string, unknown>,
|
|
22
|
+
* httpStatus: number,
|
|
23
|
+
* headers: Record<string, string | string[]>,
|
|
24
|
+
* rawBody: string | null,
|
|
25
|
+
* ...
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* The HTTP `toHaveStatus` built-in matcher reads `actual.status`, which the
|
|
29
|
+
* GraphQL envelope does NOT expose (it's `httpStatus` — a deliberate rename
|
|
30
|
+
* in CG-10 to avoid shadowing the native `Response.status` semantics). The
|
|
31
|
+
* `toHaveHttpStatus` matcher added here reads the envelope's `httpStatus`
|
|
32
|
+
* field so GraphQL users have a symmetric transport-level assertion.
|
|
33
|
+
*/
|
|
34
|
+
import { type MatcherResult } from "@glubean/sdk/expect";
|
|
35
|
+
declare module "@glubean/sdk/expect" {
|
|
36
|
+
interface CustomMatchers<T> {
|
|
37
|
+
/**
|
|
38
|
+
* Assert the transport-level HTTP status on a `GraphQLResult` /
|
|
39
|
+
* `GraphqlCaseResult`. Reads `actual.httpStatus`.
|
|
40
|
+
*
|
|
41
|
+
* Use this (not `toHaveStatus`) for GraphQL responses — the envelope
|
|
42
|
+
* field is `httpStatus`, not `status`.
|
|
43
|
+
*
|
|
44
|
+
* @example ctx.expect(res).toHaveHttpStatus(200);
|
|
45
|
+
* @example ctx.expect(res).toHaveHttpStatus(401, "missing token");
|
|
46
|
+
*/
|
|
47
|
+
toHaveHttpStatus(code: number, message?: string): Expectation<T>;
|
|
48
|
+
/**
|
|
49
|
+
* Partial-match the GraphQL `data` field (like `toMatchObject`).
|
|
50
|
+
*
|
|
51
|
+
* Fails if `data` is null or the subset is not contained in `data`.
|
|
52
|
+
*
|
|
53
|
+
* @example ctx.expect(res).toHaveGraphqlData({ user: { name: "Alice" } });
|
|
54
|
+
*/
|
|
55
|
+
toHaveGraphqlData(subset: Record<string, unknown>, message?: string): Expectation<T>;
|
|
56
|
+
/**
|
|
57
|
+
* Assert the `errors` array is absent or empty (strict success).
|
|
58
|
+
*
|
|
59
|
+
* @example ctx.expect(res).toHaveGraphqlNoErrors();
|
|
60
|
+
*/
|
|
61
|
+
toHaveGraphqlNoErrors(message?: string): Expectation<T>;
|
|
62
|
+
/**
|
|
63
|
+
* Assert at least one entry in `errors` has matching
|
|
64
|
+
* `extensions.code` (case-insensitive).
|
|
65
|
+
*
|
|
66
|
+
* @example ctx.expect(res).toHaveGraphqlErrorCode("UNAUTHENTICATED");
|
|
67
|
+
*/
|
|
68
|
+
toHaveGraphqlErrorCode(code: string, message?: string): Expectation<T>;
|
|
69
|
+
/**
|
|
70
|
+
* Assert a key exists in `extensions` (server-side tracing / cost),
|
|
71
|
+
* optionally matching an exact value.
|
|
72
|
+
*
|
|
73
|
+
* @example ctx.expect(res).toHaveGraphqlExtension("tracing");
|
|
74
|
+
* @example ctx.expect(res).toHaveGraphqlExtension("version", "v2");
|
|
75
|
+
*/
|
|
76
|
+
toHaveGraphqlExtension(key: string, value?: unknown, message?: string): Expectation<T>;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Register GraphQL matchers onto the shared `Expectation` prototype. Called
|
|
81
|
+
* from `./index.ts` during the same side-effect block that registers the
|
|
82
|
+
* contract adapter; users get matchers + adapter from a single
|
|
83
|
+
* `import "@glubean/graphql"`.
|
|
84
|
+
*
|
|
85
|
+
* Idempotent: swallows the "matcher already exists" error thrown by
|
|
86
|
+
* `Expectation.extend` on re-evaluation (duplicate imports, Vitest
|
|
87
|
+
* isolation boundaries, etc.).
|
|
88
|
+
*
|
|
89
|
+
* Note: `toHaveHttpStatus` is registered here even though it's
|
|
90
|
+
* conceptually transport-level (not GraphQL-specific) — users of
|
|
91
|
+
* `@glubean/graphql` are the ones who need it because the GraphQL
|
|
92
|
+
* envelope uses `httpStatus` instead of `status`. If a future package
|
|
93
|
+
* also exposes an `httpStatus` field, registration idempotency will
|
|
94
|
+
* catch the conflict at boot.
|
|
95
|
+
*/
|
|
96
|
+
/**
|
|
97
|
+
* Collection of GraphQL custom matchers, keyed by matcher name.
|
|
98
|
+
*
|
|
99
|
+
* Consumed by the plugin manifest in `graphql/src/index.ts` as
|
|
100
|
+
* `manifest.matchers`. `installPlugin` drives the actual `Expectation.extend`
|
|
101
|
+
* call — plugin authors never need to touch it directly.
|
|
102
|
+
*/
|
|
103
|
+
export declare const graphqlMatchers: {
|
|
104
|
+
readonly toHaveHttpStatus: (actual: unknown, ...args: unknown[]) => MatcherResult;
|
|
105
|
+
readonly toHaveGraphqlData: (actual: unknown, ...args: unknown[]) => MatcherResult;
|
|
106
|
+
readonly toHaveGraphqlNoErrors: (actual: unknown) => MatcherResult;
|
|
107
|
+
readonly toHaveGraphqlErrorCode: (actual: unknown, ...args: unknown[]) => MatcherResult;
|
|
108
|
+
readonly toHaveGraphqlExtension: (actual: unknown, ...args: unknown[]) => MatcherResult;
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=matchers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../../src/contract/matchers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAO/E,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,cAAc,CAAC,CAAC;QACxB;;;;;;;;;WASG;QACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAEjE;;;;;;WAMG;QACH,iBAAiB,CACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,CAAC,EAAE,MAAM,GACf,WAAW,CAAC,CAAC,CAAC,CAAC;QAElB;;;;WAIG;QACH,qBAAqB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAExD;;;;;WAKG;QACH,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAEvE;;;;;;WAMG;QACH,sBAAsB,CACpB,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,OAAO,EACf,OAAO,CAAC,EAAE,MAAM,GACf,WAAW,CAAC,CAAC,CAAC,CAAC;KACnB;CACF;AAgPD;;;;;;;;;;;;;;;;GAgBG;AACH;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;wCAhMlB,OAAO,WACN,OAAO,EAAE,KACjB,aAAa;yCAuBN,OAAO,WACN,OAAO,EAAE,KACjB,aAAa;6CAyCuB,OAAO,KAAG,aAAa;8CAsBpD,OAAO,WACN,OAAO,EAAE,KACjB,aAAa;8CA6BN,OAAO,WACN,OAAO,EAAE,KACjB,aAAa;CA2EN,CAAC"}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL custom matchers for `ctx.expect()`.
|
|
3
|
+
*
|
|
4
|
+
* Installed on `Expectation.prototype` when `installPlugin(graphqlPlugin)`
|
|
5
|
+
* runs (typically via `glubean.setup.ts` → `bootstrap()`). The
|
|
6
|
+
* `declare module "@glubean/sdk/expect"` block below publishes ambient
|
|
7
|
+
* types so `ctx.expect(res).toHaveGraphqlData(...)` is fully typed wherever
|
|
8
|
+
* `@glubean/graphql` is referenced, even if the actual runtime install
|
|
9
|
+
* happens elsewhere in the project.
|
|
10
|
+
*
|
|
11
|
+
* Matchers work on any object that carries GraphQL response shape, including:
|
|
12
|
+
* - `GraphQLResult<T>` from `@glubean/graphql` transport
|
|
13
|
+
* (`gql.query(...)` / `gql.mutate(...)`)
|
|
14
|
+
* - `GraphqlCaseResult<T>` from the contract adapter
|
|
15
|
+
* (verify callback / flow `out` lens input)
|
|
16
|
+
*
|
|
17
|
+
* Both expose:
|
|
18
|
+
* {
|
|
19
|
+
* data: T | null,
|
|
20
|
+
* errors?: GraphQLError[],
|
|
21
|
+
* extensions?: Record<string, unknown>,
|
|
22
|
+
* httpStatus: number,
|
|
23
|
+
* headers: Record<string, string | string[]>,
|
|
24
|
+
* rawBody: string | null,
|
|
25
|
+
* ...
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* The HTTP `toHaveStatus` built-in matcher reads `actual.status`, which the
|
|
29
|
+
* GraphQL envelope does NOT expose (it's `httpStatus` — a deliberate rename
|
|
30
|
+
* in CG-10 to avoid shadowing the native `Response.status` semantics). The
|
|
31
|
+
* `toHaveHttpStatus` matcher added here reads the envelope's `httpStatus`
|
|
32
|
+
* field so GraphQL users have a symmetric transport-level assertion.
|
|
33
|
+
*/
|
|
34
|
+
import { inspect } from "@glubean/sdk/expect";
|
|
35
|
+
function readHttpStatus(actual) {
|
|
36
|
+
const s = actual?.httpStatus;
|
|
37
|
+
return typeof s === "number" ? s : undefined;
|
|
38
|
+
}
|
|
39
|
+
function readEnvelope(actual) {
|
|
40
|
+
if (!actual || typeof actual !== "object")
|
|
41
|
+
return undefined;
|
|
42
|
+
return actual;
|
|
43
|
+
}
|
|
44
|
+
/** Minimal partial-match helper — checks that every key/value in subset
|
|
45
|
+
* is deep-equal to the corresponding path in target. Keeps this file
|
|
46
|
+
* dependency-free from the sdk internal `matchesObject`. */
|
|
47
|
+
function matchesSubset(target, subset) {
|
|
48
|
+
for (const [k, expected] of Object.entries(subset)) {
|
|
49
|
+
const actual = target[k];
|
|
50
|
+
if (expected !== null &&
|
|
51
|
+
typeof expected === "object" &&
|
|
52
|
+
!Array.isArray(expected)) {
|
|
53
|
+
if (!actual || typeof actual !== "object" || Array.isArray(actual)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
if (!matchesSubset(actual, expected)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else if (Array.isArray(expected)) {
|
|
61
|
+
if (!Array.isArray(actual) || actual.length !== expected.length) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
for (let i = 0; i < expected.length; i++) {
|
|
65
|
+
if (JSON.stringify(actual[i]) !== JSON.stringify(expected[i])) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (actual !== expected) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
// =============================================================================
|
|
77
|
+
// Matcher implementations
|
|
78
|
+
// =============================================================================
|
|
79
|
+
const toHaveHttpStatus = (actual, ...args) => {
|
|
80
|
+
const code = args[0];
|
|
81
|
+
const actualCode = readHttpStatus(actual);
|
|
82
|
+
if (actualCode === undefined) {
|
|
83
|
+
return {
|
|
84
|
+
passed: false,
|
|
85
|
+
message: `to have HTTP status ${code} — actual has no \`.httpStatus\` (got ${inspect(actual)})`,
|
|
86
|
+
actual,
|
|
87
|
+
expected: code,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
passed: actualCode === code,
|
|
92
|
+
message: `to have HTTP status ${code}`,
|
|
93
|
+
actual: actualCode,
|
|
94
|
+
expected: code,
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
const toHaveGraphqlData = (actual, ...args) => {
|
|
98
|
+
const subset = args[0];
|
|
99
|
+
const env = readEnvelope(actual);
|
|
100
|
+
if (!env) {
|
|
101
|
+
return {
|
|
102
|
+
passed: false,
|
|
103
|
+
message: `to have GraphQL data matching ${inspect(subset)} — actual is not an envelope`,
|
|
104
|
+
actual,
|
|
105
|
+
expected: subset,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const data = env.data;
|
|
109
|
+
if (data == null) {
|
|
110
|
+
return {
|
|
111
|
+
passed: false,
|
|
112
|
+
message: `to have GraphQL data matching ${inspect(subset)} — \`.data\` is null`,
|
|
113
|
+
actual: data,
|
|
114
|
+
expected: subset,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (typeof data !== "object" || Array.isArray(data)) {
|
|
118
|
+
return {
|
|
119
|
+
passed: false,
|
|
120
|
+
message: `to have GraphQL data matching ${inspect(subset)} — \`.data\` is not an object`,
|
|
121
|
+
actual: data,
|
|
122
|
+
expected: subset,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const passed = matchesSubset(data, subset);
|
|
126
|
+
return {
|
|
127
|
+
passed,
|
|
128
|
+
message: `to have GraphQL data matching ${inspect(subset)}`,
|
|
129
|
+
actual: data,
|
|
130
|
+
expected: subset,
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
const toHaveGraphqlNoErrors = (actual) => {
|
|
134
|
+
const env = readEnvelope(actual);
|
|
135
|
+
const errs = env?.errors;
|
|
136
|
+
const count = Array.isArray(errs) ? errs.length : 0;
|
|
137
|
+
const summary = count > 0
|
|
138
|
+
? errs.map((e) => e.message).slice(0, 3).join("; ") +
|
|
139
|
+
(count > 3 ? ` (+${count - 3} more)` : "")
|
|
140
|
+
: "";
|
|
141
|
+
return {
|
|
142
|
+
passed: count === 0,
|
|
143
|
+
message: count > 0
|
|
144
|
+
? `to have no GraphQL errors (got ${count}: ${summary})`
|
|
145
|
+
: `to have no GraphQL errors`,
|
|
146
|
+
actual: errs ?? [],
|
|
147
|
+
expected: [],
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
const toHaveGraphqlErrorCode = (actual, ...args) => {
|
|
151
|
+
const code = args[0];
|
|
152
|
+
const env = readEnvelope(actual);
|
|
153
|
+
const errs = env?.errors;
|
|
154
|
+
if (!Array.isArray(errs) || errs.length === 0) {
|
|
155
|
+
return {
|
|
156
|
+
passed: false,
|
|
157
|
+
message: `to have GraphQL error code ${inspect(code)} — no errors on envelope`,
|
|
158
|
+
actual: errs ?? [],
|
|
159
|
+
expected: { "extensions.code": code },
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const lower = code.toUpperCase();
|
|
163
|
+
const match = errs.find((e) => {
|
|
164
|
+
const c = e.extensions?.code;
|
|
165
|
+
return typeof c === "string" && c.toUpperCase() === lower;
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
passed: !!match,
|
|
169
|
+
message: `to have GraphQL error code ${inspect(code)}`,
|
|
170
|
+
actual: errs.map((e) => e.extensions?.code ?? null),
|
|
171
|
+
expected: code,
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
const toHaveGraphqlExtension = (actual, ...args) => {
|
|
175
|
+
const key = args[0];
|
|
176
|
+
const expectedValue = args[1];
|
|
177
|
+
const env = readEnvelope(actual);
|
|
178
|
+
const ext = env?.extensions;
|
|
179
|
+
if (!ext || typeof ext !== "object") {
|
|
180
|
+
return {
|
|
181
|
+
passed: false,
|
|
182
|
+
message: `to have GraphQL extension \`${key}\` — actual has no \`.extensions\``,
|
|
183
|
+
actual: ext,
|
|
184
|
+
expected: expectedValue !== undefined ? { [key]: expectedValue } : key,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (!(key in ext)) {
|
|
188
|
+
return {
|
|
189
|
+
passed: false,
|
|
190
|
+
message: `to have GraphQL extension \`${key}\``,
|
|
191
|
+
actual: Object.keys(ext),
|
|
192
|
+
expected: expectedValue !== undefined ? { [key]: expectedValue } : key,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
if (expectedValue === undefined) {
|
|
196
|
+
return {
|
|
197
|
+
passed: true,
|
|
198
|
+
message: `to have GraphQL extension \`${key}\``,
|
|
199
|
+
actual: ext[key],
|
|
200
|
+
expected: key,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
passed: JSON.stringify(ext[key]) === JSON.stringify(expectedValue),
|
|
205
|
+
message: `to have GraphQL extension \`${key}\` = ${inspect(expectedValue)}`,
|
|
206
|
+
actual: ext[key],
|
|
207
|
+
expected: expectedValue,
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
// =============================================================================
|
|
211
|
+
// Registration — side effect
|
|
212
|
+
// =============================================================================
|
|
213
|
+
/**
|
|
214
|
+
* Register GraphQL matchers onto the shared `Expectation` prototype. Called
|
|
215
|
+
* from `./index.ts` during the same side-effect block that registers the
|
|
216
|
+
* contract adapter; users get matchers + adapter from a single
|
|
217
|
+
* `import "@glubean/graphql"`.
|
|
218
|
+
*
|
|
219
|
+
* Idempotent: swallows the "matcher already exists" error thrown by
|
|
220
|
+
* `Expectation.extend` on re-evaluation (duplicate imports, Vitest
|
|
221
|
+
* isolation boundaries, etc.).
|
|
222
|
+
*
|
|
223
|
+
* Note: `toHaveHttpStatus` is registered here even though it's
|
|
224
|
+
* conceptually transport-level (not GraphQL-specific) — users of
|
|
225
|
+
* `@glubean/graphql` are the ones who need it because the GraphQL
|
|
226
|
+
* envelope uses `httpStatus` instead of `status`. If a future package
|
|
227
|
+
* also exposes an `httpStatus` field, registration idempotency will
|
|
228
|
+
* catch the conflict at boot.
|
|
229
|
+
*/
|
|
230
|
+
/**
|
|
231
|
+
* Collection of GraphQL custom matchers, keyed by matcher name.
|
|
232
|
+
*
|
|
233
|
+
* Consumed by the plugin manifest in `graphql/src/index.ts` as
|
|
234
|
+
* `manifest.matchers`. `installPlugin` drives the actual `Expectation.extend`
|
|
235
|
+
* call — plugin authors never need to touch it directly.
|
|
236
|
+
*/
|
|
237
|
+
export const graphqlMatchers = {
|
|
238
|
+
toHaveHttpStatus,
|
|
239
|
+
toHaveGraphqlData,
|
|
240
|
+
toHaveGraphqlNoErrors,
|
|
241
|
+
toHaveGraphqlErrorCode,
|
|
242
|
+
toHaveGraphqlExtension,
|
|
243
|
+
};
|
|
244
|
+
//# sourceMappingURL=matchers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matchers.js","sourceRoot":"","sources":["../../src/contract/matchers.ts"],"names":[],"mappings":"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"}
|