@kubb/plugin-cypress 5.0.0-beta.3 → 5.0.0-beta.31
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 +25 -5
- package/dist/index.cjs +164 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +59 -31
- package/dist/index.js +165 -58
- package/dist/index.js.map +1 -1
- package/extension.yaml +906 -0
- package/package.json +9 -14
- package/src/components/Request.tsx +9 -10
- package/src/generators/cypressGenerator.tsx +19 -23
- package/src/plugin.ts +19 -24
- package/src/resolvers/resolverCypress.ts +13 -6
- package/src/types.ts +29 -17
package/dist/index.d.ts
CHANGED
|
@@ -16,26 +16,32 @@ type ResolverCypress = Resolver & {
|
|
|
16
16
|
* `resolver.resolveName('show pet by id') // -> 'showPetById'`
|
|
17
17
|
*/
|
|
18
18
|
resolveName(this: ResolverCypress, name: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Resolves the output file name for a Cypress request module.
|
|
21
|
+
*/
|
|
22
|
+
resolvePathName(this: ResolverCypress, name: string, type?: 'file' | 'function' | 'type' | 'const'): string;
|
|
19
23
|
};
|
|
20
24
|
/**
|
|
21
25
|
* Parameter handling mode that determines how path params and query/body params are arranged in function signatures.
|
|
22
26
|
*/
|
|
23
27
|
type ParamsTypeOptions = {
|
|
24
28
|
/**
|
|
25
|
-
*
|
|
29
|
+
* Every operation parameter is wrapped in a single destructured object argument.
|
|
26
30
|
*/
|
|
27
31
|
paramsType: 'object';
|
|
28
32
|
/**
|
|
29
|
-
*
|
|
33
|
+
* `pathParamsType` has no effect when `paramsType` is `'object'`.
|
|
30
34
|
*/
|
|
31
35
|
pathParamsType?: never;
|
|
32
36
|
} | {
|
|
33
37
|
/**
|
|
34
|
-
* Each parameter group emitted as a separate function argument.
|
|
38
|
+
* Each parameter group is emitted as a separate positional function argument.
|
|
35
39
|
*/
|
|
36
40
|
paramsType?: 'inline';
|
|
37
41
|
/**
|
|
38
|
-
* How path parameters are arranged
|
|
42
|
+
* How URL path parameters are arranged inside the inline argument list.
|
|
43
|
+
* - `'object'` groups them into one destructured object.
|
|
44
|
+
* - `'inline'` emits each path param as its own argument.
|
|
39
45
|
*
|
|
40
46
|
* @default 'inline'
|
|
41
47
|
*/
|
|
@@ -43,50 +49,56 @@ type ParamsTypeOptions = {
|
|
|
43
49
|
};
|
|
44
50
|
type Options = {
|
|
45
51
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
52
|
+
* Where the generated Cypress helpers are written and how they are exported.
|
|
53
|
+
*
|
|
54
|
+
* @default { path: 'cypress', barrel: { type: 'named' } }
|
|
48
55
|
*/
|
|
49
56
|
output?: Output;
|
|
50
57
|
/**
|
|
51
|
-
*
|
|
58
|
+
* Shape of the value returned from each helper.
|
|
59
|
+
* - `'data'` — only the response body.
|
|
60
|
+
* - `'full'` — the full Cypress response object (headers, status, body).
|
|
52
61
|
*
|
|
53
62
|
* @default 'data'
|
|
54
63
|
*/
|
|
55
64
|
dataReturnType?: 'data' | 'full';
|
|
56
65
|
/**
|
|
57
|
-
*
|
|
66
|
+
* Rename parameter properties in the generated helpers (path, query, headers).
|
|
67
|
+
*
|
|
68
|
+
* @note Must match the value of `paramsCasing` on `@kubb/plugin-ts`.
|
|
58
69
|
*/
|
|
59
70
|
paramsCasing?: 'camelcase';
|
|
60
71
|
/**
|
|
61
|
-
* Base URL prepended to every
|
|
72
|
+
* Base URL prepended to every request URL. When omitted, falls back to the
|
|
73
|
+
* adapter's server URL (typically `servers[0].url`).
|
|
62
74
|
*/
|
|
63
75
|
baseURL?: string;
|
|
64
76
|
/**
|
|
65
|
-
*
|
|
77
|
+
* Split generated files into subfolders based on the operation's tag.
|
|
66
78
|
*/
|
|
67
79
|
group?: Group;
|
|
68
80
|
/**
|
|
69
|
-
*
|
|
81
|
+
* Skip operations matching at least one entry in the list.
|
|
70
82
|
*/
|
|
71
83
|
exclude?: Array<Exclude>;
|
|
72
84
|
/**
|
|
73
|
-
*
|
|
85
|
+
* Restrict generation to operations matching at least one entry in the list.
|
|
74
86
|
*/
|
|
75
87
|
include?: Array<Include>;
|
|
76
88
|
/**
|
|
77
|
-
*
|
|
89
|
+
* Apply a different options object to operations matching a pattern.
|
|
78
90
|
*/
|
|
79
91
|
override?: Array<Override<ResolvedOptions>>;
|
|
80
92
|
/**
|
|
81
|
-
* Override
|
|
93
|
+
* Override how helper names and file paths are built.
|
|
82
94
|
*/
|
|
83
95
|
resolver?: Partial<ResolverCypress> & ThisType<ResolverCypress>;
|
|
84
96
|
/**
|
|
85
|
-
* AST visitor to
|
|
97
|
+
* AST visitor applied to each operation node before printing.
|
|
86
98
|
*/
|
|
87
99
|
transformer?: ast.Visitor;
|
|
88
100
|
/**
|
|
89
|
-
*
|
|
101
|
+
* Custom generators that run alongside the built-in Cypress generators.
|
|
90
102
|
*/
|
|
91
103
|
generators?: Array<Generator<PluginCypress>>;
|
|
92
104
|
} & ParamsTypeOptions;
|
|
@@ -95,7 +107,7 @@ type ResolvedOptions = {
|
|
|
95
107
|
exclude: Array<Exclude>;
|
|
96
108
|
include: Array<Include> | undefined;
|
|
97
109
|
override: Array<Override<ResolvedOptions>>;
|
|
98
|
-
group: Group |
|
|
110
|
+
group: Group | null;
|
|
99
111
|
baseURL: Options['baseURL'] | undefined;
|
|
100
112
|
dataReturnType: NonNullable<Options['dataReturnType']>;
|
|
101
113
|
pathParamsType: NonNullable<NonNullable<Options['pathParamsType']>>;
|
|
@@ -126,7 +138,7 @@ type Props = {
|
|
|
126
138
|
* TypeScript resolver for resolving param/data/response type names
|
|
127
139
|
*/
|
|
128
140
|
resolver: ResolverTs;
|
|
129
|
-
baseURL: string | undefined;
|
|
141
|
+
baseURL: string | null | undefined;
|
|
130
142
|
dataReturnType: PluginCypress['resolvedOptions']['dataReturnType'];
|
|
131
143
|
paramsCasing: PluginCypress['resolvedOptions']['paramsCasing'];
|
|
132
144
|
paramsType: PluginCypress['resolvedOptions']['paramsType'];
|
|
@@ -144,27 +156,39 @@ declare function Request({
|
|
|
144
156
|
}: Props): KubbReactNode;
|
|
145
157
|
//#endregion
|
|
146
158
|
//#region src/generators/cypressGenerator.d.ts
|
|
159
|
+
/**
|
|
160
|
+
* Built-in generator for `@kubb/plugin-cypress`. Emits one typed
|
|
161
|
+
* `cy.request()` wrapper per OpenAPI operation, ready to call inside Cypress
|
|
162
|
+
* test specs and custom commands.
|
|
163
|
+
*/
|
|
147
164
|
declare const cypressGenerator: _$_kubb_core0.Generator<PluginCypress, unknown>;
|
|
148
165
|
//#endregion
|
|
149
166
|
//#region src/plugin.d.ts
|
|
150
167
|
/**
|
|
151
|
-
* Canonical plugin name for `@kubb/plugin-cypress
|
|
152
|
-
*
|
|
168
|
+
* Canonical plugin name for `@kubb/plugin-cypress`. Used for driver lookups and
|
|
169
|
+
* cross-plugin dependency references.
|
|
153
170
|
*/
|
|
154
171
|
declare const pluginCypressName = "plugin-cypress";
|
|
155
172
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* Walks operations, delegates rendering to the active generators,
|
|
160
|
-
* and writes barrel files based on `output.barrelType`.
|
|
173
|
+
* Generates one typed `cy.request()` wrapper per OpenAPI operation. Each helper
|
|
174
|
+
* has typed path params, body, query, and a typed response, so failing API
|
|
175
|
+
* calls in Cypress show up at compile time instead of inside the test runner.
|
|
161
176
|
*
|
|
162
177
|
* @example
|
|
163
178
|
* ```ts
|
|
164
|
-
* import
|
|
179
|
+
* import { defineConfig } from 'kubb'
|
|
180
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
181
|
+
* import { pluginCypress } from '@kubb/plugin-cypress'
|
|
165
182
|
*
|
|
166
183
|
* export default defineConfig({
|
|
167
|
-
*
|
|
184
|
+
* input: { path: './petStore.yaml' },
|
|
185
|
+
* output: { path: './src/gen' },
|
|
186
|
+
* plugins: [
|
|
187
|
+
* pluginTs(),
|
|
188
|
+
* pluginCypress({
|
|
189
|
+
* output: { path: './cypress' },
|
|
190
|
+
* }),
|
|
191
|
+
* ],
|
|
168
192
|
* })
|
|
169
193
|
* ```
|
|
170
194
|
*/
|
|
@@ -172,12 +196,16 @@ declare const pluginCypress: (options?: Options | undefined) => _$_kubb_core0.Pl
|
|
|
172
196
|
//#endregion
|
|
173
197
|
//#region src/resolvers/resolverCypress.d.ts
|
|
174
198
|
/**
|
|
175
|
-
*
|
|
199
|
+
* Default resolver used by `@kubb/plugin-cypress`. Decides the names and file
|
|
200
|
+
* paths for every generated `cy.request()` wrapper. Functions and files use
|
|
201
|
+
* camelCase, matching the convention from `@kubb/plugin-client`.
|
|
176
202
|
*
|
|
177
|
-
*
|
|
203
|
+
* @example Resolve a helper name
|
|
204
|
+
* ```ts
|
|
205
|
+
* import { resolverCypress } from '@kubb/plugin-cypress'
|
|
178
206
|
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
207
|
+
* resolverCypress.default('list pets', 'function') // 'listPets'
|
|
208
|
+
* ```
|
|
181
209
|
*/
|
|
182
210
|
declare const resolverCypress: ResolverCypress;
|
|
183
211
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "./chunk--u3MIqq1.js";
|
|
2
2
|
import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
|
|
3
3
|
import { functionPrinter, pluginTsName } from "@kubb/plugin-ts";
|
|
4
|
-
import { File, Function,
|
|
4
|
+
import { File, Function, jsxRendererSync } from "@kubb/renderer-jsx";
|
|
5
5
|
import { jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
|
|
6
6
|
//#region ../../internals/utils/src/casing.ts
|
|
7
7
|
/**
|
|
@@ -214,16 +214,16 @@ var URLPath = class {
|
|
|
214
214
|
get object() {
|
|
215
215
|
return this.toObject();
|
|
216
216
|
}
|
|
217
|
-
/** Returns a map of path parameter names, or `
|
|
217
|
+
/** Returns a map of path parameter names, or `null` when the path has no parameters.
|
|
218
218
|
*
|
|
219
219
|
* @example
|
|
220
220
|
* ```ts
|
|
221
221
|
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
222
|
-
* new URLPath('/pet').params //
|
|
222
|
+
* new URLPath('/pet').params // null
|
|
223
223
|
* ```
|
|
224
224
|
*/
|
|
225
225
|
get params() {
|
|
226
|
-
return this.
|
|
226
|
+
return this.toParamsObject();
|
|
227
227
|
}
|
|
228
228
|
#transformParam(raw) {
|
|
229
229
|
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
@@ -241,7 +241,7 @@ var URLPath = class {
|
|
|
241
241
|
toObject({ type = "path", replacer, stringify } = {}) {
|
|
242
242
|
const object = {
|
|
243
243
|
url: type === "path" ? this.toURLPath() : this.toTemplateString({ replacer }),
|
|
244
|
-
params: this.
|
|
244
|
+
params: this.toParamsObject()
|
|
245
245
|
};
|
|
246
246
|
if (stringify) {
|
|
247
247
|
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
@@ -257,12 +257,13 @@ var URLPath = class {
|
|
|
257
257
|
* @example
|
|
258
258
|
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
259
259
|
*/
|
|
260
|
-
toTemplateString({ prefix
|
|
261
|
-
|
|
260
|
+
toTemplateString({ prefix, replacer } = {}) {
|
|
261
|
+
const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
262
262
|
if (i % 2 === 0) return part;
|
|
263
263
|
const param = this.#transformParam(part);
|
|
264
264
|
return `\${${replacer ? replacer(param) : param}}`;
|
|
265
|
-
}).join("")
|
|
265
|
+
}).join("");
|
|
266
|
+
return `\`${prefix ?? ""}${result}\``;
|
|
266
267
|
}
|
|
267
268
|
/**
|
|
268
269
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
@@ -271,17 +272,17 @@ var URLPath = class {
|
|
|
271
272
|
*
|
|
272
273
|
* @example
|
|
273
274
|
* ```ts
|
|
274
|
-
* new URLPath('/pet/{petId}/tag/{tagId}').
|
|
275
|
+
* new URLPath('/pet/{petId}/tag/{tagId}').toParamsObject()
|
|
275
276
|
* // { petId: 'petId', tagId: 'tagId' }
|
|
276
277
|
* ```
|
|
277
278
|
*/
|
|
278
|
-
|
|
279
|
+
toParamsObject(replacer) {
|
|
279
280
|
const params = {};
|
|
280
281
|
this.#eachParam((_raw, param) => {
|
|
281
282
|
const key = replacer ? replacer(param) : param;
|
|
282
283
|
params[key] = key;
|
|
283
284
|
});
|
|
284
|
-
return Object.keys(params).length > 0 ? params :
|
|
285
|
+
return Object.keys(params).length > 0 ? params : null;
|
|
285
286
|
}
|
|
286
287
|
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
287
288
|
*
|
|
@@ -295,9 +296,100 @@ var URLPath = class {
|
|
|
295
296
|
}
|
|
296
297
|
};
|
|
297
298
|
//#endregion
|
|
299
|
+
//#region ../../internals/shared/src/operation.ts
|
|
300
|
+
function getOperationParameters(node, options = {}) {
|
|
301
|
+
const params = ast.caseParams(node.parameters, options.paramsCasing);
|
|
302
|
+
return {
|
|
303
|
+
path: params.filter((param) => param.in === "path"),
|
|
304
|
+
query: params.filter((param) => param.in === "query"),
|
|
305
|
+
header: params.filter((param) => param.in === "header"),
|
|
306
|
+
cookie: params.filter((param) => param.in === "cookie")
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function getStatusCodeNumber(statusCode) {
|
|
310
|
+
const code = Number(statusCode);
|
|
311
|
+
return Number.isNaN(code) ? null : code;
|
|
312
|
+
}
|
|
313
|
+
function isErrorStatusCode(statusCode) {
|
|
314
|
+
const code = getStatusCodeNumber(statusCode);
|
|
315
|
+
return code !== null && code >= 400;
|
|
316
|
+
}
|
|
317
|
+
function resolveErrorNames(node, resolver) {
|
|
318
|
+
return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
|
|
319
|
+
}
|
|
320
|
+
function resolveStatusCodeNames(node, resolver) {
|
|
321
|
+
return node.responses.map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
|
|
322
|
+
}
|
|
323
|
+
const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
|
|
324
|
+
function resolveOperationTypeNames(node, resolver, options = {}) {
|
|
325
|
+
const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${(options.exclude ?? []).join(",")}`;
|
|
326
|
+
let byResolver = typeNamesByResolver.get(resolver);
|
|
327
|
+
if (byResolver) {
|
|
328
|
+
const cached = byResolver.get(cacheKey);
|
|
329
|
+
if (cached) return cached;
|
|
330
|
+
} else {
|
|
331
|
+
byResolver = /* @__PURE__ */ new Map();
|
|
332
|
+
typeNamesByResolver.set(resolver, byResolver);
|
|
333
|
+
}
|
|
334
|
+
const { path, query, header } = getOperationParameters(node, { paramsCasing: options.paramsCasing });
|
|
335
|
+
const responseStatusNames = options.responseStatusNames === "error" ? resolveErrorNames(node, resolver) : options.responseStatusNames === false ? [] : resolveStatusCodeNames(node, resolver);
|
|
336
|
+
const exclude = new Set(options.exclude ?? []);
|
|
337
|
+
const paramNames = [
|
|
338
|
+
...path.map((param) => resolver.resolvePathParamsName(node, param)),
|
|
339
|
+
...query.map((param) => resolver.resolveQueryParamsName(node, param)),
|
|
340
|
+
...header.map((param) => resolver.resolveHeaderParamsName(node, param))
|
|
341
|
+
];
|
|
342
|
+
const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null, resolver.resolveResponseName(node)];
|
|
343
|
+
const result = (options.order === "body-response-first" ? [
|
|
344
|
+
...bodyAndResponseNames,
|
|
345
|
+
...paramNames,
|
|
346
|
+
...responseStatusNames
|
|
347
|
+
] : [
|
|
348
|
+
...paramNames,
|
|
349
|
+
...bodyAndResponseNames,
|
|
350
|
+
...responseStatusNames
|
|
351
|
+
]).filter((name) => Boolean(name) && !exclude.has(name));
|
|
352
|
+
byResolver.set(cacheKey, result);
|
|
353
|
+
return result;
|
|
354
|
+
}
|
|
355
|
+
//#endregion
|
|
356
|
+
//#region ../../internals/shared/src/group.ts
|
|
357
|
+
/**
|
|
358
|
+
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
359
|
+
* shared default naming so every plugin groups output consistently:
|
|
360
|
+
*
|
|
361
|
+
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
362
|
+
* - other groups use `${camelCase(group)}${suffix}` (e.g. `petController`).
|
|
363
|
+
*
|
|
364
|
+
* Returns `null` when grouping is disabled, matching the per-plugin convention.
|
|
365
|
+
*
|
|
366
|
+
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
367
|
+
* @param options.suffix - Appended to non-`path` group names, e.g. `'Controller'` or `'Requests'`.
|
|
368
|
+
* @param options.honorName - When `true`, a user-provided `group.name` overrides the default namer.
|
|
369
|
+
*
|
|
370
|
+
* @example
|
|
371
|
+
* ```ts
|
|
372
|
+
* createGroupConfig(group, { suffix: 'Controller' }) // plugin-ts, plugin-zod
|
|
373
|
+
* createGroupConfig(group, { suffix: 'Controller', honorName: true }) // plugin-faker, plugin-client, …
|
|
374
|
+
* createGroupConfig(group, { suffix: 'Requests', honorName: true }) // plugin-cypress, plugin-mcp
|
|
375
|
+
* ```
|
|
376
|
+
*/
|
|
377
|
+
function createGroupConfig(group, options) {
|
|
378
|
+
if (!group) return null;
|
|
379
|
+
const defaultName = (ctx) => {
|
|
380
|
+
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
381
|
+
return `${camelCase(ctx.group)}${options.suffix}`;
|
|
382
|
+
};
|
|
383
|
+
return {
|
|
384
|
+
...group,
|
|
385
|
+
name: options.honorName && group.name ? group.name : defaultName
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
//#endregion
|
|
298
389
|
//#region src/components/Request.tsx
|
|
299
390
|
const declarationPrinter = functionPrinter({ mode: "declaration" });
|
|
300
391
|
function Request({ baseURL = "", name, dataReturnType, resolver, node, paramsType, pathParamsType, paramsCasing }) {
|
|
392
|
+
if (!ast.isHttpOperationNode(node)) return null;
|
|
301
393
|
const paramsNode = ast.createOperationParams(node, {
|
|
302
394
|
paramsType,
|
|
303
395
|
pathParamsType,
|
|
@@ -315,24 +407,24 @@ function Request({ baseURL = "", name, dataReturnType, resolver, node, paramsTyp
|
|
|
315
407
|
const paramsSignature = declarationPrinter.print(paramsNode) ?? "";
|
|
316
408
|
const responseType = resolver.resolveResponseName(node);
|
|
317
409
|
const returnType = dataReturnType === "data" ? `Cypress.Chainable<${responseType}>` : `Cypress.Chainable<Cypress.Response<${responseType}>>`;
|
|
318
|
-
const casedPathParams =
|
|
410
|
+
const casedPathParams = getOperationParameters(node, { paramsCasing }).path;
|
|
319
411
|
const pathParamNameMap = new Map(casedPathParams.map((p) => [camelCase(p.name), p.name]));
|
|
320
412
|
const urlTemplate = new URLPath(node.path, { casing: paramsCasing }).toTemplateString({
|
|
321
413
|
prefix: baseURL,
|
|
322
414
|
replacer: (param) => pathParamNameMap.get(camelCase(param)) ?? param
|
|
323
415
|
});
|
|
324
416
|
const requestOptions = [`method: '${node.method}'`, `url: ${urlTemplate}`];
|
|
325
|
-
const queryParams = node
|
|
417
|
+
const queryParams = getOperationParameters(node).query;
|
|
326
418
|
if (queryParams.length > 0) {
|
|
327
|
-
const casedQueryParams =
|
|
419
|
+
const casedQueryParams = getOperationParameters(node, { paramsCasing }).query;
|
|
328
420
|
if (casedQueryParams.some((p, i) => p.name !== queryParams[i].name)) {
|
|
329
421
|
const pairs = queryParams.map((orig, i) => `${orig.name}: params.${casedQueryParams[i].name}`).join(", ");
|
|
330
422
|
requestOptions.push(`qs: params ? { ${pairs} } : undefined`);
|
|
331
423
|
} else requestOptions.push("qs: params");
|
|
332
424
|
}
|
|
333
|
-
const headerParams = node
|
|
425
|
+
const headerParams = getOperationParameters(node).header;
|
|
334
426
|
if (headerParams.length > 0) {
|
|
335
|
-
const casedHeaderParams =
|
|
427
|
+
const casedHeaderParams = getOperationParameters(node, { paramsCasing }).header;
|
|
336
428
|
if (casedHeaderParams.some((p, i) => p.name !== headerParams[i].name)) {
|
|
337
429
|
const pairs = headerParams.map((orig, i) => `'${orig.name}': headers.${casedHeaderParams[i].name}`).join(", ");
|
|
338
430
|
requestOptions.push(`headers: headers ? { ${pairs} } : undefined`);
|
|
@@ -359,26 +451,22 @@ function Request({ baseURL = "", name, dataReturnType, resolver, node, paramsTyp
|
|
|
359
451
|
}
|
|
360
452
|
//#endregion
|
|
361
453
|
//#region src/generators/cypressGenerator.tsx
|
|
454
|
+
/**
|
|
455
|
+
* Built-in generator for `@kubb/plugin-cypress`. Emits one typed
|
|
456
|
+
* `cy.request()` wrapper per OpenAPI operation, ready to call inside Cypress
|
|
457
|
+
* test specs and custom commands.
|
|
458
|
+
*/
|
|
362
459
|
const cypressGenerator = defineGenerator({
|
|
363
460
|
name: "cypress",
|
|
364
|
-
renderer:
|
|
461
|
+
renderer: jsxRendererSync,
|
|
365
462
|
operation(node, ctx) {
|
|
366
|
-
|
|
463
|
+
if (!ast.isHttpOperationNode(node)) return null;
|
|
464
|
+
const { config, resolver, driver, root } = ctx;
|
|
367
465
|
const { output, baseURL, dataReturnType, paramsCasing, paramsType, pathParamsType, group } = ctx.options;
|
|
368
466
|
const pluginTs = driver.getPlugin(pluginTsName);
|
|
369
467
|
if (!pluginTs) return null;
|
|
370
468
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
371
|
-
const
|
|
372
|
-
const pathParams = casedParams.filter((p) => p.in === "path");
|
|
373
|
-
const queryParams = casedParams.filter((p) => p.in === "query");
|
|
374
|
-
const headerParams = casedParams.filter((p) => p.in === "header");
|
|
375
|
-
const importedTypeNames = [
|
|
376
|
-
...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
377
|
-
...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
378
|
-
...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
379
|
-
node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0,
|
|
380
|
-
tsResolver.resolveResponseName(node)
|
|
381
|
-
].filter(Boolean);
|
|
469
|
+
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing });
|
|
382
470
|
const meta = {
|
|
383
471
|
name: resolver.resolveName(node.operationId),
|
|
384
472
|
file: resolver.resolveFile({
|
|
@@ -389,7 +477,7 @@ const cypressGenerator = defineGenerator({
|
|
|
389
477
|
}, {
|
|
390
478
|
root,
|
|
391
479
|
output,
|
|
392
|
-
group
|
|
480
|
+
group: group ?? void 0
|
|
393
481
|
}),
|
|
394
482
|
fileTs: tsResolver.resolveFile({
|
|
395
483
|
name: node.operationId,
|
|
@@ -399,23 +487,31 @@ const cypressGenerator = defineGenerator({
|
|
|
399
487
|
}, {
|
|
400
488
|
root,
|
|
401
489
|
output: pluginTs.options?.output ?? output,
|
|
402
|
-
group: pluginTs.options?.group
|
|
490
|
+
group: pluginTs.options?.group ?? void 0
|
|
403
491
|
})
|
|
404
492
|
};
|
|
405
493
|
return /* @__PURE__ */ jsxs(File, {
|
|
406
494
|
baseName: meta.file.baseName,
|
|
407
495
|
path: meta.file.path,
|
|
408
496
|
meta: meta.file.meta,
|
|
409
|
-
banner: resolver.resolveBanner(
|
|
497
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
410
498
|
output,
|
|
411
|
-
config
|
|
499
|
+
config,
|
|
500
|
+
file: {
|
|
501
|
+
path: meta.file.path,
|
|
502
|
+
baseName: meta.file.baseName
|
|
503
|
+
}
|
|
412
504
|
}),
|
|
413
|
-
footer: resolver.resolveFooter(
|
|
505
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
414
506
|
output,
|
|
415
|
-
config
|
|
507
|
+
config,
|
|
508
|
+
file: {
|
|
509
|
+
path: meta.file.path,
|
|
510
|
+
baseName: meta.file.baseName
|
|
511
|
+
}
|
|
416
512
|
}),
|
|
417
513
|
children: [meta.fileTs && importedTypeNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
|
|
418
|
-
name:
|
|
514
|
+
name: importedTypeNames,
|
|
419
515
|
root: meta.file.path,
|
|
420
516
|
path: meta.fileTs.path,
|
|
421
517
|
isTypeOnly: true
|
|
@@ -435,43 +531,57 @@ const cypressGenerator = defineGenerator({
|
|
|
435
531
|
//#endregion
|
|
436
532
|
//#region src/resolvers/resolverCypress.ts
|
|
437
533
|
/**
|
|
438
|
-
*
|
|
534
|
+
* Default resolver used by `@kubb/plugin-cypress`. Decides the names and file
|
|
535
|
+
* paths for every generated `cy.request()` wrapper. Functions and files use
|
|
536
|
+
* camelCase, matching the convention from `@kubb/plugin-client`.
|
|
439
537
|
*
|
|
440
|
-
*
|
|
538
|
+
* @example Resolve a helper name
|
|
539
|
+
* ```ts
|
|
540
|
+
* import { resolverCypress } from '@kubb/plugin-cypress'
|
|
441
541
|
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
542
|
+
* resolverCypress.default('list pets', 'function') // 'listPets'
|
|
543
|
+
* ```
|
|
444
544
|
*/
|
|
445
|
-
const resolverCypress = defineResolver((
|
|
545
|
+
const resolverCypress = defineResolver(() => ({
|
|
446
546
|
name: "default",
|
|
447
547
|
pluginName: "plugin-cypress",
|
|
448
548
|
default(name, type) {
|
|
449
549
|
return camelCase(name, { isFile: type === "file" });
|
|
450
550
|
},
|
|
451
551
|
resolveName(name) {
|
|
452
|
-
return
|
|
552
|
+
return this.default(name, "function");
|
|
553
|
+
},
|
|
554
|
+
resolvePathName(name, type) {
|
|
555
|
+
return this.default(name, type);
|
|
453
556
|
}
|
|
454
557
|
}));
|
|
455
558
|
//#endregion
|
|
456
559
|
//#region src/plugin.ts
|
|
457
560
|
/**
|
|
458
|
-
* Canonical plugin name for `@kubb/plugin-cypress
|
|
459
|
-
*
|
|
561
|
+
* Canonical plugin name for `@kubb/plugin-cypress`. Used for driver lookups and
|
|
562
|
+
* cross-plugin dependency references.
|
|
460
563
|
*/
|
|
461
564
|
const pluginCypressName = "plugin-cypress";
|
|
462
565
|
/**
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
* Walks operations, delegates rendering to the active generators,
|
|
467
|
-
* and writes barrel files based on `output.barrelType`.
|
|
566
|
+
* Generates one typed `cy.request()` wrapper per OpenAPI operation. Each helper
|
|
567
|
+
* has typed path params, body, query, and a typed response, so failing API
|
|
568
|
+
* calls in Cypress show up at compile time instead of inside the test runner.
|
|
468
569
|
*
|
|
469
570
|
* @example
|
|
470
571
|
* ```ts
|
|
471
|
-
* import
|
|
572
|
+
* import { defineConfig } from 'kubb'
|
|
573
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
574
|
+
* import { pluginCypress } from '@kubb/plugin-cypress'
|
|
472
575
|
*
|
|
473
576
|
* export default defineConfig({
|
|
474
|
-
*
|
|
577
|
+
* input: { path: './petStore.yaml' },
|
|
578
|
+
* output: { path: './src/gen' },
|
|
579
|
+
* plugins: [
|
|
580
|
+
* pluginTs(),
|
|
581
|
+
* pluginCypress({
|
|
582
|
+
* output: { path: './cypress' },
|
|
583
|
+
* }),
|
|
584
|
+
* ],
|
|
475
585
|
* })
|
|
476
586
|
* ```
|
|
477
587
|
*/
|
|
@@ -480,13 +590,10 @@ const pluginCypress = definePlugin((options) => {
|
|
|
480
590
|
path: "cypress",
|
|
481
591
|
barrelType: "named"
|
|
482
592
|
}, group, exclude = [], include, override = [], dataReturnType = "data", baseURL, paramsCasing, paramsType = "inline", pathParamsType = paramsType === "object" ? "object" : options.pathParamsType || "inline", resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
|
|
483
|
-
const groupConfig = group
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
return `${camelCase(ctx.group)}Requests`;
|
|
488
|
-
}
|
|
489
|
-
} : void 0;
|
|
593
|
+
const groupConfig = createGroupConfig(group, {
|
|
594
|
+
suffix: "Requests",
|
|
595
|
+
honorName: true
|
|
596
|
+
});
|
|
490
597
|
return {
|
|
491
598
|
name: pluginCypressName,
|
|
492
599
|
options,
|