Skip to content

Commit

Permalink
Merge pull request #102 from jhipster/feature/e2e
Browse files Browse the repository at this point in the history
Feature/e2e
  • Loading branch information
Angelo Manganiello committed Feb 4, 2020
2 parents f85c8b2 + c797658 commit 52013a9
Show file tree
Hide file tree
Showing 38 changed files with 566 additions and 160 deletions.
11 changes: 7 additions & 4 deletions .github/workflows/test-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ jobs:
- microservice-oauth2-sql-jdl
include:
- app-type: monolith-react-oauth2-dev
arg: no
arg: 'no'
- app-type: monolith-angular-jwt-dev
arg: no
arg: 'no'
- app-type: monolith-react-jwt-sql-jdl
arg: import-jdl
- app-type: microservice-oauth2-sql-jdl
Expand Down Expand Up @@ -50,5 +50,8 @@ jobs:
run: sudo sh test-integration/04-run-generated-unit-tests-of-sample.sh $JHI_APP
- name: 'RUN E2E TESTS of generated app'
run: sudo sh test-integration/05-run-generated-e2e-tests-of-sample.sh $JHI_APP
- name: 'RUN APP'
run: sudo sh test-integration/06-run-generated-app-sample.sh $JHI_APP
- name: 'BUILD AND RUN SERVER APP'
run: sudo sh test-integration/06-run-generated-app-sample.sh $JHI_APP build
- name: 'START APP'
if: env.JHI_APP != 'monolith-react-jwt-sql-jdl'
run: sudo sh test-integration/06-run-generated-app-sample.sh $JHI_APP run
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<a name="1.0.0"></a>
<a name="1.0.0-beta.3"></a>
<a name="1.0.0-beta.2"></a>
<a name="1.0.0-beta.1"></a>
<a name="1.0.0-alpha.3"></a>
<a name="1.0.0-alpha.2"></a>

# [1.0.0](https://github.com/jhipster/generator-jhipster-nodejs/tree/v1.0.0)
# [1.0.0-beta.3](https://github.com/jhipster/generator-jhipster-nodejs/tree/v1.0.0-beta.3)

