e2e интеграционные тесты

This commit is contained in:
2025-10-28 10:51:14 +03:00
parent 6bffe6a9e1
commit 4e2d0f03de
9 changed files with 191 additions and 85 deletions

View File

@@ -15,6 +15,7 @@ program
.requiredOption('-i, --input <path>', 'Path to OpenAPI specification file (JSON or YAML)')
.requiredOption('-o, --output <path>', 'Output directory for generated files')
.option('-n, --name <name>', 'Name of generated file (without extension)')
.option('--swr', 'Generate SWR hooks for React')
.action(async (options) => {
try {
// Создание конфигурации
@@ -22,6 +23,7 @@ program
inputPath: options.input,
outputPath: options.output,
fileName: options.name,
useSwr: options.swr || false,
};
// Валидация конфигурации
@@ -38,7 +40,7 @@ program
// Генерация API
await generate(config as GeneratorConfig);
console.log(chalk.green('\n✨ Done!\n'));
console.log(chalk.green('\n✨ API client generated successfully!\n'));
} catch (error) {
console.error(chalk.red('\n❌ Error:'), error instanceof Error ? error.message : error);
console.error();

View File

@@ -8,6 +8,8 @@ export interface GeneratorConfig {
outputPath: string;
/** Имя сгенерированного файла (без расширения) */
fileName?: string;
/** Генерировать SWR hooks для React */
useSwr?: boolean;
}
/**

View File

@@ -18,25 +18,34 @@ export async function generate(config: GeneratorConfig): Promise<void> {
// Путь к кастомным шаблонам
const templatesPath = resolve(__dirname, '../src/templates');
// Читаем OpenAPI спецификацию
const inputPath = config.inputPath.startsWith('http://') || config.inputPath.startsWith('https://')
? config.inputPath
: resolve(config.inputPath);
const spec = await readJsonFile<any>(inputPath);
// Проверяем тип входного пути
const isUrl = config.inputPath.startsWith('http://') || config.inputPath.startsWith('https://');
// Для локальных файлов читаем спецификацию
let spec: any = null;
let inputPath: string | undefined = undefined;
let url: string | undefined = undefined;
if (isUrl) {
url = config.inputPath;
// Для URL не читаем спецификацию заранее, swagger-typescript-api сделает это сам
} else {
inputPath = resolve(config.inputPath);
spec = await readJsonFile<any>(inputPath);
}
// Определяем имя файла
let fileName = config.fileName;
if (!fileName) {
// Пытаемся получить имя из OpenAPI спецификации
fileName = spec.info?.title
fileName = spec?.info?.title
? spec.info.title.replace(/[^a-zA-Z0-9]/g, '')
: 'Api';
}
try {
await swaggerGenerateApi({
input: inputPath,
...(isUrl ? { url } : { input: inputPath }),
output: resolve(config.outputPath),
fileName: `${fileName}.ts`,
httpClientType: 'fetch',
@@ -60,7 +69,7 @@ export async function generate(config: GeneratorConfig): Promise<void> {
sortRoutes: false,
extractResponseError: false,
fixInvalidEnumKeyPrefix: 'KEY',
silent: false,
silent: true,
defaultResponseType: 'void',
typePrefix: '',
typeSuffix: '',
@@ -90,18 +99,18 @@ export async function generate(config: GeneratorConfig): Promise<void> {
},
onInit: (configuration) => {
// Получаем дефолтный baseUrl из OpenAPI спецификации
const defaultBaseUrl = spec.servers?.[0]?.url || '';
const apiConfig = (configuration as any).apiConfig || {};
const defaultBaseUrl = spec?.servers?.[0]?.url || apiConfig.baseUrl || '';
(configuration as any).apiConfig = (configuration as any).apiConfig || {};
(configuration as any).apiConfig.baseUrl = defaultBaseUrl;
// Передаем флаг useSwr в шаблоны
(configuration as any).useSwr = config.useSwr || false;
return configuration;
},
},
});
console.log(`Generated files in ${config.outputPath}:`);
console.log(` - ${fileName}.ts (API endpoints)`);
console.log(' - http-client.ts (HTTP client)');
console.log(' - data-contracts.ts (TypeScript types)');
// Генерация успешна
} catch (error) {
console.error('❌ Generation failed:', error);
throw error;

View File

@@ -26,8 +26,9 @@ const descriptionLines = _.compact([
%>
<% if (config.useSwr) { %>
import useSWR from "swr";
import { fetcher } from "./http-client";
<% } %>
<% if (config.httpClientType === config.constants.HTTP_CLIENT.AXIOS) { %> import type { AxiosRequestConfig, AxiosResponse } from "axios"; <% } %>
@@ -44,8 +45,8 @@ export class <%~ config.apiClassName %><SecurityDataType extends unknown><% if (
<% if(config.singleHttpClient) { %>
http: HttpClient<SecurityDataType>;
constructor (http: HttpClient<SecurityDataType>) {
this.http = http;
constructor (http?: HttpClient<SecurityDataType>) {
this.http = http || new HttpClient<SecurityDataType>();
}
<% } %>

View File

@@ -102,9 +102,9 @@ const isValidIdentifier = (name) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
...<%~ _.get(requestConfigParam, "name") %>,
})<%~ route.namespace ? ',' : '' %>
<%
// Генерируем use* функцию для GET запросов
// Генерируем use* функцию для GET запросов (только если включен флаг useSwr)
const isGetRequest = _.upperCase(method) === 'GET';
if (isGetRequest) {
if (config.useSwr && isGetRequest) {
const useMethodName = 'use' + _.upperFirst(route.routeName.usage);
const argsWithoutParams = rawWrapperArgs.filter(arg => arg.name !== requestConfigParam.name);
const useWrapperArgs = _