diff --git a/docs/content/docs/guides/faker.mdx b/docs/content/docs/guides/faker.mdx index 1cc92dcf7a..cf3915970a 100644 --- a/docs/content/docs/guides/faker.mdx +++ b/docs/content/docs/guides/faker.mdx @@ -142,6 +142,8 @@ export const getGetTenantsByRefResponseMock = ( Orval only extracts factories for shapes it can name and mock reliably. The following fall back to inline `.map()` bodies (same as `arrayItems: false`): `$ref` to scalar schemas, `oneOf` / `anyOf` item compositions, nullable object items, and nested arrays whose parent context is not the generated response wrapper (e.g. two `items` properties under `outer` and `inner` in the same operation). Plain object items, `$ref`-to-object items, and inline `allOf` items are supported. +Top-level array responses (the array itself, not a wrapper object) reuse the same generated element alias as the schema output — a `$ref`'d array schema `CatalogItems` produces item factories typed `CatalogItemsItem`, and an inline top-level array reuses its generated `Item` alias. Shapes where that alias cannot be derived with certainty (e.g. `$ref` array items composed via a multi-schema `allOf` with no direct properties) are inlined instead. + When `schemas: true` is also enabled, `$ref` items delegate to the consolidated schema factory instead (same as today). `arrayItems` is useful when item types only appear inside response wrappers or when you want item factories without emitting every `components/schemas` entry. With both options enabled, `$ref` items are not re-exported from the operation mock file — import `getMock` from `/index.faker.ts` instead. ## Options diff --git a/packages/mock/src/faker/getters/array-item-factory.test.ts b/packages/mock/src/faker/getters/array-item-factory.test.ts index 1709d19902..76ba2bb344 100644 --- a/packages/mock/src/faker/getters/array-item-factory.test.ts +++ b/packages/mock/src/faker/getters/array-item-factory.test.ts @@ -513,6 +513,253 @@ describe('extractArrayItemMock', () => { expect(splitMockImplementations).toHaveLength(1); }); + describe('top-level array responses (no parentName)', () => { + it('extracts a factory for a $ref array response, aliasing to ', () => { + const splitMockImplementations: string[] = []; + const imports: Parameters[0]['imports'] = []; + + const call = extractArrayItemMock({ + items: { + type: 'object', + properties: { + sku: { type: 'string' }, + price: { type: 'number' }, + }, + }, + propertyName: 'CatalogItems', + operationId: 'getCatalogItems', + tags: [], + mapValue: '{sku: faker.string.alpha(), price: faker.number.float()}', + context: createContextWithArrayItems(), + splitMockImplementations, + imports, + }); + + expect(call).toBe( + '{...getGetCatalogItemsResponseCatalogItemsItemMock()}', + ); + expect(splitMockImplementations).toHaveLength(1); + expect(splitMockImplementations[0]).toContain( + 'export const getGetCatalogItemsResponseCatalogItemsItemMock', + ); + expect(splitMockImplementations[0]).toContain( + 'Partial', + ); + expect(splitMockImplementations[0]).toContain('): CatalogItemsItem'); + expect(imports).toEqual([{ name: 'CatalogItemsItem' }]); + }); + + it('reproduces the reported shape: $ref array response named Items on operation getCatalogItemsShort', () => { + const splitMockImplementations: string[] = []; + const imports: Parameters[0]['imports'] = []; + + const call = extractArrayItemMock({ + items: { + type: 'object', + properties: { id: { type: 'string' } }, + }, + propertyName: 'Items', + operationId: 'getCatalogItemsShort', + tags: [], + mapValue: '{id: faker.string.uuid()}', + context: createContextWithArrayItems(), + splitMockImplementations, + imports, + }); + + expect(call).toBe('{...getGetCatalogItemsShortResponseItemsItemMock()}'); + expect(imports).toEqual([{ name: 'ItemsItem' }]); + expect(splitMockImplementations[0]).toContain('Partial'); + }); + + it('extracts a factory for an inline top-level array response, reusing the emitted element alias', () => { + const splitMockImplementations: string[] = []; + const imports: Parameters[0]['imports'] = []; + + const call = extractArrayItemMock({ + items: { + type: 'object', + properties: { + id: { type: 'string' }, + label: { type: 'string' }, + }, + }, + propertyName: 'GetCatalogItemsInline200Item[]', + operationId: 'getCatalogItemsInline', + tags: [], + mapValue: '{id: faker.string.uuid(), label: faker.string.alpha()}', + context: createContextWithArrayItems(), + splitMockImplementations, + imports, + }); + + expect(call).toBe( + '{...getGetCatalogItemsInlineResponseGetCatalogItemsInline200ItemItemMock()}', + ); + expect(imports).toEqual([{ name: 'GetCatalogItemsInline200Item' }]); + expect(splitMockImplementations[0]).toContain( + 'Partial', + ); + }); + + it('bails out (inlines) when the array-expression base is not a plain identifier', () => { + const splitMockImplementationsUnion: string[] = []; + const importsUnion: Parameters< + typeof extractArrayItemMock + >[0]['imports'] = []; + + const callUnion = extractArrayItemMock({ + items: { + oneOf: [{ type: 'object', properties: { a: { type: 'string' } } }], + }, + propertyName: '(Cat | Dog)[]', + operationId: 'getThings', + tags: [], + mapValue: '{a: faker.string.alpha()}', + context: createContextWithArrayItems(), + splitMockImplementations: splitMockImplementationsUnion, + imports: importsUnion, + }); + + expect(callUnion).toBeUndefined(); + expect(splitMockImplementationsUnion).toHaveLength(0); + expect(importsUnion).toHaveLength(0); + + const splitMockImplementationsReadonly: string[] = []; + const importsReadonly: Parameters< + typeof extractArrayItemMock + >[0]['imports'] = []; + + const callReadonly = extractArrayItemMock({ + items: { + type: 'object', + properties: { a: { type: 'string' } }, + }, + propertyName: 'readonly Foo[]', + operationId: 'getFoo', + tags: [], + mapValue: '{a: faker.string.alpha()}', + context: createContextWithArrayItems(), + splitMockImplementations: splitMockImplementationsReadonly, + imports: importsReadonly, + }); + + expect(callReadonly).toBeUndefined(); + expect(splitMockImplementationsReadonly).toHaveLength(0); + expect(importsReadonly).toHaveLength(0); + }); + + it('bails out (inlines) for a bare ref-name response whose items are a multi-ref allOf without direct properties', () => { + const splitMockImplementations: string[] = []; + const imports: Parameters[0]['imports'] = []; + + const call = extractArrayItemMock({ + items: { + allOf: [ + { $ref: '#/components/schemas/A' }, + { $ref: '#/components/schemas/B' }, + ], + }, + propertyName: 'ComposedItems', + operationId: 'getComposed', + tags: [], + mapValue: '{...getAMock(), ...getBMock()}', + context: createContextWithArrayItems(), + splitMockImplementations, + imports, + }); + + expect(call).toBeUndefined(); + expect(splitMockImplementations).toHaveLength(0); + expect(imports).toHaveLength(0); + }); + + describe('nullable top-level array responses', () => { + it('strips the " | null" suffix for an inline nullable array response, aliasing to ', () => { + const splitMockImplementations: string[] = []; + const imports: Parameters[0]['imports'] = + []; + + const call = extractArrayItemMock({ + items: { + type: 'object', + properties: { + sku: { type: 'string' }, + price: { type: 'number' }, + }, + }, + propertyName: 'CatalogItems | null', + operationId: 'getNullableCatalogItems', + tags: [], + mapValue: '{sku: faker.string.alpha(), price: faker.number.float()}', + context: createContextWithArrayItems(), + splitMockImplementations, + imports, + }); + + expect(call).toBe( + '{...getGetNullableCatalogItemsResponseCatalogItemsNullItemMock()}', + ); + expect(imports).toEqual([{ name: 'CatalogItemsItem' }]); + expect(splitMockImplementations[0]).toContain( + 'Partial', + ); + expect(splitMockImplementations[0]).toContain('): CatalogItemsItem'); + }); + + it('strips the " | null" suffix for a nullable $ref array response, reusing the emitted element alias', () => { + const splitMockImplementations: string[] = []; + const imports: Parameters[0]['imports'] = + []; + + const call = extractArrayItemMock({ + items: { + type: 'object', + properties: { + id: { type: 'string' }, + }, + }, + propertyName: 'GetFoo200Item[] | null', + operationId: 'getFoo', + tags: [], + mapValue: '{id: faker.string.uuid()}', + context: createContextWithArrayItems(), + splitMockImplementations, + imports, + }); + + expect(call).toBe('{...getGetFooResponseGetFoo200ItemNullItemMock()}'); + expect(imports).toEqual([{ name: 'GetFoo200Item' }]); + expect(splitMockImplementations[0]).toContain('Partial'); + expect(splitMockImplementations[0]).toContain('): GetFoo200Item'); + }); + + it('bails out (inlines) when the stripped bare-ref name is not a plain identifier', () => { + const splitMockImplementations: string[] = []; + const imports: Parameters[0]['imports'] = + []; + + const call = extractArrayItemMock({ + items: { + type: 'object', + properties: { a: { type: 'string' } }, + }, + propertyName: '(Cat | Dog) | null', + operationId: 'getThings', + tags: [], + mapValue: '{a: faker.string.alpha()}', + context: createContextWithArrayItems(), + splitMockImplementations, + imports, + }); + + expect(call).toBeUndefined(); + expect(splitMockImplementations).toHaveLength(0); + expect(imports).toHaveLength(0); + }); + }); + }); + it('skips $ref components/schemas items when schemas: true emits consolidated factories', () => { const splitMockImplementations: string[] = []; const contextWithSchemas = { diff --git a/packages/mock/src/faker/getters/array-item-factory.ts b/packages/mock/src/faker/getters/array-item-factory.ts index ad24bacc67..fbefbe830f 100644 --- a/packages/mock/src/faker/getters/array-item-factory.ts +++ b/packages/mock/src/faker/getters/array-item-factory.ts @@ -233,9 +233,60 @@ function getArrayItemFactoryNames({ } const itemSuffix = context.output.override.components.schemas.itemSuffix; - const typeName = parentName - ? `${pascal(parentName)}${pascal(propertyName)}${itemSuffix}` - : `${pascal(operationId)}${pascal(propertyName)}${itemSuffix}`; + + let typeName: string; + if (parentName) { + typeName = `${pascal(parentName)}${pascal(propertyName)}${itemSuffix}`; + } else { + // No `parentName`: the array IS the top-level response schema, and + // `propertyName` here is the response definition string produced by + // `getResReqTypes` (core/getters/res-req-types.ts) rather than a nested + // property key. Two shapes reach this point: + // - inline top-level array responses, where `propertyName` is the + // response type expression with a trailing `[]`; the part before + // `[]` is the element alias core already emitted via + // `createTypeAliasIfNeeded` (core/resolvers/object.ts), when that + // part is a bare identifier; + // - `$ref`'d array schemas (`items` here is the array's resolved, + // non-`$ref` items schema), where `propertyName` is the bare ref + // name and core aliases the array's items as + // `${pascal(refName)}${itemSuffix}` (core/getters/array.ts). + // Nullable top-level arrays reach this branch too: core's scalar getter + // appends a trailing ` | null` to either shape above (e.g. + // `CatalogItems | null` or `GetFoo200Item[] | null`), so that suffix is + // stripped before testing/deriving the type name below. `factoryName` + // still keys off the original, unstripped `propertyName` — outputs on + // the nullable path never compiled before this fix, so factory naming + // there is not a compatibility surface. + // If neither shape holds with certainty, bail (`undefined`) so the call + // site keeps the pre-#3514 inline item body, which is always + // type-correct, instead of referencing a name core never emitted (#3706). + const nullableSuffix = ' | null'; + const workingName = propertyName.endsWith(nullableSuffix) + ? propertyName.slice(0, -nullableSuffix.length) + : propertyName; + + if (workingName.endsWith('[]')) { + const base = workingName.slice(0, -2); + if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(base)) { + return undefined; + } + typeName = base; + } else { + const schema = items as OpenApiSchemaObject; + if (schema.allOf && !schema.properties && schema.type !== 'object') { + return undefined; + } + // Defense-in-depth: `workingName` should be a bare ref name here, but + // guard against anything that isn't a valid identifier (e.g. a + // malformed union expression) rather than emitting a phantom type. + if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(workingName)) { + return undefined; + } + typeName = `${pascal(workingName)}${itemSuffix}`; + } + } + return { factoryName: `get${pascal(operationId)}Response${pascal(propertyName)}ItemMock`, typeName, diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/endpoints.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/endpoints.ts new file mode 100644 index 0000000000..7bd74a7700 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/endpoints.ts @@ -0,0 +1,512 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ +import type { + CatalogItems, + GetCatalogItemsInline200Item, + GetNullableCatalogItems200Item, + GetNullableList200, + Items, +} from './model'; + +import { faker } from '@faker-js/faker'; + +import { HttpResponse, http } from 'msw'; +import type { RequestHandlerOptions } from 'msw'; + +import type { + CatalogItemsItem, + GetNullableList200RowsItem, + ItemsItem, +} from './model'; + +export type getCatalogItemsResponse200 = { + data: CatalogItems; + status: 200; +}; + +export type getCatalogItemsResponseSuccess = getCatalogItemsResponse200 & { + headers: Headers; +}; +export type getCatalogItemsResponse = getCatalogItemsResponseSuccess; + +export const getGetCatalogItemsUrl = () => { + return `/catalog-items`; +}; + +export const getCatalogItems = async ( + options?: RequestInit, +): Promise => { + const res = await fetch(getGetCatalogItemsUrl(), { + ...options, + method: 'GET', + }); + + const body = [204, 205, 304].includes(res.status) ? null : await res.text(); + + const data: getCatalogItemsResponse['data'] = body ? JSON.parse(body) : {}; + return { + data, + status: res.status, + headers: res.headers, + } as getCatalogItemsResponse; +}; + +export type getCatalogItemsInlineResponse200 = { + data: GetCatalogItemsInline200Item[]; + status: 200; +}; + +export type getCatalogItemsInlineResponseSuccess = + getCatalogItemsInlineResponse200 & { + headers: Headers; + }; +export type getCatalogItemsInlineResponse = + getCatalogItemsInlineResponseSuccess; + +export const getGetCatalogItemsInlineUrl = () => { + return `/catalog-items-inline`; +}; + +export const getCatalogItemsInline = async ( + options?: RequestInit, +): Promise => { + const res = await fetch(getGetCatalogItemsInlineUrl(), { + ...options, + method: 'GET', + }); + + const body = [204, 205, 304].includes(res.status) ? null : await res.text(); + + const data: getCatalogItemsInlineResponse['data'] = body + ? JSON.parse(body) + : {}; + return { + data, + status: res.status, + headers: res.headers, + } as getCatalogItemsInlineResponse; +}; + +export type getCatalogItemsShortResponse200 = { + data: Items; + status: 200; +}; + +export type getCatalogItemsShortResponseSuccess = + getCatalogItemsShortResponse200 & { + headers: Headers; + }; +export type getCatalogItemsShortResponse = getCatalogItemsShortResponseSuccess; + +export const getGetCatalogItemsShortUrl = () => { + return `/items`; +}; + +export const getCatalogItemsShort = async ( + options?: RequestInit, +): Promise => { + const res = await fetch(getGetCatalogItemsShortUrl(), { + ...options, + method: 'GET', + }); + + const body = [204, 205, 304].includes(res.status) ? null : await res.text(); + + const data: getCatalogItemsShortResponse['data'] = body + ? JSON.parse(body) + : {}; + return { + data, + status: res.status, + headers: res.headers, + } as getCatalogItemsShortResponse; +}; + +export type getNullableListResponse200 = { + data: GetNullableList200; + status: 200; +}; + +export type getNullableListResponseSuccess = getNullableListResponse200 & { + headers: Headers; +}; +export type getNullableListResponse = getNullableListResponseSuccess; + +export const getGetNullableListUrl = () => { + return `/nullable-list`; +}; + +export const getNullableList = async ( + options?: RequestInit, +): Promise => { + const res = await fetch(getGetNullableListUrl(), { + ...options, + method: 'GET', + }); + + const body = [204, 205, 304].includes(res.status) ? null : await res.text(); + + const data: getNullableListResponse['data'] = body ? JSON.parse(body) : {}; + return { + data, + status: res.status, + headers: res.headers, + } as getNullableListResponse; +}; + +export type getNullableCatalogItemsResponse200 = { + data: GetNullableCatalogItems200Item[] | null; + status: 200; +}; + +export type getNullableCatalogItemsResponseSuccess = + getNullableCatalogItemsResponse200 & { + headers: Headers; + }; +export type getNullableCatalogItemsResponse = + getNullableCatalogItemsResponseSuccess; + +export const getGetNullableCatalogItemsUrl = () => { + return `/nullable-catalog-items`; +}; + +export const getNullableCatalogItems = async ( + options?: RequestInit, +): Promise => { + const res = await fetch(getGetNullableCatalogItemsUrl(), { + ...options, + method: 'GET', + }); + + const body = [204, 205, 304].includes(res.status) ? null : await res.text(); + + const data: getNullableCatalogItemsResponse['data'] = body + ? JSON.parse(body) + : {}; + return { + data, + status: res.status, + headers: res.headers, + } as getNullableCatalogItemsResponse; +}; + +export type KeysWithNull = { + [K in keyof O]-?: null extends O[K] ? K : never; +}[keyof O]; + +export type MockWithNullableOverrides< + T, + O extends Partial, + M extends Record, +> = Omit, keyof T>> & { + [K in Extract, keyof T>]: M[K] | null; +}; + +export type CatalogItemsMock = CatalogItems; + +export type CatalogItemsItemMock = { + [K in keyof Required>]: NonNullable< + Required>[K] + >; +}; + +export type GetCatalogItemsInline200ItemMock = { + [K in keyof Required>]: NonNullable< + Required>[K] + >; +}; + +export type ItemsMock = Items; + +export type ItemsItemMock = { + [K in keyof Required>]: NonNullable< + Required>[K] + >; +}; + +export type GetNullableList200Mock = { + [K in keyof Required>]: NonNullable< + Required>[K] + >; +}; + +export type GetNullableList200RowsItemMock = { + [K in keyof Required>]: NonNullable< + Required>[K] + >; +}; + +export type GetNullableCatalogItems200ItemMock = GetNullableCatalogItems200Item; + +export const getGetCatalogItemsResponseCatalogItemsItemMock = < + O extends Partial = {}, +>( + overrideResponse?: O, +): MockWithNullableOverrides => + ({ + ...{ + sku: faker.string.alpha({ length: { min: 10, max: 20 } }), + price: faker.number.float({ fractionDigits: 2 }), + }, + ...overrideResponse, + }) as MockWithNullableOverrides; + +export const getGetCatalogItemsResponseMock = (): CatalogItemsMock => + Array.from( + { length: faker.number.int({ min: 1, max: 10 }) }, + (_, i) => i + 1, + ).map(() => ({ + ...(getGetCatalogItemsResponseCatalogItemsItemMock() as CatalogItemsItemMock), + })); + +export const getGetCatalogItemsInlineResponseGetCatalogItemsInline200ItemItemMock = + = {}>( + overrideResponse?: O, + ): MockWithNullableOverrides< + GetCatalogItemsInline200Item, + O, + GetCatalogItemsInline200ItemMock + > => + ({ + ...{ + id: faker.string.alpha({ length: { min: 10, max: 20 } }), + label: faker.string.alpha({ length: { min: 10, max: 20 } }), + }, + ...overrideResponse, + }) as MockWithNullableOverrides< + GetCatalogItemsInline200Item, + O, + GetCatalogItemsInline200ItemMock + >; + +export const getGetCatalogItemsInlineResponseMock = + (): GetCatalogItemsInline200ItemMock[] => + Array.from( + { length: faker.number.int({ min: 1, max: 10 }) }, + (_, i) => i + 1, + ).map(() => ({ + ...(getGetCatalogItemsInlineResponseGetCatalogItemsInline200ItemItemMock() as GetCatalogItemsInline200ItemMock), + })); + +export const getGetCatalogItemsShortResponseItemsItemMock = < + O extends Partial = {}, +>( + overrideResponse?: O, +): MockWithNullableOverrides => + ({ + ...{ id: faker.string.alpha({ length: { min: 10, max: 20 } }) }, + ...overrideResponse, + }) as MockWithNullableOverrides; + +export const getGetCatalogItemsShortResponseMock = (): ItemsMock => + Array.from( + { length: faker.number.int({ min: 1, max: 10 }) }, + (_, i) => i + 1, + ).map(() => ({ + ...(getGetCatalogItemsShortResponseItemsItemMock() as ItemsItemMock), + })); + +export const getGetNullableListResponseRowsItemMock = < + O extends Partial = {}, +>( + overrideResponse?: O, +): MockWithNullableOverrides< + GetNullableList200RowsItem, + O, + GetNullableList200RowsItemMock +> => + ({ + ...{ id: faker.string.alpha({ length: { min: 10, max: 20 } }) }, + ...overrideResponse, + }) as MockWithNullableOverrides< + GetNullableList200RowsItem, + O, + GetNullableList200RowsItemMock + >; + +export const getGetNullableListResponseMock = < + O extends Partial> = {}, +>( + overrideResponse?: O, +): MockWithNullableOverrides => + ({ + rows: Array.from( + { length: faker.number.int({ min: 1, max: 10 }) }, + (_, i) => i + 1, + ).map(() => ({ + ...(getGetNullableListResponseRowsItemMock() as GetNullableList200RowsItemMock), + })), + ...overrideResponse, + }) as MockWithNullableOverrides< + GetNullableList200, + O, + GetNullableList200Mock + >; + +export const getGetNullableCatalogItemsResponseGetNullableCatalogItems200ItemNullItemMock = + = {}>( + overrideResponse?: O, + ): MockWithNullableOverrides< + GetNullableCatalogItems200Item, + O, + GetNullableCatalogItems200ItemMock + > => + ({ + ...{ + id: faker.string.alpha({ length: { min: 10, max: 20 } }), + name: faker.string.alpha({ length: { min: 10, max: 20 } }), + }, + ...overrideResponse, + }) as MockWithNullableOverrides< + GetNullableCatalogItems200Item, + O, + GetNullableCatalogItems200ItemMock + >; + +export const getGetNullableCatalogItemsResponseMock = (): + | GetNullableCatalogItems200ItemMock[] + | null => + Array.from( + { length: faker.number.int({ min: 1, max: 10 }) }, + (_, i) => i + 1, + ).map(() => ({ + ...(getGetNullableCatalogItemsResponseGetNullableCatalogItems200ItemNullItemMock() as GetNullableCatalogItems200ItemMock), + })); + +export const getGetCatalogItemsMockHandler = ( + overrideResponse?: + | CatalogItems + | (( + info: Parameters[1]>[0], + ) => Promise | CatalogItems), + options?: RequestHandlerOptions, +) => { + return http.get( + '*/catalog-items', + async (info: Parameters[1]>[0]) => { + return HttpResponse.json( + overrideResponse !== undefined + ? typeof overrideResponse === 'function' + ? await overrideResponse(info) + : overrideResponse + : getGetCatalogItemsResponseMock(), + { status: 200 }, + ); + }, + options, + ); +}; + +export const getGetCatalogItemsInlineMockHandler = ( + overrideResponse?: + | GetCatalogItemsInline200Item[] + | (( + info: Parameters[1]>[0], + ) => + | Promise + | GetCatalogItemsInline200Item[]), + options?: RequestHandlerOptions, +) => { + return http.get( + '*/catalog-items-inline', + async (info: Parameters[1]>[0]) => { + return HttpResponse.json( + overrideResponse !== undefined + ? typeof overrideResponse === 'function' + ? await overrideResponse(info) + : overrideResponse + : getGetCatalogItemsInlineResponseMock(), + { status: 200 }, + ); + }, + options, + ); +}; + +export const getGetCatalogItemsShortMockHandler = ( + overrideResponse?: + | Items + | (( + info: Parameters[1]>[0], + ) => Promise | Items), + options?: RequestHandlerOptions, +) => { + return http.get( + '*/items', + async (info: Parameters[1]>[0]) => { + return HttpResponse.json( + overrideResponse !== undefined + ? typeof overrideResponse === 'function' + ? await overrideResponse(info) + : overrideResponse + : getGetCatalogItemsShortResponseMock(), + { status: 200 }, + ); + }, + options, + ); +}; + +export const getGetNullableListMockHandler = ( + overrideResponse?: + | GetNullableList200 + | (( + info: Parameters[1]>[0], + ) => Promise | GetNullableList200), + options?: RequestHandlerOptions, +) => { + return http.get( + '*/nullable-list', + async (info: Parameters[1]>[0]) => { + return HttpResponse.json( + overrideResponse !== undefined + ? typeof overrideResponse === 'function' + ? await overrideResponse(info) + : overrideResponse + : getGetNullableListResponseMock(), + { status: 200 }, + ); + }, + options, + ); +}; + +export const getGetNullableCatalogItemsMockHandler = ( + overrideResponse?: + | GetNullableCatalogItems200Item[] + | null + | (( + info: Parameters[1]>[0], + ) => + | Promise + | GetNullableCatalogItems200Item[] + | null), + options?: RequestHandlerOptions, +) => { + return http.get( + '*/nullable-catalog-items', + async (info: Parameters[1]>[0]) => { + return HttpResponse.json( + overrideResponse !== undefined + ? typeof overrideResponse === 'function' + ? await overrideResponse(info) + : overrideResponse + : getGetNullableCatalogItemsResponseMock(), + { status: 200 }, + ); + }, + options, + ); +}; +export const getMSWArrayItemFactoriesUnexportedTopLevelArrayItemAliasesMock = + () => [ + getGetCatalogItemsMockHandler(), + getGetCatalogItemsInlineMockHandler(), + getGetCatalogItemsShortMockHandler(), + getGetNullableListMockHandler(), + getGetNullableCatalogItemsMockHandler(), + ]; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/catalogItems.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/catalogItems.ts new file mode 100644 index 0000000000..a89a61252b --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/catalogItems.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ +import type { CatalogItemsItem } from './catalogItemsItem'; + +export type CatalogItems = CatalogItemsItem[]; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/catalogItemsItem.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/catalogItemsItem.ts new file mode 100644 index 0000000000..dfa3fe8619 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/catalogItemsItem.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ + +export type CatalogItemsItem = { + sku: string; + price: number; +}; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/getCatalogItemsInline200Item.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/getCatalogItemsInline200Item.ts new file mode 100644 index 0000000000..15eff89597 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/getCatalogItemsInline200Item.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ + +export type GetCatalogItemsInline200Item = { + id: string; + label: string; +}; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/getNullableCatalogItems200Item.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/getNullableCatalogItems200Item.ts new file mode 100644 index 0000000000..16a87de956 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/getNullableCatalogItems200Item.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ + +export type GetNullableCatalogItems200Item = { + id: string; + name: string; +}; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/getNullableList200.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/getNullableList200.ts new file mode 100644 index 0000000000..32f0d81db3 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/getNullableList200.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ +import type { GetNullableList200RowsItem } from './getNullableList200RowsItem'; + +export type GetNullableList200 = { + /** @nullable */ + rows: GetNullableList200RowsItem[] | null; +}; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/getNullableList200RowsItem.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/getNullableList200RowsItem.ts new file mode 100644 index 0000000000..239dfdff97 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/getNullableList200RowsItem.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ + +export type GetNullableList200RowsItem = { + id: string; +}; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/index.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/index.ts new file mode 100644 index 0000000000..9595c99927 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/index.ts @@ -0,0 +1,15 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ + +export * from './catalogItems'; +export * from './catalogItemsItem'; +export * from './getCatalogItemsInline200Item'; +export * from './getNullableCatalogItems200Item'; +export * from './getNullableList200'; +export * from './getNullableList200RowsItem'; +export * from './items'; +export * from './itemsItem'; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/items.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/items.ts new file mode 100644 index 0000000000..6b22c370b9 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/items.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ +import type { ItemsItem } from './itemsItem'; + +export type Items = ItemsItem[]; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/itemsItem.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/itemsItem.ts new file mode 100644 index 0000000000..cb1b43e321 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases-strict/model/itemsItem.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ + +export type ItemsItem = { + id: string; +}; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/endpoints.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/endpoints.ts new file mode 100644 index 0000000000..e4d046fc7a --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/endpoints.ts @@ -0,0 +1,419 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ +import type { + CatalogItems, + GetCatalogItemsInline200Item, + GetNullableCatalogItems200Item, + GetNullableList200, + Items, +} from './model'; + +import { faker } from '@faker-js/faker'; + +import { HttpResponse, http } from 'msw'; +import type { RequestHandlerOptions } from 'msw'; + +import type { + CatalogItemsItem, + GetNullableList200RowsItem, + ItemsItem, +} from './model'; + +export type getCatalogItemsResponse200 = { + data: CatalogItems; + status: 200; +}; + +export type getCatalogItemsResponseSuccess = getCatalogItemsResponse200 & { + headers: Headers; +}; +export type getCatalogItemsResponse = getCatalogItemsResponseSuccess; + +export const getGetCatalogItemsUrl = () => { + return `/catalog-items`; +}; + +export const getCatalogItems = async ( + options?: RequestInit, +): Promise => { + const res = await fetch(getGetCatalogItemsUrl(), { + ...options, + method: 'GET', + }); + + const body = [204, 205, 304].includes(res.status) ? null : await res.text(); + + const data: getCatalogItemsResponse['data'] = body ? JSON.parse(body) : {}; + return { + data, + status: res.status, + headers: res.headers, + } as getCatalogItemsResponse; +}; + +export type getCatalogItemsInlineResponse200 = { + data: GetCatalogItemsInline200Item[]; + status: 200; +}; + +export type getCatalogItemsInlineResponseSuccess = + getCatalogItemsInlineResponse200 & { + headers: Headers; + }; +export type getCatalogItemsInlineResponse = + getCatalogItemsInlineResponseSuccess; + +export const getGetCatalogItemsInlineUrl = () => { + return `/catalog-items-inline`; +}; + +export const getCatalogItemsInline = async ( + options?: RequestInit, +): Promise => { + const res = await fetch(getGetCatalogItemsInlineUrl(), { + ...options, + method: 'GET', + }); + + const body = [204, 205, 304].includes(res.status) ? null : await res.text(); + + const data: getCatalogItemsInlineResponse['data'] = body + ? JSON.parse(body) + : {}; + return { + data, + status: res.status, + headers: res.headers, + } as getCatalogItemsInlineResponse; +}; + +export type getCatalogItemsShortResponse200 = { + data: Items; + status: 200; +}; + +export type getCatalogItemsShortResponseSuccess = + getCatalogItemsShortResponse200 & { + headers: Headers; + }; +export type getCatalogItemsShortResponse = getCatalogItemsShortResponseSuccess; + +export const getGetCatalogItemsShortUrl = () => { + return `/items`; +}; + +export const getCatalogItemsShort = async ( + options?: RequestInit, +): Promise => { + const res = await fetch(getGetCatalogItemsShortUrl(), { + ...options, + method: 'GET', + }); + + const body = [204, 205, 304].includes(res.status) ? null : await res.text(); + + const data: getCatalogItemsShortResponse['data'] = body + ? JSON.parse(body) + : {}; + return { + data, + status: res.status, + headers: res.headers, + } as getCatalogItemsShortResponse; +}; + +export type getNullableListResponse200 = { + data: GetNullableList200; + status: 200; +}; + +export type getNullableListResponseSuccess = getNullableListResponse200 & { + headers: Headers; +}; +export type getNullableListResponse = getNullableListResponseSuccess; + +export const getGetNullableListUrl = () => { + return `/nullable-list`; +}; + +export const getNullableList = async ( + options?: RequestInit, +): Promise => { + const res = await fetch(getGetNullableListUrl(), { + ...options, + method: 'GET', + }); + + const body = [204, 205, 304].includes(res.status) ? null : await res.text(); + + const data: getNullableListResponse['data'] = body ? JSON.parse(body) : {}; + return { + data, + status: res.status, + headers: res.headers, + } as getNullableListResponse; +}; + +export type getNullableCatalogItemsResponse200 = { + data: GetNullableCatalogItems200Item[] | null; + status: 200; +}; + +export type getNullableCatalogItemsResponseSuccess = + getNullableCatalogItemsResponse200 & { + headers: Headers; + }; +export type getNullableCatalogItemsResponse = + getNullableCatalogItemsResponseSuccess; + +export const getGetNullableCatalogItemsUrl = () => { + return `/nullable-catalog-items`; +}; + +export const getNullableCatalogItems = async ( + options?: RequestInit, +): Promise => { + const res = await fetch(getGetNullableCatalogItemsUrl(), { + ...options, + method: 'GET', + }); + + const body = [204, 205, 304].includes(res.status) ? null : await res.text(); + + const data: getNullableCatalogItemsResponse['data'] = body + ? JSON.parse(body) + : {}; + return { + data, + status: res.status, + headers: res.headers, + } as getNullableCatalogItemsResponse; +}; + +export const getGetCatalogItemsResponseCatalogItemsItemMock = ( + overrideResponse: Partial = {}, +): CatalogItemsItem => ({ + ...{ + sku: faker.string.alpha({ length: { min: 10, max: 20 } }), + price: faker.number.float({ fractionDigits: 2 }), + }, + ...overrideResponse, +}); + +export const getGetCatalogItemsResponseMock = (): CatalogItems => + Array.from( + { length: faker.number.int({ min: 1, max: 10 }) }, + (_, i) => i + 1, + ).map(() => ({ ...getGetCatalogItemsResponseCatalogItemsItemMock() })); + +export const getGetCatalogItemsInlineResponseGetCatalogItemsInline200ItemItemMock = + ( + overrideResponse: Partial = {}, + ): GetCatalogItemsInline200Item => ({ + ...{ + id: faker.string.alpha({ length: { min: 10, max: 20 } }), + label: faker.string.alpha({ length: { min: 10, max: 20 } }), + }, + ...overrideResponse, + }); + +export const getGetCatalogItemsInlineResponseMock = + (): GetCatalogItemsInline200Item[] => + Array.from( + { length: faker.number.int({ min: 1, max: 10 }) }, + (_, i) => i + 1, + ).map(() => ({ + ...getGetCatalogItemsInlineResponseGetCatalogItemsInline200ItemItemMock(), + })); + +export const getGetCatalogItemsShortResponseItemsItemMock = ( + overrideResponse: Partial = {}, +): ItemsItem => ({ + ...{ id: faker.string.alpha({ length: { min: 10, max: 20 } }) }, + ...overrideResponse, +}); + +export const getGetCatalogItemsShortResponseMock = (): Items => + Array.from( + { length: faker.number.int({ min: 1, max: 10 }) }, + (_, i) => i + 1, + ).map(() => ({ ...getGetCatalogItemsShortResponseItemsItemMock() })); + +export const getGetNullableListResponseRowsItemMock = ( + overrideResponse: Partial = {}, +): GetNullableList200RowsItem => ({ + ...{ id: faker.string.alpha({ length: { min: 10, max: 20 } }) }, + ...overrideResponse, +}); + +export const getGetNullableListResponseMock = ( + overrideResponse: Partial> = {}, +): GetNullableList200 => ({ + rows: faker.helpers.arrayElement([ + Array.from( + { length: faker.number.int({ min: 1, max: 10 }) }, + (_, i) => i + 1, + ).map(() => ({ ...getGetNullableListResponseRowsItemMock() })), + null, + ]), + ...overrideResponse, +}); + +export const getGetNullableCatalogItemsResponseGetNullableCatalogItems200ItemNullItemMock = + ( + overrideResponse: Partial = {}, + ): GetNullableCatalogItems200Item => ({ + ...{ + id: faker.string.alpha({ length: { min: 10, max: 20 } }), + name: faker.string.alpha({ length: { min: 10, max: 20 } }), + }, + ...overrideResponse, + }); + +export const getGetNullableCatalogItemsResponseMock = (): + | GetNullableCatalogItems200Item[] + | null => + Array.from( + { length: faker.number.int({ min: 1, max: 10 }) }, + (_, i) => i + 1, + ).map(() => ({ + ...getGetNullableCatalogItemsResponseGetNullableCatalogItems200ItemNullItemMock(), + })); + +export const getGetCatalogItemsMockHandler = ( + overrideResponse?: + | CatalogItems + | (( + info: Parameters[1]>[0], + ) => Promise | CatalogItems), + options?: RequestHandlerOptions, +) => { + return http.get( + '*/catalog-items', + async (info: Parameters[1]>[0]) => { + return HttpResponse.json( + overrideResponse !== undefined + ? typeof overrideResponse === 'function' + ? await overrideResponse(info) + : overrideResponse + : getGetCatalogItemsResponseMock(), + { status: 200 }, + ); + }, + options, + ); +}; + +export const getGetCatalogItemsInlineMockHandler = ( + overrideResponse?: + | GetCatalogItemsInline200Item[] + | (( + info: Parameters[1]>[0], + ) => + | Promise + | GetCatalogItemsInline200Item[]), + options?: RequestHandlerOptions, +) => { + return http.get( + '*/catalog-items-inline', + async (info: Parameters[1]>[0]) => { + return HttpResponse.json( + overrideResponse !== undefined + ? typeof overrideResponse === 'function' + ? await overrideResponse(info) + : overrideResponse + : getGetCatalogItemsInlineResponseMock(), + { status: 200 }, + ); + }, + options, + ); +}; + +export const getGetCatalogItemsShortMockHandler = ( + overrideResponse?: + | Items + | (( + info: Parameters[1]>[0], + ) => Promise | Items), + options?: RequestHandlerOptions, +) => { + return http.get( + '*/items', + async (info: Parameters[1]>[0]) => { + return HttpResponse.json( + overrideResponse !== undefined + ? typeof overrideResponse === 'function' + ? await overrideResponse(info) + : overrideResponse + : getGetCatalogItemsShortResponseMock(), + { status: 200 }, + ); + }, + options, + ); +}; + +export const getGetNullableListMockHandler = ( + overrideResponse?: + | GetNullableList200 + | (( + info: Parameters[1]>[0], + ) => Promise | GetNullableList200), + options?: RequestHandlerOptions, +) => { + return http.get( + '*/nullable-list', + async (info: Parameters[1]>[0]) => { + return HttpResponse.json( + overrideResponse !== undefined + ? typeof overrideResponse === 'function' + ? await overrideResponse(info) + : overrideResponse + : getGetNullableListResponseMock(), + { status: 200 }, + ); + }, + options, + ); +}; + +export const getGetNullableCatalogItemsMockHandler = ( + overrideResponse?: + | GetNullableCatalogItems200Item[] + | null + | (( + info: Parameters[1]>[0], + ) => + | Promise + | GetNullableCatalogItems200Item[] + | null), + options?: RequestHandlerOptions, +) => { + return http.get( + '*/nullable-catalog-items', + async (info: Parameters[1]>[0]) => { + return HttpResponse.json( + overrideResponse !== undefined + ? typeof overrideResponse === 'function' + ? await overrideResponse(info) + : overrideResponse + : getGetNullableCatalogItemsResponseMock(), + { status: 200 }, + ); + }, + options, + ); +}; +export const getMSWArrayItemFactoriesUnexportedTopLevelArrayItemAliasesMock = + () => [ + getGetCatalogItemsMockHandler(), + getGetCatalogItemsInlineMockHandler(), + getGetCatalogItemsShortMockHandler(), + getGetNullableListMockHandler(), + getGetNullableCatalogItemsMockHandler(), + ]; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/catalogItems.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/catalogItems.ts new file mode 100644 index 0000000000..a89a61252b --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/catalogItems.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ +import type { CatalogItemsItem } from './catalogItemsItem'; + +export type CatalogItems = CatalogItemsItem[]; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/catalogItemsItem.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/catalogItemsItem.ts new file mode 100644 index 0000000000..dfa3fe8619 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/catalogItemsItem.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ + +export type CatalogItemsItem = { + sku: string; + price: number; +}; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/getCatalogItemsInline200Item.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/getCatalogItemsInline200Item.ts new file mode 100644 index 0000000000..15eff89597 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/getCatalogItemsInline200Item.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ + +export type GetCatalogItemsInline200Item = { + id: string; + label: string; +}; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/getNullableCatalogItems200Item.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/getNullableCatalogItems200Item.ts new file mode 100644 index 0000000000..16a87de956 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/getNullableCatalogItems200Item.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ + +export type GetNullableCatalogItems200Item = { + id: string; + name: string; +}; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/getNullableList200.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/getNullableList200.ts new file mode 100644 index 0000000000..32f0d81db3 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/getNullableList200.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ +import type { GetNullableList200RowsItem } from './getNullableList200RowsItem'; + +export type GetNullableList200 = { + /** @nullable */ + rows: GetNullableList200RowsItem[] | null; +}; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/getNullableList200RowsItem.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/getNullableList200RowsItem.ts new file mode 100644 index 0000000000..239dfdff97 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/getNullableList200RowsItem.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ + +export type GetNullableList200RowsItem = { + id: string; +}; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/index.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/index.ts new file mode 100644 index 0000000000..9595c99927 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/index.ts @@ -0,0 +1,15 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ + +export * from './catalogItems'; +export * from './catalogItemsItem'; +export * from './getCatalogItemsInline200Item'; +export * from './getNullableCatalogItems200Item'; +export * from './getNullableList200'; +export * from './getNullableList200RowsItem'; +export * from './items'; +export * from './itemsItem'; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/items.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/items.ts new file mode 100644 index 0000000000..6b22c370b9 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/items.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ +import type { ItemsItem } from './itemsItem'; + +export type Items = ItemsItem[]; diff --git a/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/itemsItem.ts b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/itemsItem.ts new file mode 100644 index 0000000000..cb1b43e321 --- /dev/null +++ b/tests/__snapshots__/mock/issue-3706-msw-array-item-aliases/model/itemsItem.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v8.20.0 🍺 + * Do not edit manually. + * MSW array item factories - unexported top-level array item aliases + * OpenAPI spec version: 1.0.0 + */ + +export type ItemsItem = { + id: string; +}; diff --git a/tests/configs/mock.config.ts b/tests/configs/mock.config.ts index b51c3f7d54..aaf22e0353 100644 --- a/tests/configs/mock.config.ts +++ b/tests/configs/mock.config.ts @@ -853,6 +853,43 @@ export default defineConfig({ target: '../specifications/msw-array-items.yaml', }, }, + issue3706MswArrayItemAliases: { + output: { + target: '../generated/mock/issue-3706-msw-array-item-aliases/endpoints.ts', + schemas: '../generated/mock/issue-3706-msw-array-item-aliases/model', + client: 'fetch', + mock: { + generators: [{ type: 'msw', arrayItems: true, delay: false }], + }, + clean: true, + formatter: 'prettier', + }, + input: { + target: '../specifications/issue-3706-msw-unexported-item-aliases.yaml', + }, + }, + issue3706MswArrayItemAliasesStrict: { + output: { + target: + '../generated/mock/issue-3706-msw-array-item-aliases-strict/endpoints.ts', + schemas: '../generated/mock/issue-3706-msw-array-item-aliases-strict/model', + client: 'fetch', + mock: { + generators: [{ type: 'msw', arrayItems: true, delay: false }], + }, + override: { + mock: { + required: true, + nonNullable: true, + }, + }, + clean: true, + formatter: 'prettier', + }, + input: { + target: '../specifications/issue-3706-msw-unexported-item-aliases.yaml', + }, + }, issue3574StrictMockTagsSplitAngular: { output: { target: diff --git a/tests/specifications/issue-3706-msw-unexported-item-aliases.yaml b/tests/specifications/issue-3706-msw-unexported-item-aliases.yaml new file mode 100644 index 0000000000..63ec2871f4 --- /dev/null +++ b/tests/specifications/issue-3706-msw-unexported-item-aliases.yaml @@ -0,0 +1,122 @@ +openapi: 3.0.3 +info: + title: MSW array item factories - unexported top-level array item aliases + version: 1.0.0 +paths: + /catalog-items: + get: + operationId: getCatalogItems + tags: + - catalog + responses: + '200': + description: List catalog items ($ref array response) + content: + application/json: + schema: + $ref: '#/components/schemas/CatalogItems' + /catalog-items-inline: + get: + operationId: getCatalogItemsInline + tags: + - catalog + responses: + '200': + description: List catalog items (inline top-level array response) + content: + application/json: + schema: + type: array + items: + type: object + required: + - id + - label + properties: + id: + type: string + label: + type: string + /items: + get: + operationId: getCatalogItemsShort + tags: + - catalog + responses: + '200': + description: List items ($ref array response, reproduces the reported shape) + content: + application/json: + schema: + $ref: '#/components/schemas/Items' + /nullable-list: + get: + operationId: getNullableList + tags: + - catalog + responses: + '200': + description: Object wrapping a nullable array property (parentName-path regression guard) + content: + application/json: + schema: + type: object + required: + - rows + properties: + rows: + type: array + nullable: true + items: + type: object + required: + - id + properties: + id: + type: string + /nullable-catalog-items: + get: + operationId: getNullableCatalogItems + tags: + - catalog + responses: + '200': + description: List catalog items (nullable top-level array response) + content: + application/json: + schema: + type: array + nullable: true + items: + type: object + required: + - id + - name + properties: + id: + type: string + name: + type: string +components: + schemas: + CatalogItems: + type: array + items: + type: object + required: + - sku + - price + properties: + sku: + type: string + price: + type: number + Items: + type: array + items: + type: object + required: + - id + properties: + id: + type: string