Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/e2e #102

Merged
24 commits merged into from
Feb 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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