graphql-data-generator 0.2.3 → 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/esm/codegen.js +3 -1
- package/esm/init.js +80 -28
- package/esm/jest.js +120 -0
- package/esm/plugin.cjs +2 -1
- package/esm/util.js +22 -8
- package/package.json +25 -3
- package/types/init.d.ts +1 -1
- package/types/jest.d.ts +8 -0
- package/types/types.d.ts +1 -0
package/esm/codegen.js
CHANGED
|
@@ -555,7 +555,9 @@ ${usedTypes.map(([name]) => ` ${name}: ${rename(name)};`).join("\n")}
|
|
|
555
555
|
}
|
|
556
556
|
else if (enums.startsWith("import:") &&
|
|
557
557
|
usedReferences.some((r) => r.kind === Kind.ENUM_TYPE_DEFINITION)) {
|
|
558
|
-
serializedTypes.unshift(`import {
|
|
558
|
+
serializedTypes.unshift(`import {
|
|
559
|
+
${usedReferences.filter((r) => r.kind === Kind.ENUM_TYPE_DEFINITION).map((r) => r.name.value).join(",\n ")},
|
|
560
|
+
} from "${enums.slice(7)}";`);
|
|
559
561
|
}
|
|
560
562
|
return serializedTypes.join("\n\n") + "\n";
|
|
561
563
|
};
|
package/esm/init.js
CHANGED
|
@@ -72,18 +72,62 @@ export const init = (schema, queries, mutations, subscriptions, types, inputs, s
|
|
|
72
72
|
return `${kind}${operation[0].toUpperCase()}${operation.slice(1)}`;
|
|
73
73
|
};
|
|
74
74
|
const addOperationTransforms = (operation, obj) => {
|
|
75
|
-
Object.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
75
|
+
Object.defineProperties(obj, {
|
|
76
|
+
patch: {
|
|
77
|
+
value: (...patches) => {
|
|
78
|
+
const prev = typeof obj === "function" ? obj() : obj;
|
|
79
|
+
const builder = build[operation];
|
|
80
|
+
const { result, request, error, ...rest } = prev;
|
|
81
|
+
return builder({
|
|
82
|
+
data: result.data,
|
|
83
|
+
variables: request.variables,
|
|
84
|
+
error: error,
|
|
85
|
+
errors: result.errors,
|
|
86
|
+
...rest,
|
|
87
|
+
}, ...patches);
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
variables: {
|
|
91
|
+
value: (variables) => {
|
|
92
|
+
const prev = typeof obj === "function" ? obj() : obj;
|
|
93
|
+
const builder = build[operation];
|
|
94
|
+
const { result, request, error, ...rest } = prev;
|
|
95
|
+
const mock = builder({
|
|
96
|
+
data: result.data,
|
|
97
|
+
variables: request.variables,
|
|
98
|
+
error: error,
|
|
99
|
+
errors: result.errors,
|
|
100
|
+
...rest,
|
|
101
|
+
}, {
|
|
102
|
+
variables: typeof variables === "function"
|
|
103
|
+
? variables(result.data, request.variables)
|
|
104
|
+
: variables,
|
|
105
|
+
});
|
|
106
|
+
Error.captureStackTrace(mock, obj.variables);
|
|
107
|
+
mock.stack = mock.stack?.slice(6);
|
|
108
|
+
return mock;
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
data: {
|
|
112
|
+
value: (data) => {
|
|
113
|
+
const prev = typeof obj === "function" ? obj() : obj;
|
|
114
|
+
const builder = build[operation];
|
|
115
|
+
const { result, request, error, ...rest } = prev;
|
|
116
|
+
const mock = builder({
|
|
117
|
+
data: result.data,
|
|
118
|
+
variables: request.variables,
|
|
119
|
+
error: error,
|
|
120
|
+
errors: result.errors,
|
|
121
|
+
...rest,
|
|
122
|
+
}, {
|
|
123
|
+
data: typeof data === "function"
|
|
124
|
+
? data(request.variables, result.data)
|
|
125
|
+
: data,
|
|
126
|
+
});
|
|
127
|
+
Error.captureStackTrace(mock, obj.data);
|
|
128
|
+
mock.stack = mock.stack?.slice(6);
|
|
129
|
+
return mock;
|
|
130
|
+
},
|
|
87
131
|
},
|
|
88
132
|
});
|
|
89
133
|
Object.defineProperties(obj, Object.fromEntries(Object.entries(transforms[operation] ?? {}).map(([name, fn]) => [name, {
|
|
@@ -100,26 +144,34 @@ export const init = (schema, queries, mutations, subscriptions, types, inputs, s
|
|
|
100
144
|
? operationFn(prevInput, ...args)
|
|
101
145
|
: operationFn);
|
|
102
146
|
const builder = build[operation];
|
|
103
|
-
|
|
147
|
+
const mock = builder(prevInput, patch);
|
|
148
|
+
Error.captureStackTrace(mock, obj[name]);
|
|
149
|
+
mock.stack = mock.stack?.slice(6);
|
|
150
|
+
return mock;
|
|
104
151
|
},
|
|
105
152
|
}])));
|
|
106
153
|
return obj;
|
|
107
154
|
};
|
|
108
|
-
const operationBuilder = (name, path) =>
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
155
|
+
const operationBuilder = (name, path) => {
|
|
156
|
+
const builder = (...patches) => {
|
|
157
|
+
const query = getOperationContent(path, name);
|
|
158
|
+
if (transforms[name] && "default" in transforms[name]) {
|
|
159
|
+
patches = [transforms[name].default, ...patches];
|
|
160
|
+
}
|
|
161
|
+
const { request: { query: parsedQuery, ...request }, ...raw } = operation(doc.definitions, scalars, query, ...patches);
|
|
162
|
+
const mock = toObject({
|
|
163
|
+
request: { ...request },
|
|
164
|
+
...raw,
|
|
165
|
+
});
|
|
166
|
+
mock.request.query = parsedQuery;
|
|
167
|
+
Error.captureStackTrace(mock, builder);
|
|
168
|
+
mock.stack = mock.stack?.slice(6);
|
|
169
|
+
return addOperationTransforms(name, options?.finalizeOperation
|
|
170
|
+
? options.finalizeOperation(mock)
|
|
171
|
+
: mock);
|
|
172
|
+
};
|
|
173
|
+
return addOperationTransforms(name, builder);
|
|
174
|
+
};
|
|
123
175
|
{
|
|
124
176
|
const nonQueryNames = [
|
|
125
177
|
...Object.keys(mutations),
|
package/esm/jest.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import * as dntShim from "./_dnt.shims.js";
|
|
2
|
+
import "./deps/esm.sh/@types/jest@29.5.3/index";
|
|
3
|
+
import React, { useMemo } from "react";
|
|
4
|
+
import { ApolloLink } from "@apollo/client";
|
|
5
|
+
import { ErrorLink } from "@apollo/client/link/error/index.js";
|
|
6
|
+
import { MockedProvider, MockLink, } from "@apollo/client/testing/index.js";
|
|
7
|
+
import { waitFor } from "@testing-library/dom";
|
|
8
|
+
import { print } from "graphql";
|
|
9
|
+
import { diff as jestDiff } from "jest-diff";
|
|
10
|
+
let currentSpecResult;
|
|
11
|
+
jasmine.getEnv().addReporter({
|
|
12
|
+
specStarted: (result) => currentSpecResult = result,
|
|
13
|
+
});
|
|
14
|
+
const afterTest = [];
|
|
15
|
+
afterEach(async () => {
|
|
16
|
+
for (const hook of afterTest)
|
|
17
|
+
await hook();
|
|
18
|
+
afterTest.splice(0);
|
|
19
|
+
});
|
|
20
|
+
const diff = (a, b) => jestDiff(a, b, {
|
|
21
|
+
omitAnnotationLines: true,
|
|
22
|
+
// aColor: green,
|
|
23
|
+
// bColor: red,
|
|
24
|
+
// changeColor: inverse,
|
|
25
|
+
// commonColor: dim,
|
|
26
|
+
// patchColor: yellow,
|
|
27
|
+
})
|
|
28
|
+
?.replace(/\w+ \{/g, "{") // Remove class names
|
|
29
|
+
.replace(/\w+ \[/g, "["); // Remove array class names
|
|
30
|
+
const getErrorMessage = (operation, mockLink) => {
|
|
31
|
+
const definition = operation.query.definitions[0];
|
|
32
|
+
const operationType = definition.kind === "OperationDefinition"
|
|
33
|
+
? definition.operation
|
|
34
|
+
: "<unknown operation type>";
|
|
35
|
+
const key = JSON.stringify({
|
|
36
|
+
query: print(operation.query),
|
|
37
|
+
});
|
|
38
|
+
// Bypassing private variable
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
const alts = (mockLink
|
|
41
|
+
.mockedResponsesByKey[key] ?? []);
|
|
42
|
+
let errorMessage = `Expected GraphQL ${operationType} ${operation.operationName} to have been mocked`;
|
|
43
|
+
if (alts.length || Object.keys(operation.variables).length > 0) {
|
|
44
|
+
errorMessage += ` with variables ${dntShim.Deno.inspect(operation.variables, { depth: Infinity, colors: true })}`;
|
|
45
|
+
}
|
|
46
|
+
if (alts.length > 1) {
|
|
47
|
+
errorMessage +=
|
|
48
|
+
`, found ${alts.length} similar operations with differing variables:${alts
|
|
49
|
+
.slice(0, 9)
|
|
50
|
+
.map((o, i) => `\n${i + 1}.\n${diff(operation.variables, o.request.variables)}`)
|
|
51
|
+
.join("")}`;
|
|
52
|
+
if (alts.length > 9)
|
|
53
|
+
errorMessage += `\n... and ${alts.length - 9} more`;
|
|
54
|
+
}
|
|
55
|
+
else if (alts.length === 1) {
|
|
56
|
+
errorMessage += `, found similar operation with differing variables:\n${diff(operation.variables, alts[0].request.variables)}`;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
message: errorMessage,
|
|
60
|
+
stack: alts[0] && "stack" in alts[0] && typeof alts[0].stack === "string"
|
|
61
|
+
? alts[0]?.stack
|
|
62
|
+
: undefined,
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
export const MockProvider = ({ mocks, stack, children }) => {
|
|
66
|
+
const observableMocks = useMemo(() => {
|
|
67
|
+
const observableMocks = mocks.map((m) => ({
|
|
68
|
+
...m,
|
|
69
|
+
stack: m.stack,
|
|
70
|
+
result: Object.assign(jest.fn(() => m.result), m.result),
|
|
71
|
+
}));
|
|
72
|
+
afterTest.push(async () => {
|
|
73
|
+
if (currentSpecResult.failedExpectations.length)
|
|
74
|
+
return;
|
|
75
|
+
for (const mock of observableMocks) {
|
|
76
|
+
if ("optional" in mock && mock.optional || mock.error)
|
|
77
|
+
continue;
|
|
78
|
+
await waitFor(() => {
|
|
79
|
+
if (currentSpecResult.failedExpectations.length)
|
|
80
|
+
return;
|
|
81
|
+
if (mock.result.mock.calls.length === 0) {
|
|
82
|
+
const err = new Error(`Expected to have used mock ${mock.request.variables
|
|
83
|
+
? ` with variables ${dntShim.Deno.inspect(mock.request.variables, {
|
|
84
|
+
depth: Infinity,
|
|
85
|
+
colors: true,
|
|
86
|
+
})}`
|
|
87
|
+
: ""}`);
|
|
88
|
+
err.stack = `${err.message}\n${mock.stack ?? stack ?? err.stack?.slice(6)}`;
|
|
89
|
+
throw err;
|
|
90
|
+
}
|
|
91
|
+
}, { onTimeout: (e) => e });
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
return observableMocks;
|
|
95
|
+
}, [mocks]);
|
|
96
|
+
const link = useMemo(() => {
|
|
97
|
+
const mockLink = new MockLink(observableMocks);
|
|
98
|
+
mockLink.showWarnings = false;
|
|
99
|
+
const errorLoggingLink = new ErrorLink(({ networkError, operation }) => {
|
|
100
|
+
if (!networkError?.message.includes("No more mocked responses"))
|
|
101
|
+
return;
|
|
102
|
+
const { message, stack: altStack } = getErrorMessage(operation, mockLink);
|
|
103
|
+
try {
|
|
104
|
+
networkError.message = message;
|
|
105
|
+
const finalStack = altStack ?? stack
|
|
106
|
+
? `${message}\n${altStack ?? stack}`
|
|
107
|
+
: undefined;
|
|
108
|
+
if (finalStack)
|
|
109
|
+
networkError.stack = finalStack;
|
|
110
|
+
fail({ name: "Error", message, stack: finalStack });
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// fail both throws and marks the test as failed in jest; we only need the latter
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
// @ts-ignore It's fine
|
|
117
|
+
return ApolloLink.from([errorLoggingLink, mockLink]);
|
|
118
|
+
}, [observableMocks, stack]);
|
|
119
|
+
return React.createElement(MockedProvider, { link: link }, children);
|
|
120
|
+
};
|
package/esm/plugin.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
module.exports = {
|
|
3
3
|
async plugin(schema, documents, config) {
|
|
4
4
|
const { codegen } = await import("./codegen.js");
|
|
5
|
-
|
|
5
|
+
const { formatCode } = await import("./util.js");
|
|
6
|
+
return (config.banner ?? "") + await formatCode(codegen(schema, documents.map((d) => ({ path: d.location, content: d.rawSDL })), { namingConvention: "change-case-all#pascalCase", ...config }));
|
|
6
7
|
},
|
|
7
8
|
};
|
package/esm/util.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
2
|
export const raise = (error) => {
|
|
3
3
|
if (typeof error === "string") {
|
|
4
4
|
const err = new Error(error);
|
|
@@ -10,14 +10,28 @@ export const raise = (error) => {
|
|
|
10
10
|
export const formatCode = async (input) => {
|
|
11
11
|
try {
|
|
12
12
|
return await new Promise((resolve, reject) => {
|
|
13
|
-
const process =
|
|
14
|
-
|
|
15
|
-
return reject(error);
|
|
16
|
-
if (stderr)
|
|
17
|
-
return reject(new Error(stderr));
|
|
18
|
-
resolve(stdout);
|
|
13
|
+
const process = spawn("deno", ["fmt", "-"], {
|
|
14
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
19
15
|
});
|
|
20
|
-
|
|
16
|
+
let output = "";
|
|
17
|
+
let errorOutput = "";
|
|
18
|
+
process.stdout.on("data", (data) => {
|
|
19
|
+
output += data.toString();
|
|
20
|
+
});
|
|
21
|
+
process.stderr.on("data", (data) => {
|
|
22
|
+
errorOutput += data.toString();
|
|
23
|
+
});
|
|
24
|
+
process.on("close", (code) => {
|
|
25
|
+
if (code !== 0 || errorOutput) {
|
|
26
|
+
reject(new Error(errorOutput || `Process exited with code ${code}`));
|
|
27
|
+
}
|
|
28
|
+
else
|
|
29
|
+
resolve(output);
|
|
30
|
+
});
|
|
31
|
+
process.on("error", (error) => {
|
|
32
|
+
reject(error);
|
|
33
|
+
});
|
|
34
|
+
process.stdin.write(input);
|
|
21
35
|
process.stdin?.end();
|
|
22
36
|
});
|
|
23
37
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphql-data-generator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-alpha.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
|
-
"url": "git+https://github.com/
|
|
6
|
+
"url": "git+https://github.com/voces/graphql-data-generator.git"
|
|
7
7
|
},
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"bugs": {
|
|
@@ -24,6 +24,12 @@
|
|
|
24
24
|
"require": "./esm/plugin.cjs",
|
|
25
25
|
"types": "./types/plugin.cjs",
|
|
26
26
|
"default": "./esm/plugin.cjs"
|
|
27
|
+
},
|
|
28
|
+
"./jest": {
|
|
29
|
+
"import": "./esm/jest.js",
|
|
30
|
+
"require": "./esm/jest.js",
|
|
31
|
+
"types": "./types/jest.d.ts",
|
|
32
|
+
"default": "./esm/jest.js"
|
|
27
33
|
}
|
|
28
34
|
},
|
|
29
35
|
"bin": {
|
|
@@ -31,7 +37,9 @@
|
|
|
31
37
|
},
|
|
32
38
|
"dependencies": {
|
|
33
39
|
"@graphql-codegen/visitor-plugin-common": "*",
|
|
40
|
+
"@testing-library/dom": "*",
|
|
34
41
|
"fast-glob": "*",
|
|
42
|
+
"jest-diff": "*",
|
|
35
43
|
"@deno/shim-deno": "~0.18.0"
|
|
36
44
|
},
|
|
37
45
|
"devDependencies": {
|
|
@@ -40,6 +48,20 @@
|
|
|
40
48
|
"_generatedBy": "dnt@dev",
|
|
41
49
|
"peerDependencies": {
|
|
42
50
|
"graphql": "*",
|
|
43
|
-
"@graphql-tools/graphql-tag-pluck": "*"
|
|
51
|
+
"@graphql-tools/graphql-tag-pluck": "*",
|
|
52
|
+
"react": "*",
|
|
53
|
+
"@types/react": "*",
|
|
54
|
+
"@apollo/client": "*"
|
|
55
|
+
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"react": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"@types/react": {
|
|
61
|
+
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"@apollo/client": {
|
|
64
|
+
"optional": true
|
|
65
|
+
}
|
|
44
66
|
}
|
|
45
67
|
}
|
package/types/init.d.ts
CHANGED
|
@@ -12,5 +12,5 @@ export declare const init: <Query extends Record<string, {
|
|
|
12
12
|
}>, Types extends Record<string, Record<string, unknown>>, Inputs extends Record<string, Record<string, unknown>>, Extra = object>(schema: string, queries: { [operation in keyof Query]: string; }, mutations: { [operation in keyof Mutation]: string; }, subscriptions: { [operation in keyof Subscription]: string; }, types: readonly (keyof Types & string)[], inputs: readonly (keyof Inputs & string)[], scalars: {
|
|
13
13
|
[name: string]: ((typeName: string, fieldName: string) => unknown) | string | number | boolean | null;
|
|
14
14
|
}, options?: {
|
|
15
|
-
finalizeOperation
|
|
15
|
+
finalizeOperation?: <T extends OperationMock & Partial<Extra>>(operation: T) => T;
|
|
16
16
|
}) => <Transforms extends MapObjectsToTransforms<Types & Inputs> & MapOperationsToTransforms<Query, Mutation, Subscription, Types, Inputs>>(fn: (b: Build<Query, Mutation, Subscription, Types, Inputs, ContravariantEmpty, Extra>) => Transforms) => Build<Query, Mutation, Subscription, Types, Inputs, Transforms, Extra>;
|
package/types/jest.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "./deps/esm.sh/@types/jest@29.5.3/index";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import type { OperationMock } from "./types.js";
|
|
4
|
+
export declare const MockProvider: ({ mocks, stack, children }: {
|
|
5
|
+
mocks: OperationMock[];
|
|
6
|
+
stack?: string;
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
}) => React.JSX.Element;
|
package/types/types.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export type OperationMock<Data extends Record<string, unknown> = Record<string,
|
|
|
27
27
|
errors?: GraphQLError[];
|
|
28
28
|
};
|
|
29
29
|
error?: Error;
|
|
30
|
+
stack?: string;
|
|
30
31
|
};
|
|
31
32
|
export type SimpleOperationMock<Data extends Record<string, unknown> = Record<string, unknown>, Variables = Record<string, unknown> | never> = {
|
|
32
33
|
data: Data;
|