Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5707abf6bb | |||
|
|
d47afb6ff1 | ||
|
|
463626da0c | ||
| 05a58c4254 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@blas/openapi-clean-arch-generator",
|
"name": "@blas/openapi-clean-arch-generator",
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"description": "Angular Clean Architecture generator from OpenAPI/Swagger",
|
"description": "Angular Clean Architecture generator from OpenAPI/Swagger",
|
||||||
"main": "dist/main.js",
|
"main": "dist/main.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
@@ -74,7 +74,8 @@ export function generateCleanArchitecture(
|
|||||||
mappers: 0,
|
mappers: 0,
|
||||||
useCases: 0,
|
useCases: 0,
|
||||||
providers: 0,
|
providers: 0,
|
||||||
mocks: 0
|
mocks: 0,
|
||||||
|
specs: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
const schemas =
|
const schemas =
|
||||||
@@ -210,6 +211,26 @@ export function generateCleanArchitecture(
|
|||||||
generatedCount,
|
generatedCount,
|
||||||
'mocks'
|
'mocks'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Model spec
|
||||||
|
renderTemplate(
|
||||||
|
templatesDir,
|
||||||
|
'model-entity.spec.mustache',
|
||||||
|
modelViewData,
|
||||||
|
path.join(outputDir, 'entities/models', `${toCamelCase(baseName)}.model.spec.ts`),
|
||||||
|
generatedCount,
|
||||||
|
'specs'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mapper spec
|
||||||
|
renderTemplate(
|
||||||
|
templatesDir,
|
||||||
|
'mapper.spec.mustache',
|
||||||
|
mapperViewData,
|
||||||
|
path.join(outputDir, 'data/mappers', `${toCamelCase(baseName)}.mapper.spec.ts`),
|
||||||
|
generatedCount,
|
||||||
|
'specs'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Generate Use Cases and Repositories from Paths/Tags
|
// 2. Generate Use Cases and Repositories from Paths/Tags
|
||||||
@@ -227,7 +248,8 @@ export function generateCleanArchitecture(
|
|||||||
paramName: p.name,
|
paramName: p.name,
|
||||||
dataType: mapSwaggerTypeToTs(p.schema?.type || ''),
|
dataType: mapSwaggerTypeToTs(p.schema?.type || ''),
|
||||||
description: p.description || '',
|
description: p.description || '',
|
||||||
required: p.required
|
required: p.required,
|
||||||
|
testValue: resolveTestParamValue(mapSwaggerTypeToTs(p.schema?.type || ''))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (op.requestBody) {
|
if (op.requestBody) {
|
||||||
@@ -241,14 +263,19 @@ export function generateCleanArchitecture(
|
|||||||
paramName: 'body',
|
paramName: 'body',
|
||||||
dataType: bodyType,
|
dataType: bodyType,
|
||||||
description: op.requestBody.description || '',
|
description: op.requestBody.description || '',
|
||||||
required: true
|
required: true,
|
||||||
|
testValue: resolveTestParamValue(bodyType)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let returnType = 'void';
|
let returnType = 'void';
|
||||||
let returnBaseType = 'void';
|
let returnBaseType = 'void';
|
||||||
let isListContainer = false;
|
let isListContainer = false;
|
||||||
const responseSchema = op.responses?.['200']?.content?.['application/json']?.schema;
|
const successCode = ['200', '201', '202', '203'].find((code) => op.responses?.[code]);
|
||||||
|
const responseSchema =
|
||||||
|
successCode !== undefined
|
||||||
|
? op.responses?.[successCode]?.content?.['application/json']?.schema
|
||||||
|
: undefined;
|
||||||
if (responseSchema) {
|
if (responseSchema) {
|
||||||
if (responseSchema.$ref) {
|
if (responseSchema.$ref) {
|
||||||
returnType = responseSchema.$ref.split('/').pop() || 'unknown';
|
returnType = responseSchema.$ref.split('/').pop() || 'unknown';
|
||||||
@@ -260,25 +287,31 @@ export function generateCleanArchitecture(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasQueryParams = (op.parameters || []).some((p) => p.in === 'query');
|
||||||
|
const hasBodyParam = !!op.requestBody;
|
||||||
|
|
||||||
tagsMap[tag].push({
|
tagsMap[tag].push({
|
||||||
nickname: op.operationId || `${method}${pathKey.replace(/\//g, '_')}`,
|
nickname: op.operationId || `${method}${pathKey.replace(/\//g, '_')}`,
|
||||||
summary: op.summary || '',
|
summary: op.summary || '',
|
||||||
notes: op.description || '',
|
notes: op.description || '',
|
||||||
httpMethod: method.toLowerCase(),
|
httpMethod: method.toLowerCase(),
|
||||||
|
uppercaseHttpMethod: method.toUpperCase(),
|
||||||
path: pathKey,
|
path: pathKey,
|
||||||
allParams: allParams.map((p, i: number) => ({
|
allParams: allParams.map((p, i: number) => ({
|
||||||
...p,
|
...p,
|
||||||
'-last': i === allParams.length - 1
|
'-last': i === allParams.length - 1
|
||||||
})),
|
})),
|
||||||
hasQueryParams: (op.parameters || []).some((p) => p.in === 'query'),
|
hasQueryParams,
|
||||||
queryParams: (op.parameters || [])
|
queryParams: (op.parameters || [])
|
||||||
.filter((p) => p.in === 'query')
|
.filter((p) => p.in === 'query')
|
||||||
.map((p, i: number, arr: unknown[]) => ({
|
.map((p, i: number, arr: unknown[]) => ({
|
||||||
paramName: p.name,
|
paramName: p.name,
|
||||||
'-last': i === arr.length - 1
|
'-last': i === arr.length - 1
|
||||||
})),
|
})),
|
||||||
hasBodyParam: !!op.requestBody,
|
hasBodyParam,
|
||||||
bodyParam: 'body',
|
bodyParam: 'body',
|
||||||
|
hasOptions: hasQueryParams || hasBodyParam,
|
||||||
|
hasBothParamsAndBody: hasQueryParams && hasBodyParam,
|
||||||
returnType: returnType !== 'void' ? returnType : false,
|
returnType: returnType !== 'void' ? returnType : false,
|
||||||
returnBaseType: returnBaseType !== 'void' ? returnBaseType : false,
|
returnBaseType: returnBaseType !== 'void' ? returnBaseType : false,
|
||||||
returnTypeVarName: returnType !== 'void' ? toCamelCase(returnType) : false,
|
returnTypeVarName: returnType !== 'void' ? toCamelCase(returnType) : false,
|
||||||
@@ -438,10 +471,30 @@ export function generateCleanArchitecture(
|
|||||||
generatedCount,
|
generatedCount,
|
||||||
'mocks'
|
'mocks'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Repository impl spec
|
||||||
|
renderTemplate(
|
||||||
|
templatesDir,
|
||||||
|
'api.repository.impl.spec.mustache',
|
||||||
|
apiViewData,
|
||||||
|
path.join(outputDir, 'data/repositories', `${toCamelCase(tag)}.repository.impl.spec.ts`),
|
||||||
|
generatedCount,
|
||||||
|
'specs'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Use-cases impl spec
|
||||||
|
renderTemplate(
|
||||||
|
templatesDir,
|
||||||
|
'api.use-cases.impl.spec.mustache',
|
||||||
|
apiViewData,
|
||||||
|
path.join(outputDir, 'domain/use-cases', `${toCamelCase(tag)}.use-cases.impl.spec.ts`),
|
||||||
|
generatedCount,
|
||||||
|
'specs'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
logSuccess(
|
logSuccess(
|
||||||
`${generatedCount.models} Models, ${generatedCount.repositories} Repos, ${generatedCount.useCases} Use Cases, ${generatedCount.mappers} Mappers, ${generatedCount.providers} Providers, ${generatedCount.mocks} Mocks generated`
|
`${generatedCount.models} Models, ${generatedCount.repositories} Repos, ${generatedCount.useCases} Use Cases, ${generatedCount.mappers} Mappers, ${generatedCount.providers} Providers, ${generatedCount.mocks} Mocks, ${generatedCount.specs} Specs generated`
|
||||||
);
|
);
|
||||||
return generatedCount;
|
return generatedCount;
|
||||||
}
|
}
|
||||||
@@ -463,3 +516,18 @@ function renderTemplate(
|
|||||||
counter[key]++;
|
counter[key]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Resolves a simple test value literal for a given TypeScript type. */
|
||||||
|
function resolveTestParamValue(dataType: string): string {
|
||||||
|
switch (dataType) {
|
||||||
|
case 'string':
|
||||||
|
return "'test'";
|
||||||
|
case 'number':
|
||||||
|
return '1';
|
||||||
|
case 'boolean':
|
||||||
|
return 'true';
|
||||||
|
default:
|
||||||
|
if (dataType.endsWith('[]')) return '[]';
|
||||||
|
return '{} as any';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,15 @@ function countMockFiles(dir: string): number {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Counts files ending with `.spec.ts` in a directory (returns 0 if directory does not exist). */
|
||||||
|
function countSpecFiles(dir: string): number {
|
||||||
|
try {
|
||||||
|
return fs.readdirSync(dir).filter((f) => f.endsWith('.spec.ts')).length;
|
||||||
|
} catch {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Generates and persists the `generation-report.json` file with process statistics. */
|
/** Generates and persists the `generation-report.json` file with process statistics. */
|
||||||
export function generateReport(outputDir: string, analysis: SwaggerAnalysis): GenerationReport {
|
export function generateReport(outputDir: string, analysis: SwaggerAnalysis): GenerationReport {
|
||||||
logStep('Generating report...');
|
logStep('Generating report...');
|
||||||
@@ -35,7 +44,12 @@ export function generateReport(outputDir: string, analysis: SwaggerAnalysis): Ge
|
|||||||
countMockFiles(path.join(outputDir, 'di/repositories')) +
|
countMockFiles(path.join(outputDir, 'di/repositories')) +
|
||||||
countMockFiles(path.join(outputDir, 'di/use-cases')) +
|
countMockFiles(path.join(outputDir, 'di/use-cases')) +
|
||||||
countMockFiles(path.join(outputDir, 'domain/use-cases')) +
|
countMockFiles(path.join(outputDir, 'domain/use-cases')) +
|
||||||
countMockFiles(path.join(outputDir, 'entities/models'))
|
countMockFiles(path.join(outputDir, 'entities/models')),
|
||||||
|
specs:
|
||||||
|
countSpecFiles(path.join(outputDir, 'entities/models')) +
|
||||||
|
countSpecFiles(path.join(outputDir, 'data/mappers')) +
|
||||||
|
countSpecFiles(path.join(outputDir, 'data/repositories')) +
|
||||||
|
countSpecFiles(path.join(outputDir, 'domain/use-cases'))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export interface GeneratedCount {
|
|||||||
useCases: number;
|
useCases: number;
|
||||||
providers: number;
|
providers: number;
|
||||||
mocks: number;
|
mocks: number;
|
||||||
|
specs: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,5 +26,6 @@ export interface GenerationReport {
|
|||||||
useCases: number;
|
useCases: number;
|
||||||
providers: number;
|
providers: number;
|
||||||
mocks: number;
|
mocks: number;
|
||||||
|
specs: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ export interface TagOperationParam {
|
|||||||
description: string;
|
description: string;
|
||||||
required: boolean;
|
required: boolean;
|
||||||
'-last': boolean;
|
'-last': boolean;
|
||||||
|
testValue?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,12 +108,15 @@ export interface TagOperation {
|
|||||||
summary: string;
|
summary: string;
|
||||||
notes: string;
|
notes: string;
|
||||||
httpMethod: string;
|
httpMethod: string;
|
||||||
|
uppercaseHttpMethod: string;
|
||||||
path: string;
|
path: string;
|
||||||
allParams: TagOperationParam[];
|
allParams: TagOperationParam[];
|
||||||
hasQueryParams: boolean;
|
hasQueryParams: boolean;
|
||||||
queryParams: unknown[];
|
queryParams: unknown[];
|
||||||
hasBodyParam: boolean;
|
hasBodyParam: boolean;
|
||||||
bodyParam: string;
|
bodyParam: string;
|
||||||
|
hasOptions: boolean;
|
||||||
|
hasBothParamsAndBody: boolean;
|
||||||
returnType: string | boolean;
|
returnType: string | boolean;
|
||||||
returnBaseType: string | boolean;
|
returnBaseType: string | boolean;
|
||||||
returnTypeVarName: string | boolean;
|
returnTypeVarName: string | boolean;
|
||||||
|
|||||||
@@ -32,26 +32,20 @@ export class {{classname}}RepositoryImpl extends MRepository implements {{classn
|
|||||||
{{#operation}}
|
{{#operation}}
|
||||||
{{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}{{^-last}}, {{/-last}}{{/allParams}}): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> {
|
{{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}{{^-last}}, {{/-last}}{{/allParams}}): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> {
|
||||||
{{#isListContainer}}
|
{{#isListContainer}}
|
||||||
return this.{{httpMethod}}<{{{returnBaseType}}}Dto>('{{path}}'{{#hasQueryParams}}, {
|
return this.{{httpMethod}}<{{{returnBaseType}}}Dto>('{{path}}'{{#hasOptions}}, { {{#hasQueryParams}}params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }{{/hasQueryParams}}{{#hasBothParamsAndBody}}, {{/hasBothParamsAndBody}}{{#hasBodyParam}}body{{/hasBodyParam}} }{{/hasOptions}})
|
||||||
params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }
|
|
||||||
}{{/hasQueryParams}}{{#hasBodyParam}}, {{bodyParam}}{{/hasBodyParam}})
|
|
||||||
.pipe(
|
.pipe(
|
||||||
map((response) => response.{{#vendorExtensions}}{{x-response-property}}{{/vendorExtensions}}{{^vendorExtensions}}items{{/vendorExtensions}}.map({{{returnBaseTypeVarName}}}Mapper))
|
map((response) => response.{{#vendorExtensions}}{{x-response-property}}{{/vendorExtensions}}{{^vendorExtensions}}items{{/vendorExtensions}}.map({{{returnBaseTypeVarName}}}Mapper))
|
||||||
);
|
);
|
||||||
{{/isListContainer}}
|
{{/isListContainer}}
|
||||||
{{^isListContainer}}
|
{{^isListContainer}}
|
||||||
{{#returnType}}
|
{{#returnType}}
|
||||||
return this.{{httpMethod}}<{{{returnType}}}Dto>('{{path}}'{{#hasQueryParams}}, {
|
return this.{{httpMethod}}<{{{returnType}}}Dto>('{{path}}'{{#hasOptions}}, { {{#hasQueryParams}}params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }{{/hasQueryParams}}{{#hasBothParamsAndBody}}, {{/hasBothParamsAndBody}}{{#hasBodyParam}}body{{/hasBodyParam}} }{{/hasOptions}})
|
||||||
params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }
|
|
||||||
}{{/hasQueryParams}}{{#hasBodyParam}}, {{bodyParam}}{{/hasBodyParam}})
|
|
||||||
.pipe(
|
.pipe(
|
||||||
map({{{returnTypeVarName}}}Mapper)
|
map({{{returnTypeVarName}}}Mapper)
|
||||||
);
|
);
|
||||||
{{/returnType}}
|
{{/returnType}}
|
||||||
{{^returnType}}
|
{{^returnType}}
|
||||||
return this.{{httpMethod}}<void>('{{path}}'{{#hasQueryParams}}, {
|
return this.{{httpMethod}}<void>('{{path}}'{{#hasOptions}}, { {{#hasQueryParams}}params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }{{/hasQueryParams}}{{#hasBothParamsAndBody}}, {{/hasBothParamsAndBody}}{{#hasBodyParam}}body{{/hasBodyParam}} }{{/hasOptions}});
|
||||||
params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }
|
|
||||||
}{{/hasQueryParams}}{{#hasBodyParam}}, {{bodyParam}}{{/hasBodyParam}});
|
|
||||||
{{/returnType}}
|
{{/returnType}}
|
||||||
{{/isListContainer}}
|
{{/isListContainer}}
|
||||||
}
|
}
|
||||||
|
|||||||
97
templates/api.repository.impl.spec.mustache
Normal file
97
templates/api.repository.impl.spec.mustache
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
{{#apiInfo}}
|
||||||
|
{{#apis}}
|
||||||
|
{{#operations}}
|
||||||
|
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { {{classname}}RepositoryImpl } from './{{classFilename}}.repository.impl';
|
||||||
|
{{#returnImports}}
|
||||||
|
import { mock{{classname}}Dto } from '@/dtos/{{classFilename}}.dto.mock';
|
||||||
|
import { mock{{classname}}Model } from '@/entities/models/{{classFilename}}.model.mock';
|
||||||
|
{{/returnImports}}
|
||||||
|
|
||||||
|
describe('{{classname}}RepositoryImpl', () => {
|
||||||
|
let repository: {{classname}}RepositoryImpl;
|
||||||
|
let httpMock: HttpTestingController;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [HttpClientTestingModule],
|
||||||
|
providers: [{{classname}}RepositoryImpl]
|
||||||
|
});
|
||||||
|
|
||||||
|
repository = TestBed.inject({{classname}}RepositoryImpl);
|
||||||
|
httpMock = TestBed.inject(HttpTestingController);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
httpMock.verify();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(repository).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
{{#operation}}
|
||||||
|
describe('{{nickname}}', () => {
|
||||||
|
it('should perform a {{uppercaseHttpMethod}} request', () => {
|
||||||
|
repository.{{nickname}}({{#allParams}}{{{testValue}}}{{^-last}}, {{/-last}}{{/allParams}}).subscribe();
|
||||||
|
|
||||||
|
const req = httpMock.expectOne((r) => r.method === '{{uppercaseHttpMethod}}');
|
||||||
|
expect(req.request.method).toBe('{{uppercaseHttpMethod}}');
|
||||||
|
{{#isListContainer}}
|
||||||
|
req.flush({ items: [mock{{returnBaseType}}Dto()] });
|
||||||
|
{{/isListContainer}}
|
||||||
|
{{^isListContainer}}
|
||||||
|
{{#returnType}}
|
||||||
|
req.flush(mock{{returnBaseType}}Dto());
|
||||||
|
{{/returnType}}
|
||||||
|
{{^returnType}}
|
||||||
|
req.flush(null);
|
||||||
|
{{/returnType}}
|
||||||
|
{{/isListContainer}}
|
||||||
|
});
|
||||||
|
|
||||||
|
{{#returnType}}
|
||||||
|
it('should map the response to the domain model', () => {
|
||||||
|
const dto = mock{{returnBaseType}}Dto();
|
||||||
|
const expectedModel = mock{{returnBaseType}}Model();
|
||||||
|
|
||||||
|
{{#isListContainer}}
|
||||||
|
repository.{{nickname}}({{#allParams}}{{{testValue}}}{{^-last}}, {{/-last}}{{/allParams}}).subscribe((result) => {
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(result.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
httpMock.expectOne((r) => r.method === '{{uppercaseHttpMethod}}').flush({ items: [dto] });
|
||||||
|
{{/isListContainer}}
|
||||||
|
{{^isListContainer}}
|
||||||
|
repository.{{nickname}}({{#allParams}}{{{testValue}}}{{^-last}}, {{/-last}}{{/allParams}}).subscribe((result) => {
|
||||||
|
expect(result).toEqual(expectedModel);
|
||||||
|
});
|
||||||
|
|
||||||
|
httpMock.expectOne((r) => r.method === '{{uppercaseHttpMethod}}').flush(dto);
|
||||||
|
{{/isListContainer}}
|
||||||
|
});
|
||||||
|
|
||||||
|
{{/returnType}}
|
||||||
|
it('should propagate HTTP errors', (done) => {
|
||||||
|
repository.{{nickname}}({{#allParams}}{{{testValue}}}{{^-last}}, {{/-last}}{{/allParams}}).subscribe({
|
||||||
|
error: (err) => {
|
||||||
|
expect(err.status).toBe(500);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
httpMock
|
||||||
|
.expectOne((r) => r.method === '{{uppercaseHttpMethod}}')
|
||||||
|
.flush('Internal Server Error', { status: 500, statusText: 'Internal Server Error' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
{{/operation}}
|
||||||
|
});
|
||||||
|
|
||||||
|
{{/operations}}
|
||||||
|
{{/apis}}
|
||||||
|
{{/apiInfo}}
|
||||||
91
templates/api.use-cases.impl.spec.mustache
Normal file
91
templates/api.use-cases.impl.spec.mustache
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
{{#apiInfo}}
|
||||||
|
{{#apis}}
|
||||||
|
{{#operations}}
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
|
import { {{classname}}UseCasesImpl } from './{{classFilename}}.use-cases.impl';
|
||||||
|
|
||||||
|
import { {{constantName}}_REPOSITORY, {{classname}}Repository } from '@/domain/repositories/{{classFilename}}.repository.contract';
|
||||||
|
{{#returnImports}}
|
||||||
|
import { mock{{classname}}Model } from '@/entities/models/{{classFilename}}.model.mock';
|
||||||
|
{{/returnImports}}
|
||||||
|
|
||||||
|
describe('{{classname}}UseCasesImpl', () => {
|
||||||
|
let useCase: {{classname}}UseCasesImpl;
|
||||||
|
let mockRepository: jasmine.SpyObj<{{classname}}Repository>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockRepository = jasmine.createSpyObj('{{classname}}Repository', [{{#operation}}'{{nickname}}', {{/operation}}]);
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{{classname}}UseCasesImpl,
|
||||||
|
{ provide: {{constantName}}_REPOSITORY, useValue: mockRepository }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
useCase = TestBed.inject({{classname}}UseCasesImpl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(useCase).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
{{#operation}}
|
||||||
|
describe('{{nickname}}', () => {
|
||||||
|
it('should delegate to the repository', () => {
|
||||||
|
{{#isListContainer}}
|
||||||
|
mockRepository.{{nickname}}.and.returnValue(of([mock{{returnBaseType}}Model()]));
|
||||||
|
{{/isListContainer}}
|
||||||
|
{{^isListContainer}}
|
||||||
|
{{#returnBaseType}}
|
||||||
|
mockRepository.{{nickname}}.and.returnValue(of(mock{{returnBaseType}}Model()));
|
||||||
|
{{/returnBaseType}}
|
||||||
|
{{^returnBaseType}}
|
||||||
|
mockRepository.{{nickname}}.and.returnValue(of(undefined));
|
||||||
|
{{/returnBaseType}}
|
||||||
|
{{/isListContainer}}
|
||||||
|
|
||||||
|
useCase.{{nickname}}({{#allParams}}{{{testValue}}}{{^-last}}, {{/-last}}{{/allParams}});
|
||||||
|
|
||||||
|
expect(mockRepository.{{nickname}}).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the observable from the repository', (done) => {
|
||||||
|
{{#isListContainer}}
|
||||||
|
const expected = [mock{{returnBaseType}}Model()];
|
||||||
|
mockRepository.{{nickname}}.and.returnValue(of(expected));
|
||||||
|
|
||||||
|
useCase.{{nickname}}({{#allParams}}{{{testValue}}}{{^-last}}, {{/-last}}{{/allParams}}).subscribe((result) => {
|
||||||
|
expect(result).toEqual(expected);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
{{/isListContainer}}
|
||||||
|
{{^isListContainer}}
|
||||||
|
{{#returnBaseType}}
|
||||||
|
const expected = mock{{returnBaseType}}Model();
|
||||||
|
mockRepository.{{nickname}}.and.returnValue(of(expected));
|
||||||
|
|
||||||
|
useCase.{{nickname}}({{#allParams}}{{{testValue}}}{{^-last}}, {{/-last}}{{/allParams}}).subscribe((result) => {
|
||||||
|
expect(result).toEqual(expected);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
{{/returnBaseType}}
|
||||||
|
{{^returnBaseType}}
|
||||||
|
mockRepository.{{nickname}}.and.returnValue(of(undefined));
|
||||||
|
|
||||||
|
useCase.{{nickname}}({{#allParams}}{{{testValue}}}{{^-last}}, {{/-last}}{{/allParams}}).subscribe({
|
||||||
|
complete: () => done()
|
||||||
|
});
|
||||||
|
{{/returnBaseType}}
|
||||||
|
{{/isListContainer}}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
{{/operation}}
|
||||||
|
});
|
||||||
|
|
||||||
|
{{/operations}}
|
||||||
|
{{/apis}}
|
||||||
|
{{/apiInfo}}
|
||||||
39
templates/mapper.spec.mustache
Normal file
39
templates/mapper.spec.mustache
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{{#models}}
|
||||||
|
{{#model}}
|
||||||
|
import { {{classVarName}}Mapper } from './{{classFilename}}.mapper';
|
||||||
|
|
||||||
|
import { mock{{classname}}Dto } from '@/dtos/{{classFilename}}.dto.mock';
|
||||||
|
import { {{classname}} } from '@/entities/models/{{classFilename}}.model';
|
||||||
|
|
||||||
|
describe('{{classVarName}}Mapper', () => {
|
||||||
|
{{#vars}}
|
||||||
|
it('should map {{name}} from DTO to model', () => {
|
||||||
|
const dto = mock{{classname}}Dto();
|
||||||
|
|
||||||
|
const result = {{classVarName}}Mapper(dto);
|
||||||
|
|
||||||
|
expect(result.{{name}}).toBe(dto.{{name}});
|
||||||
|
});
|
||||||
|
|
||||||
|
{{/vars}}
|
||||||
|
it('should return an instance of {{classname}}', () => {
|
||||||
|
const dto = mock{{classname}}Dto();
|
||||||
|
|
||||||
|
const result = {{classVarName}}Mapper(dto);
|
||||||
|
|
||||||
|
expect(result).toBeInstanceOf({{classname}});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should map all fields correctly from a complete DTO', () => {
|
||||||
|
const dto = mock{{classname}}Dto();
|
||||||
|
|
||||||
|
const result = {{classVarName}}Mapper(dto);
|
||||||
|
|
||||||
|
{{#vars}}
|
||||||
|
expect(result.{{name}}).toBe(dto.{{name}});
|
||||||
|
{{/vars}}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
{{/model}}
|
||||||
|
{{/models}}
|
||||||
34
templates/model-entity.spec.mustache
Normal file
34
templates/model-entity.spec.mustache
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{{#models}}
|
||||||
|
{{#model}}
|
||||||
|
import { {{classname}} } from './{{classFilename}}.model';
|
||||||
|
import { mock{{classname}}Model } from './{{classFilename}}.model.mock';
|
||||||
|
|
||||||
|
describe('{{classname}}', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
const model = new {{classname}}();
|
||||||
|
|
||||||
|
expect(model).toBeInstanceOf({{classname}});
|
||||||
|
});
|
||||||
|
|
||||||
|
{{#vars}}
|
||||||
|
it('should allow setting {{name}}', () => {
|
||||||
|
const model = new {{classname}}();
|
||||||
|
const expected = mock{{classname}}Model();
|
||||||
|
model.{{name}} = expected.{{name}};
|
||||||
|
|
||||||
|
expect(model.{{name}}).toBe(expected.{{name}});
|
||||||
|
});
|
||||||
|
|
||||||
|
{{/vars}}
|
||||||
|
it('should build a valid model from mock', () => {
|
||||||
|
const model = mock{{classname}}Model();
|
||||||
|
|
||||||
|
expect(model).toBeInstanceOf({{classname}});
|
||||||
|
{{#vars}}
|
||||||
|
expect(model.{{name}}).toBeDefined();
|
||||||
|
{{/vars}}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
{{/model}}
|
||||||
|
{{/models}}
|
||||||
Reference in New Issue
Block a user