@kubb/plugin-cypress 5.0.0-beta.3 → 5.0.0-beta.30

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/package.json CHANGED
@@ -1,21 +1,15 @@
1
1
  {
2
2
  "name": "@kubb/plugin-cypress",
3
- "version": "5.0.0-beta.3",
4
- "description": "Cypress test generator plugin for Kubb, creating end-to-end tests from OpenAPI specifications for automated API testing.",
3
+ "version": "5.0.0-beta.30",
4
+ "description": "Generate Cypress request commands and e2e test fixtures from your OpenAPI specification, enabling automated API testing with zero manual setup.",
5
5
  "keywords": [
6
- "api-testing",
7
- "code-generator",
6
+ "code-generation",
8
7
  "codegen",
9
8
  "cypress",
10
9
  "e2e-testing",
11
- "end-to-end",
12
- "integration-testing",
13
10
  "kubb",
14
- "oas",
15
11
  "openapi",
16
- "plugins",
17
12
  "swagger",
18
- "test-automation",
19
13
  "test-generation",
20
14
  "testing",
21
15
  "typescript"
@@ -30,7 +24,7 @@
30
24
  "files": [
31
25
  "src",
32
26
  "dist",
33
- "plugin.json",
27
+ "extension.yaml",
34
28
  "!/**/**.test.**",
35
29
  "!/**/__tests__/**",
36
30
  "!/**/__snapshots__/**"
@@ -53,15 +47,16 @@
53
47
  "registry": "https://registry.npmjs.org/"
54
48
  },
55
49
  "dependencies": {
56
- "@kubb/core": "5.0.0-beta.3",
57
- "@kubb/renderer-jsx": "5.0.0-beta.3",
58
- "@kubb/plugin-ts": "5.0.0-beta.3"
50
+ "@kubb/core": "5.0.0-beta.29",
51
+ "@kubb/renderer-jsx": "5.0.0-beta.29",
52
+ "@kubb/plugin-ts": "5.0.0-beta.30"
59
53
  },
60
54
  "devDependencies": {
55
+ "@internals/shared": "0.0.0",
61
56
  "@internals/utils": "0.0.0"
62
57
  },
63
58
  "peerDependencies": {
64
- "@kubb/renderer-jsx": "5.0.0-beta.3"
59
+ "@kubb/renderer-jsx": "5.0.0-beta.29"
65
60
  },
66
61
  "size-limit": [
67
62
  {
@@ -1,3 +1,4 @@
1
+ import { getOperationParameters } from '@internals/shared'
1
2
  import { camelCase, URLPath } from '@internals/utils'
2
3
  import { ast } from '@kubb/core'
3
4
  import type { ResolverTs } from '@kubb/plugin-ts'
@@ -19,7 +20,7 @@ type Props = {
19
20
  * TypeScript resolver for resolving param/data/response type names
20
21
  */
21
22
  resolver: ResolverTs
22
- baseURL: string | undefined
23
+ baseURL: string | null | undefined
23
24
  dataReturnType: PluginCypress['resolvedOptions']['dataReturnType']
24
25
  paramsCasing: PluginCypress['resolvedOptions']['paramsCasing']
25
26
  paramsType: PluginCypress['resolvedOptions']['paramsType']
@@ -47,10 +48,7 @@ export function Request({ baseURL = '', name, dataReturnType, resolver, node, pa
47
48
  const responseType = resolver.resolveResponseName(node)
48
49
  const returnType = dataReturnType === 'data' ? `Cypress.Chainable<${responseType}>` : `Cypress.Chainable<Cypress.Response<${responseType}>>`
49
50
 
50
- const casedPathParams = ast.caseParams(
51
- node.parameters.filter((p) => p.in === 'path'),
52
- paramsCasing,
53
- )
51
+ const casedPathParams = getOperationParameters(node, { paramsCasing }).path
54
52
  // Build a lookup keyed by camelCase-normalized name so that path-template names
55
53
  // (e.g. `{pet_id}`) correctly resolve to the function-parameter name (`petId`)
56
54
  // even when the OpenAPI spec has inconsistent casing between the two.
@@ -62,11 +60,11 @@ export function Request({ baseURL = '', name, dataReturnType, resolver, node, pa
62
60
  replacer: (param) => pathParamNameMap.get(camelCase(param)) ?? param,
63
61
  })
64
62
 
65
- const requestOptions: string[] = [`method: '${node.method}'`, `url: ${urlTemplate}`]
63
+ const requestOptions: Array<string> = [`method: '${node.method}'`, `url: ${urlTemplate}`]
66
64
 
67
- const queryParams = node.parameters.filter((p) => p.in === 'query')
65
+ const queryParams = getOperationParameters(node).query
68
66
  if (queryParams.length > 0) {
69
- const casedQueryParams = ast.caseParams(queryParams, paramsCasing)
67
+ const casedQueryParams = getOperationParameters(node, { paramsCasing }).query
70
68
  // When paramsCasing renames query params (e.g. page_size → pageSize), we must remap
71
69
  // the camelCase keys back to the original API names before passing them to `qs`.
72
70
  const needsQsTransform = casedQueryParams.some((p, i) => p.name !== queryParams[i]!.name)
@@ -78,9 +76,9 @@ export function Request({ baseURL = '', name, dataReturnType, resolver, node, pa
78
76
  }
79
77
  }
80
78
 
81
- const headerParams = node.parameters.filter((p) => p.in === 'header')
79
+ const headerParams = getOperationParameters(node).header
82
80
  if (headerParams.length > 0) {
83
- const casedHeaderParams = ast.caseParams(headerParams, paramsCasing)
81
+ const casedHeaderParams = getOperationParameters(node, { paramsCasing }).header
84
82
  // When paramsCasing renames header params (e.g. x-api-key → xApiKey), we must remap
85
83
  // the camelCase keys back to the original API names before passing them to `headers`.
86
84
  const needsHeaderTransform = casedHeaderParams.some((p, i) => p.name !== headerParams[i]!.name)
@@ -1,14 +1,20 @@
1
- import { ast, defineGenerator } from '@kubb/core'
1
+ import { resolveOperationTypeNames } from '@internals/shared'
2
+ import { defineGenerator } from '@kubb/core'
2
3
  import { pluginTsName } from '@kubb/plugin-ts'
3
- import { File, jsxRenderer } from '@kubb/renderer-jsx'
4
+ import { File, jsxRendererSync } from '@kubb/renderer-jsx'
4
5
  import { Request } from '../components/Request.tsx'
5
6
  import type { PluginCypress } from '../types.ts'
6
7
 
8
+ /**
9
+ * Built-in generator for `@kubb/plugin-cypress`. Emits one typed
10
+ * `cy.request()` wrapper per OpenAPI operation, ready to call inside Cypress
11
+ * test specs and custom commands.
12
+ */
7
13
  export const cypressGenerator = defineGenerator<PluginCypress>({
8
14
  name: 'cypress',
9
- renderer: jsxRenderer,
15
+ renderer: jsxRendererSync,
10
16
  operation(node, ctx) {
11
- const { adapter, config, resolver, driver, root } = ctx
17
+ const { config, resolver, driver, root } = ctx
12
18
  const { output, baseURL, dataReturnType, paramsCasing, paramsType, pathParamsType, group } = ctx.options
13
19
 
14
20
  const pluginTs = driver.getPlugin(pluginTsName)
@@ -19,29 +25,20 @@ export const cypressGenerator = defineGenerator<PluginCypress>({
19
25
 
20
26
  const tsResolver = driver.getResolver(pluginTsName)
21
27
 
22
- const casedParams = ast.caseParams(node.parameters, paramsCasing)
23
-
24
- const pathParams = casedParams.filter((p) => p.in === 'path')
25
- const queryParams = casedParams.filter((p) => p.in === 'query')
26
- const headerParams = casedParams.filter((p) => p.in === 'header')
27
-
28
- const importedTypeNames = [
29
- ...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
30
- ...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
31
- ...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
32
- node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : undefined,
33
- tsResolver.resolveResponseName(node),
34
- ].filter(Boolean)
28
+ const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing })
35
29
 
36
30
  const meta = {
37
31
  name: resolver.resolveName(node.operationId),
38
- file: resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
32
+ file: resolver.resolveFile(
33
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
34
+ { root, output, group: group ?? undefined },
35
+ ),
39
36
  fileTs: tsResolver.resolveFile(
40
37
  { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
41
38
  {
42
39
  root,
43
40
  output: pluginTs.options?.output ?? output,
44
- group: pluginTs.options?.group,
41
+ group: pluginTs.options?.group ?? undefined,
45
42
  },
46
43
  ),
47
44
  } as const
@@ -51,12 +48,10 @@ export const cypressGenerator = defineGenerator<PluginCypress>({
51
48
  baseName={meta.file.baseName}
52
49
  path={meta.file.path}
53
50
  meta={meta.file.meta}
54
- banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
55
- footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
51
+ banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
52
+ footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
56
53
  >
57
- {meta.fileTs && importedTypeNames.length > 0 && (
58
- <File.Import name={Array.from(new Set(importedTypeNames))} root={meta.file.path} path={meta.fileTs.path} isTypeOnly />
59
- )}
54
+ {meta.fileTs && importedTypeNames.length > 0 && <File.Import name={importedTypeNames} root={meta.file.path} path={meta.fileTs.path} isTypeOnly />}
60
55
  <Request
61
56
  name={meta.name}
62
57
  node={node}
package/src/plugin.ts CHANGED
@@ -6,24 +6,31 @@ import { resolverCypress } from './resolvers/resolverCypress.ts'
6
6
  import type { PluginCypress } from './types.ts'
7
7
 
8
8
  /**
9
- * Canonical plugin name for `@kubb/plugin-cypress`, used to identify the plugin
10
- * in driver lookups and warnings.
9
+ * Canonical plugin name for `@kubb/plugin-cypress`. Used for driver lookups and
10
+ * cross-plugin dependency references.
11
11
  */
12
12
  export const pluginCypressName = 'plugin-cypress' satisfies PluginCypress['name']
13
13
 
14
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`.
15
+ * Generates one typed `cy.request()` wrapper per OpenAPI operation. Each helper
16
+ * has typed path params, body, query, and a typed response, so failing API
17
+ * calls in Cypress show up at compile time instead of inside the test runner.
20
18
  *
21
19
  * @example
22
20
  * ```ts
23
- * import pluginCypress from '@kubb/plugin-cypress'
21
+ * import { defineConfig } from 'kubb'
22
+ * import { pluginTs } from '@kubb/plugin-ts'
23
+ * import { pluginCypress } from '@kubb/plugin-cypress'
24
24
  *
25
25
  * export default defineConfig({
26
- * plugins: [pluginCypress({ output: { path: 'cypress' } })],
26
+ * input: { path: './petStore.yaml' },
27
+ * output: { path: './src/gen' },
28
+ * plugins: [
29
+ * pluginTs(),
30
+ * pluginCypress({
31
+ * output: { path: './cypress' },
32
+ * }),
33
+ * ],
27
34
  * })
28
35
  * ```
29
36
  */
@@ -56,7 +63,7 @@ export const pluginCypress = definePlugin<PluginCypress>((options) => {
56
63
  return `${camelCase(ctx.group)}Requests`
57
64
  },
58
65
  } satisfies Group)
59
- : undefined
66
+ : null
60
67
 
61
68
  return {
62
69
  name: pluginCypressName,
@@ -3,20 +3,27 @@ import { defineResolver } from '@kubb/core'
3
3
  import type { PluginCypress } from '../types.ts'
4
4
 
5
5
  /**
6
- * Naming convention resolver for Cypress plugin.
6
+ * Default resolver used by `@kubb/plugin-cypress`. Decides the names and file
7
+ * paths for every generated `cy.request()` wrapper. Functions and files use
8
+ * camelCase, matching the convention from `@kubb/plugin-client`.
7
9
  *
8
- * Provides default naming helpers using camelCase for functions and file paths.
10
+ * @example Resolve a helper name
11
+ * ```ts
12
+ * import { resolverCypress } from '@kubb/plugin-cypress'
9
13
  *
10
- * @example
11
- * `resolverCypress.default('list pets', 'function') // → 'listPets'`
14
+ * resolverCypress.default('list pets', 'function') // 'listPets'
15
+ * ```
12
16
  */
13
- export const resolverCypress = defineResolver<PluginCypress>((ctx) => ({
17
+ export const resolverCypress = defineResolver<PluginCypress>(() => ({
14
18
  name: 'default',
15
19
  pluginName: 'plugin-cypress',
16
20
  default(name, type) {
17
21
  return camelCase(name, { isFile: type === 'file' })
18
22
  },
19
23
  resolveName(name) {
20
- return ctx.default(name, 'function')
24
+ return this.default(name, 'function')
25
+ },
26
+ resolvePathName(name, type) {
27
+ return this.default(name, type)
21
28
  },
22
29
  }))
package/src/types.ts CHANGED
@@ -11,6 +11,10 @@ export type ResolverCypress = Resolver & {
11
11
  * `resolver.resolveName('show pet by id') // -> 'showPetById'`
12
12
  */
13
13
  resolveName(this: ResolverCypress, name: string): string
14
+ /**
15
+ * Resolves the output file name for a Cypress request module.
16
+ */
17
+ resolvePathName(this: ResolverCypress, name: string, type?: 'file' | 'function' | 'type' | 'const'): string
14
18
  }
15
19
 
16
20
  /**
@@ -19,21 +23,23 @@ export type ResolverCypress = Resolver & {
19
23
  type ParamsTypeOptions =
20
24
  | {
21
25
  /**
22
- * All parameters merged into a single destructured object.
26
+ * Every operation parameter is wrapped in a single destructured object argument.
23
27
  */
24
28
  paramsType: 'object'
25
29
  /**
26
- * Not applicable when all parameters are merged into a single object.
30
+ * `pathParamsType` has no effect when `paramsType` is `'object'`.
27
31
  */
28
32
  pathParamsType?: never
29
33
  }
30
34
  | {
31
35
  /**
32
- * Each parameter group emitted as a separate function argument.
36
+ * Each parameter group is emitted as a separate positional function argument.
33
37
  */
34
38
  paramsType?: 'inline'
35
39
  /**
36
- * How path parameters are arranged: grouped in an object or spread inline.
40
+ * How URL path parameters are arranged inside the inline argument list.
41
+ * - `'object'` groups them into one destructured object.
42
+ * - `'inline'` emits each path param as its own argument.
37
43
  *
38
44
  * @default 'inline'
39
45
  */
@@ -42,50 +48,56 @@ type ParamsTypeOptions =
42
48
 
43
49
  export type Options = {
44
50
  /**
45
- * Specify the export location for the files and define the behavior of the output.
46
- * @default { path: 'cypress', barrelType: 'named' }
51
+ * Where the generated Cypress helpers are written and how they are exported.
52
+ *
53
+ * @default { path: 'cypress', barrel: { type: 'named' } }
47
54
  */
48
55
  output?: Output
49
56
  /**
50
- * Return type when calling cy.request: response data only or full response.
57
+ * Shape of the value returned from each helper.
58
+ * - `'data'` — only the response body.
59
+ * - `'full'` — the full Cypress response object (headers, status, body).
51
60
  *
52
61
  * @default 'data'
53
62
  */
54
63
  dataReturnType?: 'data' | 'full'
55
64
  /**
56
- * Apply casing to parameter names.
65
+ * Rename parameter properties in the generated helpers (path, query, headers).
66
+ *
67
+ * @note Must match the value of `paramsCasing` on `@kubb/plugin-ts`.
57
68
  */
58
69
  paramsCasing?: 'camelcase'
59
70
  /**
60
- * Base URL prepended to every generated request URL.
71
+ * Base URL prepended to every request URL. When omitted, falls back to the
72
+ * adapter's server URL (typically `servers[0].url`).
61
73
  */
62
74
  baseURL?: string
63
75
  /**
64
- * Group the Cypress requests based on the provided name.
76
+ * Split generated files into subfolders based on the operation's tag.
65
77
  */
66
78
  group?: Group
67
79
  /**
68
- * Tags, operations, or paths to exclude from generation.
80
+ * Skip operations matching at least one entry in the list.
69
81
  */
70
82
  exclude?: Array<Exclude>
71
83
  /**
72
- * Tags, operations, or paths to include in generation.
84
+ * Restrict generation to operations matching at least one entry in the list.
73
85
  */
74
86
  include?: Array<Include>
75
87
  /**
76
- * Override options for specific tags, operations, or paths.
88
+ * Apply a different options object to operations matching a pattern.
77
89
  */
78
90
  override?: Array<Override<ResolvedOptions>>
79
91
  /**
80
- * Override naming conventions for function names and types.
92
+ * Override how helper names and file paths are built.
81
93
  */
82
94
  resolver?: Partial<ResolverCypress> & ThisType<ResolverCypress>
83
95
  /**
84
- * AST visitor to transform generated nodes.
96
+ * AST visitor applied to each operation node before printing.
85
97
  */
86
98
  transformer?: ast.Visitor
87
99
  /**
88
- * Additional generators alongside the default generators.
100
+ * Custom generators that run alongside the built-in Cypress generators.
89
101
  */
90
102
  generators?: Array<Generator<PluginCypress>>
91
103
  } & 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 | undefined
110
+ group: Group | null
99
111
  baseURL: Options['baseURL'] | undefined
100
112
  dataReturnType: NonNullable<Options['dataReturnType']>
101
113
  pathParamsType: NonNullable<NonNullable<Options['pathParamsType']>>