- Add e2e tests for generated server project [issue #91](https://github.com/jhipster/generator-jhipster-nodejs/issues/91)
- Problems with running prod build [issue #99](https://github.com/jhipster/generator-jhipster-nodejs/issues/99)
- Provide service generator [issue #94](https://github.com/jhipster/generator-jhipster-nodejs/issues/94)
- Check controller generator with lint and tests [issue #92](https://github.com/jhipster/generator-jhipster-nodejs/issues/92)

Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@
> For this, a live app running is less useful than the code and the app structure show in:
>
> - **The sample repo app with** [react client and okta oauth2](https://github.com/jhipster/jhipster-sample-app-nodejs-oauth2/tree/v1.0.0-beta.2)
> - **The sample repo app with** [angulr client and jwt auth](https://github.com/jhipster/jhipster-sample-app-nodejs/tree/v1.0.0-beta.2)
> - **The sample repo app with** [angular client and jwt auth](https://github.com/jhipster/jhipster-sample-app-nodejs/tree/v1.0.0-beta.2)
<div align="center">
<a href="https://github.com/jhipster/generator-jhipster-nodejs">
<img src="https://raw.githubusercontent.com/jhipster/generator-jhipster-nodejs/v1.0.0-beta.3/nhipster-cli-logo.png">
</a>
</div>

# Greetings, nodejs Hipster!

Expand Down
5 changes: 3 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ To contribute for the next featues, fork the repo and open a pull request regard
- Swagger support
- Entity ORM generation for prod database (sql) and dev database (default db is sqlite already configured)
- Basic security management with jwt and oauth2
- All subgenerators (without services)
- All subgenerators

> Let it free to give advices or tips!
Expand Down Expand Up @@ -43,13 +43,14 @@ To contribute for the next featues, fork the repo and open a pull request regard
- [x] Needs way more testing of different relations, combinations for entity
- [x] Pipelines migrations from travis to GitHub Actions
- [x] Unit tests for all generators and sonar quality with coverage
- [x] e2e tests for generated project
- [x] Added service generator (jhipster spring-service _name_ command)

## To DO for next release

- [ ] Encrypt user password in db
- [ ] OAuth2 feature for Keycloack
- [ ] Languages subgenerator support i18n: translate home page for NHipster and remove spring boot resources message
- [ ] e2e tests for generated project
- [ ] DTOs based rest api
- [ ] Validation
- [ ] Export json/yaml for swagger to integrate with jhipster gateway
Expand Down
8 changes: 6 additions & 2 deletions generators/entity-server/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ const serverFiles = {
file: 'src/repository/entity.repository.ts',
renameTo: generator => `src/repository/${generator.entityFileName}.repository.ts`
},
{
file: 'src/service/entity.service.ts',
renameTo: generator => `src/service/${generator.entityFileName}.service.ts`
},
{
file: 'src/web/rest/entity.controller.ts',
renameTo: generator => `src/web/rest/${generator.entityFileName}.controller.ts`
},
{
file: 'test/entities/entity.controller.spec.ts',
renameTo: generator => `test/entities/${generator.entityFileName}.controller.spec.ts`
file: 'e2e/entity.e2e-spec.ts',
renameTo: generator => `e2e/${generator.entityFileName}.e2e-spec.ts`
}
]
}
Expand Down
108 changes: 108 additions & 0 deletions generators/entity-server/templates/server/e2e/entity.e2e-spec.ts.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { Test, TestingModule } from '@nestjs/testing';
import request = require('supertest');
import { AppModule } from '../src/app.module';
import { INestApplication } from '@nestjs/common';
import { AuthGuard } from '../src/security/guards/auth.guard';
import { RolesGuard } from '../src/security/guards/roles.guard';
import <%= entityClass %> from '../src/domain/<%= entityFileName %>.entity';
import { <%= entityClass %>Service } from '../src/service/<%= entityFileName %>.service';

describe('<%= entityClass %> Controller', () => {
let app: INestApplication;

const authGuardMock = { canActivate: (): any => true };
const rolesGuardMock = { canActivate: (): any => true };
const entityMock: any = {
id: 'entityId'
}

const serviceMock = {
findById: (): any => entityMock,
findAndCount: (): any => [entityMock, 0],
save: (): any => entityMock,
update: (): any => entityMock,
delete: (): any => entityMock
};


beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).overrideGuard(AuthGuard)
.useValue(authGuardMock)
.overrideGuard(RolesGuard)
.useValue(rolesGuardMock)
.overrideProvider(<%= entityClass %>Service)
.useValue(serviceMock)
.compile();

app = moduleFixture.createNestApplication();
await app.init();
});

it('/GET all <%= entityApiUrl %> ', async () => {

const getEntities: <%= entityClass %>[] = (await request(app.getHttpServer())
.get('/api/<%= entityApiUrl %>')
.expect(200)).body;

expect(getEntities).toEqual(entityMock);

}
);

it('/GET <%= entityApiUrl %> by id', async () => {


const getEntity: <%= entityClass %> = (await request(app.getHttpServer())
.get('/api/<%= entityApiUrl %>/' + entityMock.id)
.expect(200)).body;

expect(getEntity).toEqual(entityMock);

}
);

it('/POST create <%= entityApiUrl %>', async () => {

const createdEntity: <%= entityClass %> = (await request(app.getHttpServer())
.post('/api/<%= entityApiUrl %>')
.send(entityMock)
.expect(201)).body;

expect(createdEntity).toEqual(entityMock);

}
);

it('/PUT update <%= entityApiUrl %>', async () => {


const updatedEntity: <%= entityClass %> = (await request(app.getHttpServer())
.put('/api/<%= entityApiUrl %>')
.send(entityMock)
.expect(201)).body;


expect(updatedEntity).toEqual(entityMock);

}
);


it('/DELETE <%= entityApiUrl %>', async () => {


const deletedEntity: <%= entityClass %> = (await request(app.getHttpServer())
.delete('/api/<%= entityApiUrl %>/' + entityMock.id)
.expect(204)).body;

expect(deletedEntity).toEqual({});
}
);

afterEach(async () => {
await app.close();
});
});

Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { <%= entityClass %>Controller } from '../web/rest/<%= entityFileName %>.controller';
import { <%= entityClass %>Repository } from '../repository/<%= entityFileName %>.repository';
import { <%= entityClass %>Service } from '../service/<%= entityFileName %>.service';


@Module({
imports: [TypeOrmModule.forFeature([<%= entityClass %>Repository])],
controllers: [<%= entityClass %>Controller],
providers: [<%= entityClass %>Service],
exports: [<%= entityClass %>Service],
})
export class <%= entityClass %>Module {}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { FindManyOptions, FindOneOptions } from 'typeorm';
import <%= entityClass %> from '../domain/<%= entityFileName %>.entity';
import { <%= entityClass %>Repository } from '../repository/<%= entityFileName %>.repository';


@Injectable()
export class <%= entityClass %>Service {
logger = new Logger('<%= entityClass %>Service');

constructor(@InjectRepository(<%= entityClass %>Repository) private <%= asEntity(entityInstance) %>Repository: <%= entityClass %>Repository) {}

async findById(id: string): Promise<<%= entityClass %> | undefined> {
return await this.<%= asEntity(entityInstance) %>Repository.findOne(id);
}

async findByfields(options: FindOneOptions<<%= entityClass %>>): Promise<<%= entityClass %> | undefined> {
return await this.<%= asEntity(entityInstance) %>Repository.findOne(options);
}

async findAndCount(options: FindManyOptions<<%= entityClass %>>): Promise<[<%= entityClass %>[], number]> {
return await this.<%= asEntity(entityInstance) %>Repository.findAndCount(options);
}

async save(<%= asEntity(entityInstance) %>: <%= entityClass %>): Promise<<%= entityClass %> | undefined> {
return await this.<%= asEntity(entityInstance) %>Repository.save(<%= asEntity(entityInstance) %>);
}

async update(<%= asEntity(entityInstance) %>: <%= entityClass %>): Promise<<%= entityClass %> | undefined> {
return await this.save(<%= asEntity(entityInstance) %>);
}

async delete(<%= asEntity(entityInstance) %>: <%= entityClass %>): Promise<<%= entityClass %> | undefined> {
return await this.<%= asEntity(entityInstance) %>Repository.remove(<%= asEntity(entityInstance) %>);
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { Body, Controller, Delete, Get, Logger, Param, Post as PostMethod, Put, UseGuards, Req, UseInterceptors } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { <% if (authenticationType === 'jwt') { _%> ApiBearerAuth, <% } else if (authenticationType === 'oauth2') { _%> ApiOAuth2Auth, <% } _%> ApiUseTags, ApiResponse, ApiOperation } from '@nestjs/swagger';
import { Request } from 'express';
import <%= entityClass %> from '../../domain/<%= entityFileName %>.entity';
import { <%= entityClass %>Service } from '../../service/<%= entityFileName %>.service';
import { PageRequest, Page } from '../../domain/base/pagination.entity';
import { <%= entityClass %>Repository } from '../../repository/<%= entityFileName %>.repository';
import { AuthGuard, Roles, RolesGuard, RoleType } from '../../security';
import { HeaderUtil } from '../../client/header-util';
import { LoggingInterceptor } from '../../client/interceptors/logging.interceptor';
Expand All @@ -21,7 +20,8 @@ import { LoggingInterceptor } from '../../client/interceptors/logging.intercepto
export class <%= entityClass %>Controller {
logger = new Logger('<%= entityClass %>Controller');

constructor(@InjectRepository(<%= entityClass %>Repository) private <%= asEntity(entityInstance) %>Repository: <%= entityClass %>Repository) {}
constructor(private readonly <%= asEntity(entityInstance) %>Service: <%= entityClass %>Service) {}


@Get('/')
@Roles(RoleType.USER)
Expand All @@ -30,9 +30,9 @@ export class <%= entityClass %>Controller {
description: 'List all records',
type: <%= entityClass %>,
})
async getAll(@Req() req: Request): Promise<any> {
async getAll(@Req() req: Request): Promise<<%= entityClass %> []> {
const pageRequest: PageRequest = new PageRequest(req.query.page, req.query.size, req.query.sort);
const [results, count] = await this.<%= asEntity(entityInstance) %>Repository.findAndCount({
const [results, count] = await this.<%= asEntity(entityInstance) %>Service.findAndCount({
skip: +pageRequest.page * pageRequest.size,
take: +pageRequest.size,
order: pageRequest.sort.asOrder(),
Expand All @@ -42,50 +42,54 @@ export class <%= entityClass %>Controller {
}

@Get('/:id')
@Roles(RoleType.USER)
@ApiResponse({
status: 200,
description: 'The found record',
type: <%= entityClass %>,
})
async getOne(@Param('id') id: string): Promise<any> {
return await this.<%= asEntity(entityInstance) %>Repository.findOne(id);
async getOne(@Param('id') id: string): Promise<<%= entityClass %>> {
return await this.<%= asEntity(entityInstance) %>Service.findById(id);
}

@PostMethod('/')
@Roles(RoleType.USER)
@ApiOperation({ title: 'Create <%= asEntity(entityInstance) %>' })
@ApiResponse({
status: 201,
description: 'The record has been successfully created.',
type: <%= entityClass %>,
})
@ApiResponse({ status: 403, description: 'Forbidden.' })
async post(@Req() req: Request, @Body() <%= asEntity(entityInstance) %>: <%= entityClass %>): Promise<any> {
const created = await this.<%= asEntity(entityInstance) %>Repository.save(<%= asEntity(entityInstance) %>);
async post(@Req() req: Request, @Body() <%= asEntity(entityInstance) %>: <%= entityClass %>): Promise<<%= entityClass %>> {
const created = await this.<%= asEntity(entityInstance) %>Service.save(<%= asEntity(entityInstance) %>);
HeaderUtil.addEntityCreatedHeaders(req.res, '<%= entityClass %>', created.id);
return created;
}

@Put('/')
@Roles(RoleType.USER)
@ApiOperation({ title: 'Update <%= asEntity(entityInstance) %>' })
@ApiResponse({
status: 200,
description: 'The record has been successfully updated.',
type: <%= entityClass %>,
})
async put(@Req() req: Request, @Body() <%= asEntity(entityInstance) %>: <%= entityClass %>): Promise<any> {
const id = <%= asEntity(entityInstance) %>.id;
HeaderUtil.addEntityCreatedHeaders(req.res, '<%= entityClass %>', id);
return await this.<%= asEntity(entityInstance) %>Repository.update(id, <%= asEntity(entityInstance) %>);
async put(@Req() req: Request, @Body() <%= asEntity(entityInstance) %>: <%= entityClass %>): Promise<<%= entityClass %>> {
HeaderUtil.addEntityCreatedHeaders(req.res, '<%= entityClass %>', <%= asEntity(entityInstance) %>.id);
return await this.<%= asEntity(entityInstance) %>Service.update(<%= asEntity(entityInstance) %>);
}

@Delete('/:id')
@Roles(RoleType.USER)
@ApiOperation({ title: 'Delete <%= asEntity(entityInstance) %>' })
@ApiResponse({
status: 204,
description: 'The record has been successfully deleted.',
})
async remove(@Req() req: Request, @Param('id') id: string): Promise<any> {
async remove(@Req() req: Request, @Param('id') id: string): Promise<<%= entityClass %>> {
HeaderUtil.addEntityDeletedHeaders(req.res, '<%= entityClass %>', id);
return await this.<%= asEntity(entityInstance) %>Repository.delete(id);
const toDelete = await this.<%= asEntity(entityInstance) %>Service.findById(id);
return await this.<%= asEntity(entityInstance) %>Service.delete(toDelete);
}
}

This file was deleted.

Loading

0 comments on commit 52013a9

Please sign in to comment.