@kubb/plugin-cypress 5.0.0-alpha.3 → 5.0.0-alpha.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/dist/index.cjs +465 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +206 -4
- package/dist/index.js +463 -60
- package/dist/index.js.map +1 -1
- package/package.json +6 -30
- package/src/components/Request.tsx +72 -108
- package/src/generators/cypressGenerator.tsx +48 -43
- package/src/index.ts +8 -1
- package/src/plugin.ts +81 -91
- package/src/presets.ts +23 -0
- package/src/resolvers/resolverCypress.ts +26 -0
- package/src/types.ts +105 -38
- package/dist/components-BK_6GU4v.js +0 -257
- package/dist/components-BK_6GU4v.js.map +0 -1
- package/dist/components-Drg_gLu2.cjs +0 -305
- package/dist/components-Drg_gLu2.cjs.map +0 -1
- package/dist/components.cjs +0 -3
- package/dist/components.d.ts +0 -50
- package/dist/components.js +0 -2
- package/dist/generators-BImGfp9Q.js +0 -71
- package/dist/generators-BImGfp9Q.js.map +0 -1
- package/dist/generators-DdeGa-zp.cjs +0 -75
- package/dist/generators-DdeGa-zp.cjs.map +0 -1
- package/dist/generators.cjs +0 -3
- package/dist/generators.d.ts +0 -500
- package/dist/generators.js +0 -2
- package/dist/types-BzXXi6dv.d.ts +0 -85
- package/src/components/index.ts +0 -1
- package/src/generators/index.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/plugin-cypress",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.31",
|
|
4
4
|
"description": "Cypress test generator plugin for Kubb, creating end-to-end tests from OpenAPI specifications for automated API testing.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cypress",
|
|
@@ -34,33 +34,10 @@
|
|
|
34
34
|
"import": "./dist/index.js",
|
|
35
35
|
"require": "./dist/index.cjs"
|
|
36
36
|
},
|
|
37
|
-
"./components": {
|
|
38
|
-
"import": "./dist/components.js",
|
|
39
|
-
"require": "./dist/components.cjs"
|
|
40
|
-
},
|
|
41
|
-
"./generators": {
|
|
42
|
-
"import": "./dist/generators.js",
|
|
43
|
-
"require": "./dist/generators.cjs"
|
|
44
|
-
},
|
|
45
37
|
"./package.json": "./package.json"
|
|
46
38
|
},
|
|
47
39
|
"types": "./dist/index.d.ts",
|
|
48
|
-
"typesVersions": {
|
|
49
|
-
"*": {
|
|
50
|
-
"utils": [
|
|
51
|
-
"./dist/utils.d.ts"
|
|
52
|
-
],
|
|
53
|
-
"hooks": [
|
|
54
|
-
"./dist/hooks.d.ts"
|
|
55
|
-
],
|
|
56
|
-
"components": [
|
|
57
|
-
"./dist/components.d.ts"
|
|
58
|
-
],
|
|
59
|
-
"generators": [
|
|
60
|
-
"./dist/generators.d.ts"
|
|
61
|
-
]
|
|
62
|
-
}
|
|
63
|
-
},
|
|
40
|
+
"typesVersions": {},
|
|
64
41
|
"files": [
|
|
65
42
|
"src",
|
|
66
43
|
"dist",
|
|
@@ -76,11 +53,10 @@
|
|
|
76
53
|
}
|
|
77
54
|
],
|
|
78
55
|
"dependencies": {
|
|
79
|
-
"@kubb/react-fabric": "0.
|
|
80
|
-
"@kubb/
|
|
81
|
-
"@kubb/
|
|
82
|
-
"@kubb/
|
|
83
|
-
"@kubb/plugin-ts": "5.0.0-alpha.3"
|
|
56
|
+
"@kubb/react-fabric": "0.15.1",
|
|
57
|
+
"@kubb/ast": "5.0.0-alpha.31",
|
|
58
|
+
"@kubb/plugin-ts": "5.0.0-alpha.31",
|
|
59
|
+
"@kubb/core": "5.0.0-alpha.31"
|
|
84
60
|
},
|
|
85
61
|
"engines": {
|
|
86
62
|
"node": ">=22"
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { URLPath } from '@internals/utils'
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { camelCase, URLPath } from '@internals/utils'
|
|
2
|
+
import { caseParams, createFunctionParameter, createOperationParams, createTypeNode } from '@kubb/ast'
|
|
3
|
+
import type { OperationNode } from '@kubb/ast/types'
|
|
4
|
+
import type { ResolverTs } from '@kubb/plugin-ts'
|
|
5
|
+
import { functionPrinter } from '@kubb/plugin-ts'
|
|
6
|
+
import { File, Function } from '@kubb/react-fabric'
|
|
6
7
|
import type { FabricReactNode } from '@kubb/react-fabric/types'
|
|
7
8
|
import type { PluginCypress } from '../types.ts'
|
|
8
9
|
|
|
@@ -11,137 +12,100 @@ type Props = {
|
|
|
11
12
|
* Name of the function
|
|
12
13
|
*/
|
|
13
14
|
name: string
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* AST operation node
|
|
17
|
+
*/
|
|
18
|
+
node: OperationNode
|
|
19
|
+
/**
|
|
20
|
+
* TypeScript resolver for resolving param/data/response type names
|
|
21
|
+
*/
|
|
22
|
+
resolver: ResolverTs
|
|
16
23
|
baseURL: string | undefined
|
|
17
24
|
dataReturnType: PluginCypress['resolvedOptions']['dataReturnType']
|
|
18
25
|
paramsCasing: PluginCypress['resolvedOptions']['paramsCasing']
|
|
19
26
|
paramsType: PluginCypress['resolvedOptions']['paramsType']
|
|
20
27
|
pathParamsType: PluginCypress['resolvedOptions']['pathParamsType']
|
|
21
|
-
method: HttpMethod
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
type GetParamsProps = {
|
|
25
|
-
paramsCasing: PluginCypress['resolvedOptions']['paramsCasing']
|
|
26
|
-
paramsType: PluginCypress['resolvedOptions']['paramsType']
|
|
27
|
-
pathParamsType: PluginCypress['resolvedOptions']['pathParamsType']
|
|
28
|
-
typeSchemas: OperationSchemas
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
type: typeSchemas.request?.name,
|
|
43
|
-
optional: isOptional(typeSchemas.request?.schema),
|
|
44
|
-
}
|
|
45
|
-
: undefined,
|
|
46
|
-
params: typeSchemas.queryParams?.name
|
|
47
|
-
? {
|
|
48
|
-
type: typeSchemas.queryParams?.name,
|
|
49
|
-
optional: isOptional(typeSchemas.queryParams?.schema),
|
|
50
|
-
}
|
|
51
|
-
: undefined,
|
|
52
|
-
headers: typeSchemas.headerParams?.name
|
|
53
|
-
? {
|
|
54
|
-
type: typeSchemas.headerParams?.name,
|
|
55
|
-
optional: isOptional(typeSchemas.headerParams?.schema),
|
|
56
|
-
}
|
|
57
|
-
: undefined,
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
options: {
|
|
61
|
-
type: 'Partial<Cypress.RequestOptions>',
|
|
62
|
-
default: '{}',
|
|
63
|
-
},
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return FunctionParams.factory({
|
|
68
|
-
pathParams: typeSchemas.pathParams?.name
|
|
69
|
-
? {
|
|
70
|
-
mode: pathParamsType === 'object' ? 'object' : 'inlineSpread',
|
|
71
|
-
children: getPathParams(typeSchemas.pathParams, { typed: true, casing: paramsCasing }),
|
|
72
|
-
default: isAllOptional(typeSchemas.pathParams?.schema) ? '{}' : undefined,
|
|
73
|
-
}
|
|
74
|
-
: undefined,
|
|
75
|
-
data: typeSchemas.request?.name
|
|
76
|
-
? {
|
|
77
|
-
type: typeSchemas.request?.name,
|
|
78
|
-
optional: isOptional(typeSchemas.request?.schema),
|
|
79
|
-
}
|
|
80
|
-
: undefined,
|
|
81
|
-
params: typeSchemas.queryParams?.name
|
|
82
|
-
? {
|
|
83
|
-
type: typeSchemas.queryParams?.name,
|
|
84
|
-
optional: isOptional(typeSchemas.queryParams?.schema),
|
|
85
|
-
}
|
|
86
|
-
: undefined,
|
|
87
|
-
headers: typeSchemas.headerParams?.name
|
|
88
|
-
? {
|
|
89
|
-
type: typeSchemas.headerParams?.name,
|
|
90
|
-
optional: isOptional(typeSchemas.headerParams?.schema),
|
|
91
|
-
}
|
|
92
|
-
: undefined,
|
|
93
|
-
options: {
|
|
94
|
-
type: 'Partial<Cypress.RequestOptions>',
|
|
95
|
-
default: '{}',
|
|
96
|
-
},
|
|
30
|
+
const declarationPrinter = functionPrinter({ mode: 'declaration' })
|
|
31
|
+
|
|
32
|
+
export function Request({ baseURL = '', name, dataReturnType, resolver, node, paramsType, pathParamsType, paramsCasing }: Props): FabricReactNode {
|
|
33
|
+
const paramsNode = createOperationParams(node, {
|
|
34
|
+
paramsType,
|
|
35
|
+
pathParamsType,
|
|
36
|
+
paramsCasing,
|
|
37
|
+
resolver,
|
|
38
|
+
extraParams: [
|
|
39
|
+
createFunctionParameter({ name: 'options', type: createTypeNode({ variant: 'reference', name: 'Partial<Cypress.RequestOptions>' }), default: '{}' }),
|
|
40
|
+
],
|
|
97
41
|
})
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
export function Request({ baseURL = '', name, dataReturnType, typeSchemas, url, method, paramsType, paramsCasing, pathParamsType }: Props): FabricReactNode {
|
|
101
|
-
const path = new URLPath(url, { casing: paramsCasing })
|
|
102
|
-
|
|
103
|
-
const params = getParams({ paramsType, paramsCasing, pathParamsType, typeSchemas })
|
|
42
|
+
const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
|
|
104
43
|
|
|
105
|
-
const
|
|
106
|
-
|
|
44
|
+
const responseType = resolver.resolveResponseName(node)
|
|
45
|
+
const returnType = dataReturnType === 'data' ? `Cypress.Chainable<${responseType}>` : `Cypress.Chainable<Cypress.Response<${responseType}>>`
|
|
107
46
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
47
|
+
const casedPathParams = caseParams(
|
|
48
|
+
node.parameters.filter((p) => p.in === 'path'),
|
|
49
|
+
paramsCasing,
|
|
50
|
+
)
|
|
51
|
+
// Build a lookup keyed by camelCase-normalized name so that path-template names
|
|
52
|
+
// (e.g. `{pet_id}`) correctly resolve to the function-parameter name (`petId`)
|
|
53
|
+
// even when the OpenAPI spec has inconsistent casing between the two.
|
|
54
|
+
const pathParamNameMap = new Map(casedPathParams.map((p) => [camelCase(p.name), p.name]))
|
|
55
|
+
|
|
56
|
+
const urlPath = new URLPath(node.path, { casing: paramsCasing })
|
|
57
|
+
const urlTemplate = urlPath.toTemplateString({
|
|
58
|
+
prefix: baseURL,
|
|
59
|
+
replacer: (param) => pathParamNameMap.get(camelCase(param)) ?? param,
|
|
60
|
+
})
|
|
113
61
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
62
|
+
const requestOptions: string[] = [`method: '${node.method}'`, `url: ${urlTemplate}`]
|
|
63
|
+
|
|
64
|
+
const queryParams = node.parameters.filter((p) => p.in === 'query')
|
|
65
|
+
if (queryParams.length > 0) {
|
|
66
|
+
const casedQueryParams = caseParams(queryParams, paramsCasing)
|
|
67
|
+
// When paramsCasing renames query params (e.g. page_size → pageSize), we must remap
|
|
68
|
+
// the camelCase keys back to the original API names before passing them to `qs`.
|
|
69
|
+
const needsQsTransform = casedQueryParams.some((p, i) => p.name !== queryParams[i]!.name)
|
|
70
|
+
if (needsQsTransform) {
|
|
71
|
+
const pairs = queryParams.map((orig, i) => `${orig.name}: params.${casedQueryParams[i]!.name}`).join(', ')
|
|
72
|
+
requestOptions.push(`qs: params ? { ${pairs} } : undefined`)
|
|
73
|
+
} else {
|
|
74
|
+
requestOptions.push('qs: params')
|
|
75
|
+
}
|
|
117
76
|
}
|
|
118
77
|
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
|
|
78
|
+
const headerParams = node.parameters.filter((p) => p.in === 'header')
|
|
79
|
+
if (headerParams.length > 0) {
|
|
80
|
+
const casedHeaderParams = caseParams(headerParams, paramsCasing)
|
|
81
|
+
// When paramsCasing renames header params (e.g. x-api-key → xApiKey), we must remap
|
|
82
|
+
// the camelCase keys back to the original API names before passing them to `headers`.
|
|
83
|
+
const needsHeaderTransform = casedHeaderParams.some((p, i) => p.name !== headerParams[i]!.name)
|
|
84
|
+
if (needsHeaderTransform) {
|
|
85
|
+
const pairs = headerParams.map((orig, i) => `'${orig.name}': headers.${casedHeaderParams[i]!.name}`).join(', ')
|
|
86
|
+
requestOptions.push(`headers: headers ? { ${pairs} } : undefined`)
|
|
87
|
+
} else {
|
|
88
|
+
requestOptions.push('headers')
|
|
89
|
+
}
|
|
122
90
|
}
|
|
123
91
|
|
|
124
|
-
|
|
125
|
-
if (typeSchemas.request?.name) {
|
|
92
|
+
if (node.requestBody?.schema) {
|
|
126
93
|
requestOptions.push('body: data')
|
|
127
94
|
}
|
|
128
95
|
|
|
129
|
-
// Spread additional Cypress options
|
|
130
96
|
requestOptions.push('...options')
|
|
131
97
|
|
|
132
98
|
return (
|
|
133
99
|
<File.Source name={name} isIndexable isExportable>
|
|
134
|
-
<Function name={name} export params={
|
|
100
|
+
<Function name={name} export params={paramsSignature} returnType={returnType}>
|
|
135
101
|
{dataReturnType === 'data'
|
|
136
|
-
? `return cy.request<${
|
|
102
|
+
? `return cy.request<${responseType}>({
|
|
137
103
|
${requestOptions.join(',\n ')}
|
|
138
104
|
}).then((res) => res.body)`
|
|
139
|
-
: `return cy.request<${
|
|
105
|
+
: `return cy.request<${responseType}>({
|
|
140
106
|
${requestOptions.join(',\n ')}
|
|
141
107
|
})`}
|
|
142
108
|
</Function>
|
|
143
109
|
</File.Source>
|
|
144
110
|
)
|
|
145
111
|
}
|
|
146
|
-
|
|
147
|
-
Request.getParams = getParams
|
|
@@ -1,64 +1,69 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { useOas, useOperationManager } from '@kubb/plugin-oas/hooks'
|
|
4
|
-
import { getBanner, getFooter } from '@kubb/plugin-oas/utils'
|
|
1
|
+
import { caseParams } from '@kubb/ast'
|
|
2
|
+
import { defineGenerator } from '@kubb/core'
|
|
5
3
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
6
4
|
import { File } from '@kubb/react-fabric'
|
|
7
|
-
import { Request } from '../components'
|
|
8
|
-
import type { PluginCypress } from '../types'
|
|
5
|
+
import { Request } from '../components/Request.tsx'
|
|
6
|
+
import type { PluginCypress } from '../types.ts'
|
|
9
7
|
|
|
10
|
-
export const cypressGenerator =
|
|
8
|
+
export const cypressGenerator = defineGenerator<PluginCypress>({
|
|
11
9
|
name: 'cypress',
|
|
12
|
-
|
|
13
|
-
const {
|
|
14
|
-
|
|
15
|
-
} = plugin
|
|
16
|
-
const pluginManager = usePluginManager()
|
|
10
|
+
operation(node, options) {
|
|
11
|
+
const { adapter, config, resolver, driver, root } = this
|
|
12
|
+
const { output, baseURL, dataReturnType, paramsCasing, paramsType, pathParamsType, group } = options
|
|
17
13
|
|
|
18
|
-
const
|
|
19
|
-
const { getSchemas, getName, getFile } = useOperationManager(generator)
|
|
14
|
+
const pluginTs = driver.getPlugin(pluginTsName)
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
file: getFile(operation),
|
|
16
|
+
if (!pluginTs?.resolver) {
|
|
17
|
+
return null
|
|
24
18
|
}
|
|
25
19
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
const casedParams = caseParams(node.parameters, paramsCasing)
|
|
21
|
+
|
|
22
|
+
const pathParams = casedParams.filter((p) => p.in === 'path')
|
|
23
|
+
const queryParams = casedParams.filter((p) => p.in === 'query')
|
|
24
|
+
const headerParams = casedParams.filter((p) => p.in === 'header')
|
|
25
|
+
|
|
26
|
+
const importedTypeNames = [
|
|
27
|
+
...pathParams.map((p) => pluginTs.resolver.resolvePathParamsName(node, p)),
|
|
28
|
+
...queryParams.map((p) => pluginTs.resolver.resolveQueryParamsName(node, p)),
|
|
29
|
+
...headerParams.map((p) => pluginTs.resolver.resolveHeaderParamsName(node, p)),
|
|
30
|
+
node.requestBody?.schema ? pluginTs.resolver.resolveDataName(node) : undefined,
|
|
31
|
+
pluginTs.resolver.resolveResponseName(node),
|
|
32
|
+
].filter(Boolean)
|
|
33
|
+
|
|
34
|
+
const meta = {
|
|
35
|
+
name: resolver.resolveName(node.operationId),
|
|
36
|
+
file: resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
|
|
37
|
+
fileTs: pluginTs.resolver.resolveFile(
|
|
38
|
+
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
39
|
+
{
|
|
40
|
+
root,
|
|
41
|
+
output: pluginTs.options?.output ?? output,
|
|
42
|
+
group: pluginTs.options?.group,
|
|
43
|
+
},
|
|
44
|
+
),
|
|
45
|
+
} as const
|
|
30
46
|
|
|
31
47
|
return (
|
|
32
48
|
<File
|
|
33
|
-
baseName={
|
|
34
|
-
path={
|
|
35
|
-
meta={
|
|
36
|
-
banner={
|
|
37
|
-
footer={
|
|
49
|
+
baseName={meta.file.baseName}
|
|
50
|
+
path={meta.file.path}
|
|
51
|
+
meta={meta.file.meta}
|
|
52
|
+
banner={resolver.resolveBanner(adapter.rootNode, { output, config })}
|
|
53
|
+
footer={resolver.resolveFooter(adapter.rootNode, { output, config })}
|
|
38
54
|
>
|
|
39
|
-
|
|
40
|
-
name={
|
|
41
|
-
|
|
42
|
-
type.schemas.response.name,
|
|
43
|
-
type.schemas.pathParams?.name,
|
|
44
|
-
type.schemas.queryParams?.name,
|
|
45
|
-
type.schemas.headerParams?.name,
|
|
46
|
-
...(type.schemas.statusCodes?.map((item) => item.name) || []),
|
|
47
|
-
].filter(Boolean)}
|
|
48
|
-
root={request.file.path}
|
|
49
|
-
path={type.file.path}
|
|
50
|
-
isTypeOnly
|
|
51
|
-
/>
|
|
55
|
+
{meta.fileTs && importedTypeNames.length > 0 && (
|
|
56
|
+
<File.Import name={Array.from(new Set(importedTypeNames))} root={meta.file.path} path={meta.fileTs.path} isTypeOnly />
|
|
57
|
+
)}
|
|
52
58
|
<Request
|
|
53
|
-
name={
|
|
59
|
+
name={meta.name}
|
|
60
|
+
node={node}
|
|
61
|
+
resolver={pluginTs.resolver}
|
|
54
62
|
dataReturnType={dataReturnType}
|
|
55
63
|
paramsCasing={paramsCasing}
|
|
56
64
|
paramsType={paramsType}
|
|
57
65
|
pathParamsType={pathParamsType}
|
|
58
|
-
typeSchemas={type.schemas}
|
|
59
|
-
method={operation.method}
|
|
60
66
|
baseURL={baseURL}
|
|
61
|
-
url={operation.path}
|
|
62
67
|
/>
|
|
63
68
|
</File>
|
|
64
69
|
)
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
export { Request } from './components/Request.tsx'
|
|
2
|
+
|
|
3
|
+
export { cypressGenerator } from './generators/cypressGenerator.tsx'
|
|
4
|
+
|
|
1
5
|
export { pluginCypress, pluginCypressName } from './plugin.ts'
|
|
2
|
-
|
|
6
|
+
|
|
7
|
+
export { resolverCypress } from './resolvers/resolverCypress.ts'
|
|
8
|
+
|
|
9
|
+
export type { PluginCypress, ResolverCypress } from './types.ts'
|
package/src/plugin.ts
CHANGED
|
@@ -1,119 +1,109 @@
|
|
|
1
|
-
import path from 'node:path'
|
|
2
1
|
import { camelCase } from '@internals/utils'
|
|
3
|
-
import {
|
|
4
|
-
import { OperationGenerator, pluginOasName } from '@kubb/plugin-oas'
|
|
2
|
+
import { createPlugin, type Group, getPreset, mergeGenerators } from '@kubb/core'
|
|
5
3
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
6
|
-
import {
|
|
4
|
+
import { version } from '../package.json'
|
|
5
|
+
import { presets } from './presets.ts'
|
|
7
6
|
import type { PluginCypress } from './types.ts'
|
|
8
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Canonical plugin name for `@kubb/plugin-cypress`, used to identify the plugin
|
|
10
|
+
* in driver lookups and warnings.
|
|
11
|
+
*/
|
|
9
12
|
export const pluginCypressName = 'plugin-cypress' satisfies PluginCypress['name']
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The `@kubb/plugin-cypress` plugin factory.
|
|
16
|
+
*
|
|
17
|
+
* Generates Cypress `cy.request()` test functions from an OpenAPI/AST `RootNode`.
|
|
18
|
+
* Walks operations, delegates rendering to the active generators,
|
|
19
|
+
* and writes barrel files based on `output.barrelType`.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { pluginCypress } from '@kubb/plugin-cypress'
|
|
24
|
+
*
|
|
25
|
+
* export default defineConfig({
|
|
26
|
+
* plugins: [pluginCypress({ output: { path: 'cypress' } })],
|
|
27
|
+
* })
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export const pluginCypress = createPlugin<PluginCypress>((options) => {
|
|
12
31
|
const {
|
|
13
32
|
output = { path: 'cypress', barrelType: 'named' },
|
|
14
33
|
group,
|
|
15
|
-
dataReturnType = 'data',
|
|
16
34
|
exclude = [],
|
|
17
35
|
include,
|
|
18
36
|
override = [],
|
|
19
|
-
|
|
20
|
-
generators = [cypressGenerator].filter(Boolean),
|
|
21
|
-
contentType,
|
|
37
|
+
dataReturnType = 'data',
|
|
22
38
|
baseURL,
|
|
23
39
|
paramsCasing,
|
|
24
40
|
paramsType = 'inline',
|
|
25
41
|
pathParamsType = paramsType === 'object' ? 'object' : options.pathParamsType || 'inline',
|
|
42
|
+
compatibilityPreset = 'default',
|
|
43
|
+
resolver: userResolver,
|
|
44
|
+
transformer: userTransformer,
|
|
45
|
+
generators: userGenerators = [],
|
|
26
46
|
} = options
|
|
27
47
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
const preset = getPreset({
|
|
49
|
+
preset: compatibilityPreset,
|
|
50
|
+
presets,
|
|
51
|
+
resolver: userResolver,
|
|
52
|
+
transformer: userTransformer,
|
|
53
|
+
generators: userGenerators,
|
|
54
|
+
})
|
|
35
55
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
pathParamsType,
|
|
39
|
-
},
|
|
40
|
-
pre: [pluginOasName, pluginTsName].filter(Boolean),
|
|
41
|
-
resolvePath(baseName, pathMode, options) {
|
|
42
|
-
const root = path.resolve(this.config.root, this.config.output.path)
|
|
43
|
-
const mode = pathMode ?? getMode(path.resolve(root, output.path))
|
|
44
|
-
|
|
45
|
-
if (mode === 'single') {
|
|
46
|
-
/**
|
|
47
|
-
* when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
|
|
48
|
-
* Other plugins then need to call addOrAppend instead of just add from the fileManager class
|
|
49
|
-
*/
|
|
50
|
-
return path.resolve(root, output.path)
|
|
51
|
-
}
|
|
56
|
+
const generators = preset.generators ?? []
|
|
57
|
+
const mergedGenerator = mergeGenerators(generators)
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return `${ctx.group.split('/')[1]}`
|
|
59
|
-
}
|
|
60
|
-
return `${camelCase(ctx.group)}Requests`
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return path.resolve(
|
|
64
|
-
root,
|
|
65
|
-
output.path,
|
|
66
|
-
groupName({
|
|
67
|
-
group: group.type === 'path' ? options.group.path! : options.group.tag!,
|
|
68
|
-
}),
|
|
69
|
-
baseName,
|
|
70
|
-
)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return path.resolve(root, output.path, baseName)
|
|
59
|
+
return {
|
|
60
|
+
name: pluginCypressName,
|
|
61
|
+
version,
|
|
62
|
+
get resolver() {
|
|
63
|
+
return preset.resolver
|
|
74
64
|
},
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
isFile: type === 'file',
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
if (type) {
|
|
81
|
-
return transformers?.name?.(resolvedName, type) || resolvedName
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return resolvedName
|
|
65
|
+
get transformer() {
|
|
66
|
+
return preset.transformer
|
|
85
67
|
},
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const oas = await this.getOas()
|
|
90
|
-
|
|
91
|
-
const operationGenerator = new OperationGenerator(this.plugin.options, {
|
|
92
|
-
fabric: this.fabric,
|
|
93
|
-
oas,
|
|
94
|
-
pluginManager: this.pluginManager,
|
|
95
|
-
events: this.events,
|
|
96
|
-
plugin: this.plugin,
|
|
97
|
-
contentType,
|
|
68
|
+
get options() {
|
|
69
|
+
return {
|
|
70
|
+
output,
|
|
98
71
|
exclude,
|
|
99
72
|
include,
|
|
100
73
|
override,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
74
|
+
dataReturnType,
|
|
75
|
+
group: group
|
|
76
|
+
? ({
|
|
77
|
+
...group,
|
|
78
|
+
name: group.name
|
|
79
|
+
? group.name
|
|
80
|
+
: (ctx: { group: string }) => {
|
|
81
|
+
if (group.type === 'path') {
|
|
82
|
+
return `${ctx.group.split('/')[1]}`
|
|
83
|
+
}
|
|
84
|
+
return `${camelCase(ctx.group)}Requests`
|
|
85
|
+
},
|
|
86
|
+
} satisfies Group)
|
|
87
|
+
: undefined,
|
|
88
|
+
baseURL,
|
|
89
|
+
paramsCasing,
|
|
90
|
+
paramsType,
|
|
91
|
+
pathParamsType,
|
|
92
|
+
resolver: preset.resolver,
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
pre: [pluginTsName].filter(Boolean),
|
|
96
|
+
async schema(node, options) {
|
|
97
|
+
return mergedGenerator.schema?.call(this, node, options)
|
|
98
|
+
},
|
|
99
|
+
async operation(node, options) {
|
|
100
|
+
return mergedGenerator.operation?.call(this, node, options)
|
|
101
|
+
},
|
|
102
|
+
async operations(nodes, options) {
|
|
103
|
+
return mergedGenerator.operations?.call(this, nodes, options)
|
|
104
|
+
},
|
|
105
|
+
async buildStart() {
|
|
106
|
+
await this.openInStudio({ ast: true })
|
|
117
107
|
},
|
|
118
108
|
}
|
|
119
109
|
})
|
package/src/presets.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { definePresets } from '@kubb/core'
|
|
2
|
+
import { cypressGenerator } from './generators/cypressGenerator.tsx'
|
|
3
|
+
import { resolverCypress } from './resolvers/resolverCypress.ts'
|
|
4
|
+
import type { ResolverCypress } from './types.ts'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Built-in preset registry for `@kubb/plugin-cypress`.
|
|
8
|
+
*
|
|
9
|
+
* - `default` — uses `resolverCypress` and `cypressGenerator`.
|
|
10
|
+
* - `kubbV4` — uses `resolverCypress` and `cypressGenerator`.
|
|
11
|
+
*/
|
|
12
|
+
export const presets = definePresets<ResolverCypress>({
|
|
13
|
+
default: {
|
|
14
|
+
name: 'default',
|
|
15
|
+
resolver: resolverCypress,
|
|
16
|
+
generators: [cypressGenerator],
|
|
17
|
+
},
|
|
18
|
+
kubbV4: {
|
|
19
|
+
name: 'kubbV4',
|
|
20
|
+
resolver: resolverCypress,
|
|
21
|
+
generators: [cypressGenerator],
|
|
22
|
+
},
|
|
23
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { camelCase } from '@internals/utils'
|
|
2
|
+
import { defineResolver } from '@kubb/core'
|
|
3
|
+
import type { PluginCypress } from '../types.ts'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolver for `@kubb/plugin-cypress` that provides the default naming
|
|
7
|
+
* and path-resolution helpers used by the plugin.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { resolverCypress } from '@kubb/plugin-cypress'
|
|
12
|
+
*
|
|
13
|
+
* resolverCypress.default('list pets', 'function') // -> 'listPets'
|
|
14
|
+
* resolverCypress.resolveName('show pet by id') // -> 'showPetById'
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export const resolverCypress = defineResolver<PluginCypress>(() => ({
|
|
18
|
+
name: 'default',
|
|
19
|
+
pluginName: 'plugin-cypress',
|
|
20
|
+
default(name, type) {
|
|
21
|
+
return camelCase(name, { isFile: type === 'file' })
|
|
22
|
+
},
|
|
23
|
+
resolveName(name) {
|
|
24
|
+
return this.default(name, 'function')
|
|
25
|
+
},
|
|
26
|
+
}))
|