feat: добавить split-режим генерации REST-клиента
- добавлен режим генерации single, split и both - добавлены отдельные operation-файлы и createApiClient - удалена генерация SWR-хуков и зависимости React/SWR - обновлены CLI, шаблоны, примеры, документация и тесты - версия пакета повышена до 3.0.0
This commit is contained in:
@@ -49,23 +49,28 @@ describe('E2E Generation', () => {
|
||||
FIXTURES.VALID,
|
||||
'--output',
|
||||
outputPath,
|
||||
'--name',
|
||||
'TestApi',
|
||||
'--mode',
|
||||
'split',
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
|
||||
// 2. Проверка создания файла
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const generatedFile = join(outputPath, 'index.ts');
|
||||
const exists = await fileExists(generatedFile);
|
||||
expect(exists).toBe(true);
|
||||
|
||||
// 3. Проверка импорта (компиляция TypeScript)
|
||||
const testFile = join(tempDir, 'test-import.ts');
|
||||
const testCode = `
|
||||
import { Api } from '${generatedFile}';
|
||||
|
||||
const api = new Api();
|
||||
import { createApiClient, HttpClient } from '${generatedFile}';
|
||||
import { getAll } from '${join(outputPath, 'operations', 'get-all.ts')}';
|
||||
|
||||
const api = createApiClient(new HttpClient(), {
|
||||
users: {
|
||||
getAll,
|
||||
},
|
||||
});
|
||||
console.log('Import successful');
|
||||
`;
|
||||
|
||||
@@ -97,24 +102,7 @@ describe('E2E Generation', () => {
|
||||
|
||||
test('повторная генерация (перезапись файлов)', async () => {
|
||||
const outputPath = join(tempDir, 'output');
|
||||
const fileName = 'TestApi';
|
||||
|
||||
// Первая генерация
|
||||
await execa('bun', [
|
||||
'run',
|
||||
CLI_PATH,
|
||||
'--input',
|
||||
FIXTURES.MINIMAL,
|
||||
'--output',
|
||||
outputPath,
|
||||
'--name',
|
||||
fileName,
|
||||
]);
|
||||
|
||||
const generatedFile = join(outputPath, `${fileName}.ts`);
|
||||
const firstContent = await readTextFile(generatedFile);
|
||||
|
||||
// Вторая генерация (перезапись)
|
||||
// Первая генерация с endpoints
|
||||
await execa('bun', [
|
||||
'run',
|
||||
CLI_PATH,
|
||||
@@ -122,8 +110,25 @@ describe('E2E Generation', () => {
|
||||
FIXTURES.VALID,
|
||||
'--output',
|
||||
outputPath,
|
||||
'--name',
|
||||
fileName,
|
||||
'--mode',
|
||||
'split',
|
||||
]);
|
||||
|
||||
const generatedFile = join(outputPath, 'operations', 'index.ts');
|
||||
const staleOperationFile = join(outputPath, 'operations', 'get-all.ts');
|
||||
const firstContent = await readTextFile(generatedFile);
|
||||
expect(await fileExists(staleOperationFile)).toBe(true);
|
||||
|
||||
// Вторая генерация (перезапись меньшей схемой)
|
||||
await execa('bun', [
|
||||
'run',
|
||||
CLI_PATH,
|
||||
'--input',
|
||||
FIXTURES.MINIMAL,
|
||||
'--output',
|
||||
outputPath,
|
||||
'--mode',
|
||||
'split',
|
||||
]);
|
||||
|
||||
const secondContent = await readTextFile(generatedFile);
|
||||
@@ -134,6 +139,7 @@ describe('E2E Generation', () => {
|
||||
// Файл должен существовать
|
||||
const exists = await fileExists(generatedFile);
|
||||
expect(exists).toBe(true);
|
||||
expect(await fileExists(staleOperationFile)).toBe(false);
|
||||
}, 60000);
|
||||
|
||||
test('генерация из HTTP URL', async () => {
|
||||
@@ -147,50 +153,41 @@ describe('E2E Generation', () => {
|
||||
'https://petstore3.swagger.io/api/v3/openapi.json',
|
||||
'--output',
|
||||
outputPath,
|
||||
'--name',
|
||||
'PetStore',
|
||||
'--mode',
|
||||
'split',
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
|
||||
const generatedFile = join(outputPath, 'PetStore.ts');
|
||||
const generatedFile = join(outputPath, 'index.ts');
|
||||
const exists = await fileExists(generatedFile);
|
||||
expect(exists).toBe(true);
|
||||
|
||||
// Проверяем что файл не пустой
|
||||
const content = await readTextFile(generatedFile);
|
||||
const content = await readTextFile(join(outputPath, 'http-client.ts'));
|
||||
expect(content.length).toBeGreaterThan(1000);
|
||||
}, 60000);
|
||||
|
||||
test('генерация с флагом --swr', async () => {
|
||||
test('флаг --swr больше не поддерживается', async () => {
|
||||
const outputPath = join(tempDir, 'output');
|
||||
|
||||
const { exitCode } = await execa('bun', [
|
||||
'run',
|
||||
CLI_PATH,
|
||||
'--input',
|
||||
FIXTURES.VALID,
|
||||
'--output',
|
||||
outputPath,
|
||||
'--name',
|
||||
'SwrApi',
|
||||
'--swr',
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
|
||||
const generatedFile = join(outputPath, 'SwrApi.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем наличие импорта useSWR
|
||||
expect(content).toContain('import useSWR from "swr"');
|
||||
|
||||
// Проверяем наличие use* хуков для GET запросов
|
||||
expect(content).toContain('useGetAll');
|
||||
expect(content).toContain('useGetById');
|
||||
try {
|
||||
await execa('bun', [
|
||||
'run',
|
||||
CLI_PATH,
|
||||
'--input',
|
||||
FIXTURES.VALID,
|
||||
'--output',
|
||||
outputPath,
|
||||
'--swr',
|
||||
]);
|
||||
throw new Error('Should have thrown');
|
||||
} catch (error: any) {
|
||||
expect(error.exitCode).not.toBe(0);
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
test('генерация без флага --swr не содержит хуки', async () => {
|
||||
test('REST генерация не содержит SWR хуки', async () => {
|
||||
const outputPath = join(tempDir, 'output');
|
||||
|
||||
const { exitCode } = await execa('bun', [
|
||||
@@ -200,13 +197,13 @@ describe('E2E Generation', () => {
|
||||
FIXTURES.VALID,
|
||||
'--output',
|
||||
outputPath,
|
||||
'--name',
|
||||
'NoSwrApi',
|
||||
'--mode',
|
||||
'split',
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
|
||||
const generatedFile = join(outputPath, 'NoSwrApi.ts');
|
||||
const generatedFile = join(outputPath, 'http-client.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем отсутствие импорта useSWR
|
||||
@@ -229,16 +226,21 @@ describe('E2E Generation', () => {
|
||||
FIXTURES.VALID,
|
||||
'--output',
|
||||
outputPath,
|
||||
'--name',
|
||||
'TestApi',
|
||||
'--mode',
|
||||
'split',
|
||||
]);
|
||||
|
||||
// Динамически импортируем сгенерированный API
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const { Api } = await import(generatedFile);
|
||||
|
||||
const api = new Api();
|
||||
const result = await api.users.getAll();
|
||||
const generatedFile = join(outputPath, 'index.ts');
|
||||
const { createApiClient, HttpClient } = await import(generatedFile);
|
||||
const { getAll } = await import(join(outputPath, 'operations', 'get-all.ts'));
|
||||
|
||||
const api = createApiClient(new HttpClient(), {
|
||||
users: {
|
||||
getAll,
|
||||
},
|
||||
});
|
||||
const result = await api.users.getAll({});
|
||||
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
expect(result.length).toBe(2);
|
||||
@@ -254,15 +256,20 @@ describe('E2E Generation', () => {
|
||||
FIXTURES.VALID,
|
||||
'--output',
|
||||
outputPath,
|
||||
'--name',
|
||||
'TestApi',
|
||||
'--mode',
|
||||
'split',
|
||||
]);
|
||||
|
||||
// Динамически импортируем сгенерированный API
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const { Api } = await import(generatedFile);
|
||||
|
||||
const api = new Api();
|
||||
const generatedFile = join(outputPath, 'index.ts');
|
||||
const { createApiClient, HttpClient } = await import(generatedFile);
|
||||
const { create } = await import(join(outputPath, 'operations', 'create.ts'));
|
||||
|
||||
const api = createApiClient(new HttpClient(), {
|
||||
users: {
|
||||
create,
|
||||
},
|
||||
});
|
||||
const result = await api.users.create({
|
||||
email: 'new@example.com',
|
||||
password: 'password123'
|
||||
@@ -282,17 +289,22 @@ describe('E2E Generation', () => {
|
||||
FIXTURES.VALID,
|
||||
'--output',
|
||||
outputPath,
|
||||
'--name',
|
||||
'TestApi',
|
||||
'--mode',
|
||||
'split',
|
||||
]);
|
||||
|
||||
const testFile = join(tempDir, 'test-404.ts');
|
||||
const testCode = `
|
||||
import { Api } from '${join(outputPath, 'TestApi.ts')}';
|
||||
|
||||
const api = new Api();
|
||||
import { createApiClient, HttpClient } from '${join(outputPath, 'index.ts')}';
|
||||
import { getById } from '${join(outputPath, 'operations', 'get-by-id.ts')}';
|
||||
|
||||
const api = createApiClient(new HttpClient(), {
|
||||
users: {
|
||||
getById,
|
||||
},
|
||||
});
|
||||
try {
|
||||
await api.users.getById('999');
|
||||
await api.users.getById({ id: '999' });
|
||||
} catch (error) {
|
||||
console.log('error');
|
||||
}
|
||||
@@ -314,14 +326,16 @@ describe('E2E Generation', () => {
|
||||
FIXTURES.WITH_AUTH,
|
||||
'--output',
|
||||
outputPath,
|
||||
'--name',
|
||||
'AuthApi',
|
||||
'--mode',
|
||||
'split',
|
||||
]);
|
||||
|
||||
// Динамически импортируем сгенерированный API
|
||||
const generatedFile = join(outputPath, 'AuthApi.ts');
|
||||
const { Api, HttpClient } = await import(generatedFile);
|
||||
|
||||
const generatedFile = join(outputPath, 'index.ts');
|
||||
const { createApiClient, HttpClient } = await import(generatedFile);
|
||||
const { login } = await import(join(outputPath, 'operations', 'login.ts'));
|
||||
const { get } = await import(join(outputPath, 'operations', 'get.ts'));
|
||||
|
||||
// Создаем HttpClient с securityWorker для добавления Bearer токена
|
||||
const httpClient = new HttpClient({
|
||||
securityWorker: (securityData: string | null) => {
|
||||
@@ -334,8 +348,15 @@ describe('E2E Generation', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const api = new Api(httpClient);
|
||||
|
||||
const api = createApiClient(httpClient, {
|
||||
auth: {
|
||||
login,
|
||||
},
|
||||
profile: {
|
||||
get,
|
||||
},
|
||||
});
|
||||
|
||||
// Логин
|
||||
const loginResult = await api.auth.login({
|
||||
@@ -352,4 +373,4 @@ describe('E2E Generation', () => {
|
||||
expect(profile.email).toBe('test@example.com');
|
||||
}, 60000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,11 +28,12 @@ describe('Generated Client', () => {
|
||||
inputPath: FIXTURES.VALID,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const generatedFile = join(outputPath, 'index.ts');
|
||||
|
||||
// Пытаемся скомпилировать
|
||||
const { exitCode } = await execa('bun', ['build', generatedFile, '--outdir', tempDir]);
|
||||
@@ -46,11 +47,12 @@ describe('Generated Client', () => {
|
||||
inputPath: FIXTURES.VALID,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const generatedFile = join(outputPath, 'index.ts');
|
||||
|
||||
// Проверяем с помощью TypeScript компилятора
|
||||
const { exitCode, stderr } = await execa('bun', ['build', generatedFile, '--outdir', tempDir]);
|
||||
@@ -65,16 +67,17 @@ describe('Generated Client', () => {
|
||||
inputPath: FIXTURES.VALID,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const generatedFile = join(outputPath, 'index.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем экспорты
|
||||
expect(content).toContain('export');
|
||||
expect(content).toContain('class');
|
||||
expect(content).toContain('createApiClient');
|
||||
}, 30000);
|
||||
});
|
||||
|
||||
@@ -85,11 +88,12 @@ describe('Generated Client', () => {
|
||||
inputPath: FIXTURES.VALID,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const generatedFile = join(outputPath, 'operations', 'index.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем что все основные методы есть
|
||||
@@ -97,7 +101,7 @@ describe('Generated Client', () => {
|
||||
expect(content).toContain('create');
|
||||
expect(content).toContain('getById');
|
||||
expect(content).toContain('update');
|
||||
expect(content).toContain('delete');
|
||||
expect(content).toContain('deleteUsersId');
|
||||
}, 30000);
|
||||
|
||||
test('корректные имена методов (без Controller префиксов)', async () => {
|
||||
@@ -106,11 +110,12 @@ describe('Generated Client', () => {
|
||||
inputPath: FIXTURES.VALID,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const generatedFile = join(outputPath, 'operations', 'index.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем что "Controller" удален
|
||||
@@ -128,11 +133,12 @@ describe('Generated Client', () => {
|
||||
inputPath: FIXTURES.VALID,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const generatedFile = join(outputPath, 'http-client.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем наличие HttpClient
|
||||
@@ -148,11 +154,12 @@ describe('Generated Client', () => {
|
||||
inputPath: FIXTURES.WITH_AUTH,
|
||||
outputPath,
|
||||
fileName: 'AuthApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'AuthApi.ts');
|
||||
const generatedFile = join(outputPath, 'http-client.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем наличие метода для установки токена
|
||||
@@ -167,11 +174,12 @@ describe('Generated Client', () => {
|
||||
inputPath: FIXTURES.MINIMAL,
|
||||
outputPath,
|
||||
fileName: 'MinimalApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'MinimalApi.ts');
|
||||
const generatedFile = join(outputPath, 'index.ts');
|
||||
const exists = await fileExists(generatedFile);
|
||||
expect(exists).toBe(true);
|
||||
|
||||
@@ -186,11 +194,12 @@ describe('Generated Client', () => {
|
||||
inputPath: FIXTURES.WITH_AUTH,
|
||||
outputPath,
|
||||
fileName: 'AuthApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'AuthApi.ts');
|
||||
const generatedFile = join(outputPath, 'operations', 'index.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем наличие методов авторизации
|
||||
@@ -204,17 +213,18 @@ describe('Generated Client', () => {
|
||||
inputPath: FIXTURES.COMPLEX,
|
||||
outputPath,
|
||||
fileName: 'ComplexApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'ComplexApi.ts');
|
||||
const generatedFile = join(outputPath, 'index.ts');
|
||||
const exists = await fileExists(generatedFile);
|
||||
expect(exists).toBe(true);
|
||||
|
||||
// Проверяем что файл не пустой
|
||||
const content = await readTextFile(generatedFile);
|
||||
const content = await readTextFile(join(outputPath, 'operations', 'index.ts'));
|
||||
expect(content.length).toBeGreaterThan(1000);
|
||||
}, 30000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@ describe('CLI', () => {
|
||||
expect(exists).toBe(true);
|
||||
}, 30000);
|
||||
|
||||
test('должен генерировать с кастомным именем файла', async () => {
|
||||
test('должен генерировать монолит с кастомным именем файла', async () => {
|
||||
const outputPath = join(tempDir, 'output');
|
||||
const customName = 'CustomApi';
|
||||
|
||||
@@ -69,6 +69,48 @@ describe('CLI', () => {
|
||||
expect(exists).toBe(true);
|
||||
}, 30000);
|
||||
|
||||
test('должен генерировать split режим', async () => {
|
||||
const outputPath = join(tempDir, 'output');
|
||||
|
||||
const { exitCode } = await execa('bun', [
|
||||
'run',
|
||||
CLI_PATH,
|
||||
'--input',
|
||||
FIXTURES.MINIMAL,
|
||||
'--output',
|
||||
outputPath,
|
||||
'--mode',
|
||||
'split',
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(await fileExists(join(outputPath, 'index.ts'))).toBe(true);
|
||||
expect(await fileExists(join(outputPath, 'http-client.ts'))).toBe(true);
|
||||
expect(await fileExists(join(outputPath, 'operations', 'index.ts'))).toBe(true);
|
||||
}, 30000);
|
||||
|
||||
test('должен генерировать both режим', async () => {
|
||||
const outputPath = join(tempDir, 'output');
|
||||
|
||||
const { exitCode } = await execa('bun', [
|
||||
'run',
|
||||
CLI_PATH,
|
||||
'--input',
|
||||
FIXTURES.MINIMAL,
|
||||
'--output',
|
||||
outputPath,
|
||||
'--name',
|
||||
'CustomApi',
|
||||
'--mode',
|
||||
'both',
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(await fileExists(join(outputPath, 'CustomApi.ts'))).toBe(true);
|
||||
expect(await fileExists(join(outputPath, 'index.ts'))).toBe(true);
|
||||
expect(await fileExists(join(outputPath, 'operations', 'index.ts'))).toBe(true);
|
||||
}, 30000);
|
||||
|
||||
test('должен отображать версию с --version', async () => {
|
||||
const { stdout } = await execa('bun', ['run', CLI_PATH, '--version']);
|
||||
|
||||
@@ -78,7 +120,7 @@ describe('CLI', () => {
|
||||
test('должен отображать help с --help', async () => {
|
||||
const { stdout } = await execa('bun', ['run', CLI_PATH, '--help']);
|
||||
|
||||
expect(stdout).toContain('Generate TypeScript API client');
|
||||
expect(stdout).toContain('Генерация TypeScript API клиента');
|
||||
expect(stdout).toContain('--input');
|
||||
expect(stdout).toContain('--output');
|
||||
});
|
||||
@@ -141,5 +183,26 @@ describe('CLI', () => {
|
||||
expect(error.exitCode).not.toBe(0);
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
test('должен показывать русскую ошибку для недоступного URL', async () => {
|
||||
const outputPath = join(tempDir, 'output');
|
||||
|
||||
try {
|
||||
await execa('bun', [
|
||||
'run',
|
||||
CLI_PATH,
|
||||
'--input',
|
||||
'https://127.0.0.1:1/swagger.json',
|
||||
'--output',
|
||||
outputPath,
|
||||
]);
|
||||
throw new Error('Should have thrown');
|
||||
} catch (error: any) {
|
||||
expect(error.exitCode).not.toBe(0);
|
||||
expect(error.stderr).toContain('Не удалось подключиться к OpenAPI спецификации');
|
||||
expect(error.stderr).toContain('Проверьте доступность URL и сетевое подключение');
|
||||
expect(error.stderr).not.toContain('Unable to connect');
|
||||
}
|
||||
}, 30000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('config', () => {
|
||||
outputPath: './output',
|
||||
};
|
||||
|
||||
expect(() => validateConfig(config)).toThrow('Input path is required');
|
||||
expect(() => validateConfig(config)).toThrow('Не указан путь к OpenAPI спецификации');
|
||||
});
|
||||
|
||||
test('должен выбросить ошибку без outputPath', () => {
|
||||
@@ -37,13 +37,13 @@ describe('config', () => {
|
||||
inputPath: './openapi.json',
|
||||
};
|
||||
|
||||
expect(() => validateConfig(config)).toThrow('Output path is required');
|
||||
expect(() => validateConfig(config)).toThrow('Не указана директория для генерации');
|
||||
});
|
||||
|
||||
test('должен выбросить ошибку без обоих обязательных полей', () => {
|
||||
const config: Partial<GeneratorConfig> = {};
|
||||
|
||||
expect(() => validateConfig(config)).toThrow('Configuration validation failed');
|
||||
expect(() => validateConfig(config)).toThrow('Ошибка конфигурации');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('Generator', () => {
|
||||
expect(exists).toBe(true);
|
||||
}, 30000);
|
||||
|
||||
test('должен генерировать корректную структуру кода', async () => {
|
||||
test('должен добавлять русскую подпись в generated файлы', async () => {
|
||||
const outputPath = join(tempDir, 'output');
|
||||
const config: GeneratorConfig = {
|
||||
inputPath: FIXTURES.MINIMAL,
|
||||
@@ -49,9 +49,54 @@ describe('Generator', () => {
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
expect(content.startsWith('/* eslint-disable */\n/* tslint:disable */\n// @ts-nocheck\n\n/*')).toBe(true);
|
||||
expect(content).toContain('АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ');
|
||||
expect(content).toContain('Не редактируйте вручную: изменения будут перезаписаны.');
|
||||
expect(content).toContain('Генератор: @gromlab/api-codegen');
|
||||
expect(content).toContain('Репозиторий: https://gromlab.ru/gromov/api-codegen');
|
||||
expect(content).not.toContain('THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API');
|
||||
}, 30000);
|
||||
|
||||
test('должен добавлять русскую подпись в split режиме', async () => {
|
||||
const outputPath = join(tempDir, 'output');
|
||||
const config: GeneratorConfig = {
|
||||
inputPath: FIXTURES.MINIMAL,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'index.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
expect(content.startsWith('/* eslint-disable */\n/* tslint:disable */\n// @ts-nocheck\n\n/*')).toBe(true);
|
||||
expect(content).toContain('АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ');
|
||||
expect(content).toContain('Не редактируйте вручную: изменения будут перезаписаны.');
|
||||
expect(content).toContain('Генератор: @gromlab/api-codegen');
|
||||
expect(content).not.toContain('THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API');
|
||||
}, 30000);
|
||||
|
||||
test('должен генерировать корректную структуру кода', async () => {
|
||||
const outputPath = join(tempDir, 'output');
|
||||
const config: GeneratorConfig = {
|
||||
inputPath: FIXTURES.MINIMAL,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const indexFile = join(outputPath, 'index.ts');
|
||||
const httpClientFile = join(outputPath, 'http-client.ts');
|
||||
const indexContent = await readTextFile(indexFile);
|
||||
const httpClientContent = await readTextFile(httpClientFile);
|
||||
|
||||
// Проверяем наличие основных элементов
|
||||
expect(content).toContain('export class');
|
||||
expect(content).toContain('HttpClient');
|
||||
expect(indexContent).toContain('createApiClient');
|
||||
expect(httpClientContent).toContain('export class HttpClient');
|
||||
}, 30000);
|
||||
|
||||
test('должен обработать все HTTP методы', async () => {
|
||||
@@ -60,12 +105,13 @@ describe('Generator', () => {
|
||||
inputPath: FIXTURES.VALID,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
const operationsIndex = join(outputPath, 'operations', 'index.ts');
|
||||
const content = await readTextFile(operationsIndex);
|
||||
|
||||
// Проверяем что методы UserController переименованы
|
||||
// UserController_getAll -> getAll
|
||||
@@ -74,7 +120,7 @@ describe('Generator', () => {
|
||||
expect(content).toContain('create');
|
||||
expect(content).toContain('getById');
|
||||
expect(content).toContain('update');
|
||||
expect(content).toContain('delete');
|
||||
expect(content).toContain('deleteUsersId');
|
||||
}, 30000);
|
||||
|
||||
test('должен генерировать типы для request и response', async () => {
|
||||
@@ -83,11 +129,12 @@ describe('Generator', () => {
|
||||
inputPath: FIXTURES.VALID,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const generatedFile = join(outputPath, 'data-contracts.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем наличие типов
|
||||
@@ -102,15 +149,16 @@ describe('Generator', () => {
|
||||
inputPath: FIXTURES.VALID,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const generatedFile = join(outputPath, 'data-contracts.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем наличие enum
|
||||
expect(content).toContain('UserRole');
|
||||
expect(content).toContain('type UserRole');
|
||||
expect(content).toContain('admin');
|
||||
expect(content).toContain('user');
|
||||
expect(content).toContain('guest');
|
||||
@@ -122,11 +170,12 @@ describe('Generator', () => {
|
||||
inputPath: FIXTURES.WITH_AUTH,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const generatedFile = join(outputPath, 'http-client.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем наличие методов для работы с токеном
|
||||
@@ -139,11 +188,12 @@ describe('Generator', () => {
|
||||
inputPath: FIXTURES.VALID,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const generatedFile = join(outputPath, 'http-client.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем что baseUrl установлен
|
||||
@@ -156,11 +206,12 @@ describe('Generator', () => {
|
||||
inputPath: FIXTURES.VALID,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const generatedFile = join(outputPath, 'operations', 'index.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем что "Controller" удален из имен методов
|
||||
@@ -210,11 +261,12 @@ describe('Generator', () => {
|
||||
inputPath: FIXTURES.COMPLEX,
|
||||
outputPath,
|
||||
fileName: 'TestApi',
|
||||
mode: 'split',
|
||||
};
|
||||
|
||||
await generate(config);
|
||||
|
||||
const generatedFile = join(outputPath, 'TestApi.ts');
|
||||
const generatedFile = join(outputPath, 'operations', 'index.ts');
|
||||
const content = await readTextFile(generatedFile);
|
||||
|
||||
// Проверяем что все контроллеры присутствуют
|
||||
@@ -238,4 +290,4 @@ describe('Generator', () => {
|
||||
expect(exists).toBe(true);
|
||||
}, 30000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user