From 4069d500ac8073abd7ca62aa242c0d97896aa9ea Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Mon, 11 Dec 2017 09:21:10 +0100 Subject: [PATCH 01/25] Add e2e example --- README.md | 2 ++ package-scripts.js | 9 +++-- package.json | 2 ++ src/app.ts | 2 +- src/loaders/expressLoader.ts | 5 ++- test/e2e/api/info.test.ts | 20 +++++++++++ test/e2e/utils/app.ts | 30 ++++++++++++++++ yarn.lock | 66 ++++++++++++++++++++++++++++++------ 8 files changed, 119 insertions(+), 17 deletions(-) create mode 100644 test/e2e/api/info.test.ts create mode 100644 test/e2e/utils/app.ts diff --git a/README.md b/README.md index b4195b87..786be5a0 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Try it!! We are happy to hear your feedback or any kind of new features. - **API Documentation** thanks to [swagger](http://swagger.io/). - **API Monitoring** thanks to [express-status-monitor](https://github.com/RafalWilinski/express-status-monitor). - **Integrated Testing Tool** thanks to [Jest](https://facebook.github.io/jest). +- **E2E API Testing** thanks to [supertest](https://github.com/visionmedia/supertest). - **Basic Security Features** thanks to [Helmet](https://helmetjs.github.io/). - **Easy event dispatching** thanks to [event-dispatch](https://github.com/pleerock/event-dispatch). - **Fast Database Building** with simple migration from [TypeORM](https://github.com/typeorm/typeorm). @@ -389,6 +390,7 @@ npm start db.seed | [Helmet](https://helmetjs.github.io/) | Helmet helps you secure your Express apps by setting various HTTP headers. It’s not a silver bullet, but it can help! | | [Auth0 API Documentation](https://auth0.com/docs/api/management/v2) | Authentification service | | [Jest](http://facebook.github.io/jest/) | Delightful JavaScript Testing Library for unit and e2e tests | +| [supertest](https://github.com/visionmedia/supertest) | Super-agent driven library for testing node.js HTTP servers using a fluent API | | [swagger Documentation](http://swagger.io/) | API Tool to describe and document your api. | | [SQLite Documentation](https://www.sitepoint.com/getting-started-sqlite3-basic-commands/) | Getting Started with SQLite3 – Basic Commands. | | [GraphQL Documentation](http://graphql.org/graphql-js/) | A query language for your API. | diff --git a/package-scripts.js b/package-scripts.js index 9fb6f88e..009a5a72 100644 --- a/package-scripts.js +++ b/package-scripts.js @@ -119,6 +119,7 @@ module.exports = { script: 'nps "test.integration --verbose"' }, run: { + // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. script: 'cross-env NODE_ENV=test jest --testPathPattern=integration -i' }, }, @@ -127,7 +128,6 @@ module.exports = { script: series( 'nps banner.test', 'nps test.e2e.pretest', - runInNewWindow(series('nps build', 'nps start')), 'nps test.e2e.run' ) }, @@ -136,11 +136,10 @@ module.exports = { }, verbose: { script: 'nps "test.e2e --verbose"' + },run: { + // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. + script: 'cross-env NODE_ENV=test jest --testPathPattern=e2e -i' }, - run: series( - `wait-on --timeout 120000 http-get://localhost:3000/api/info`, - 'cross-env NODE_ENV=test jest --testPathPattern=e2e -i' - ) } }, /** diff --git a/package.json b/package.json index d3967c50..17969379 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "@types/reflect-metadata": "0.0.5", "@types/request": "^2.0.8", "@types/serve-favicon": "^2.2.29", + "@types/supertest": "^2.0.4", "@types/uuid": "^3.4.3", "@types/winston": "^2.3.7", "body-parser": "^1.18.2", @@ -84,6 +85,7 @@ "request": "^2.83.0", "routing-controllers": "^0.7.6", "serve-favicon": "^2.4.5", + "supertest": "^3.0.0", "swagger-ui-express": "^2.0.10", "ts-node": "^3.3.0", "tslint": "^5.8.0", diff --git a/src/app.ts b/src/app.ts index d93e7b64..7f353a25 100644 --- a/src/app.ts +++ b/src/app.ts @@ -27,7 +27,7 @@ import { eventDispatchLoader } from './loaders/eventDispatchLoader'; bootstrapMicroframework({ /** * Loader is a place where you can configure all your modules during microframework - * bootstrap. All loaders are executed one by one in a sequential order. + * bootstrap process. All loaders are executed one by one in a sequential order. */ loaders: [ winstonLoader, diff --git a/src/loaders/expressLoader.ts b/src/loaders/expressLoader.ts index 9c53d3ca..a63d131a 100644 --- a/src/loaders/expressLoader.ts +++ b/src/loaders/expressLoader.ts @@ -34,7 +34,10 @@ export const expressLoader: MicroframeworkLoader = (settings: MicroframeworkSett }); // Run application to listen on given port - expressApp.listen(env.app.port); + if (!env.isTest) { + const server = expressApp.listen(env.app.port); + settings.setData('express_server', server); + } // Here we can set the data for other loaders settings.setData('express_app', expressApp); diff --git a/test/e2e/api/info.test.ts b/test/e2e/api/info.test.ts new file mode 100644 index 00000000..b1768dd0 --- /dev/null +++ b/test/e2e/api/info.test.ts @@ -0,0 +1,20 @@ +import { Application } from 'express'; +import * as request from 'supertest'; +import { bootstrapApp } from '../utils/app'; + +describe('/api', () => { + + let app: Application; + beforeAll(async () => app = await bootstrapApp()); + + test('GET: / should return the api-version', async (done) => { + const response = await request(app) + .get('/api') + .expect('Content-Type', /json/) + .expect(200); + + console.log(response); + done(); + }); + +}); diff --git a/test/e2e/utils/app.ts b/test/e2e/utils/app.ts new file mode 100644 index 00000000..02987921 --- /dev/null +++ b/test/e2e/utils/app.ts @@ -0,0 +1,30 @@ + + +import { bootstrapMicroframework, Microframework } from 'microframework'; +import { Application } from 'express'; +import { expressLoader } from './../../../src/loaders/expressLoader'; +import { winstonLoader } from './../../../src/loaders/winstonLoader'; +import { typeormLoader } from './../../../src/loaders/typeormLoader'; +import { swaggerLoader } from './../../../src/loaders/swaggerLoader'; +import { monitorLoader } from './../../../src/loaders/monitorLoader'; +import { homeLoader } from './../../../src/loaders/homeLoader'; +import { publicLoader } from './../../../src/loaders/publicLoader'; +import { iocLoader } from './../../../src/loaders/iocLoader'; +import { graphqlLoader } from './../../../src/loaders/graphqlLoader'; +import { eventDispatchLoader } from './../../../src/loaders/eventDispatchLoader'; + +export const bootstrapApp = async (): Promise => { + const framework = await bootstrapMicroframework({ + loaders: [ + winstonLoader, + iocLoader, + eventDispatchLoader, + typeormLoader, + expressLoader, + homeLoader, + // publicLoader, + // graphqlLoader, + ], + }); + return framework.settings.getData('express_app') as Application; +}; diff --git a/yarn.lock b/yarn.lock index a8b5fe90..698e9a02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -113,6 +113,18 @@ "@types/express-serve-static-core" "*" "@types/mime" "*" +"@types/superagent@*": + version "3.5.6" + resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-3.5.6.tgz#9cf2632c075ba9e601f6a610aadc23992d02394c" + dependencies: + "@types/node" "*" + +"@types/supertest@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.4.tgz#28770e13293365e240a842d7d5c5a1b3d2dee593" + dependencies: + "@types/superagent" "*" + "@types/uuid@^3.4.3": version "3.4.3" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.3.tgz#121ace265f5569ce40f4f6d0ff78a338c732a754" @@ -868,7 +880,7 @@ component-bind@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" -component-emitter@1.2.1: +component-emitter@1.2.1, component-emitter@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" @@ -960,6 +972,10 @@ cookie@0.3.1, cookie@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" +cookiejar@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.1.tgz#41ad57b1b555951ec171412a81942b1e8200d34a" + copyfiles@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-1.2.0.tgz#a8da3ac41aa2220ae29bd3c58b6984294f2c593c" @@ -1445,7 +1461,7 @@ express@^4.16.2: utils-merge "1.0.1" vary "~1.1.2" -extend@~3.0.0, extend@~3.0.1: +extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" @@ -1561,22 +1577,26 @@ forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" -form-data@~2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" +form-data@^2.3.1, form-data@~2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" dependencies: asynckit "^0.4.0" combined-stream "^1.0.5" mime-types "^2.1.12" -form-data@~2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" dependencies: asynckit "^0.4.0" combined-stream "^1.0.5" mime-types "^2.1.12" +formidable@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9" + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -2774,7 +2794,7 @@ merge@^1.1.3: version "1.2.0" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" -methods@~1.1.2: +methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -2820,6 +2840,10 @@ mime@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" +mime@^1.4.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" @@ -3501,7 +3525,7 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -readable-stream@2.3.3, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5: +readable-stream@2.3.3, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5: version "2.3.3" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" dependencies: @@ -4051,6 +4075,28 @@ subarg@^1.0.0: dependencies: minimist "^1.1.0" +superagent@^3.0.0: + version "3.8.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.2.tgz#e4a11b9d047f7d3efeb3bbe536d9ec0021d16403" + dependencies: + component-emitter "^1.2.0" + cookiejar "^2.1.0" + debug "^3.1.0" + extend "^3.0.0" + form-data "^2.3.1" + formidable "^1.1.1" + methods "^1.1.1" + mime "^1.4.1" + qs "^6.5.1" + readable-stream "^2.0.5" + +supertest@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-3.0.0.tgz#8d4bb68fd1830ee07033b1c5a5a9a4021c965296" + dependencies: + methods "~1.1.2" + superagent "^3.0.0" + supports-color@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" From 54397b6bb17a5d640234854d704e1a046077be60 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Mon, 11 Dec 2017 09:23:32 +0100 Subject: [PATCH 02/25] Test api version --- test/e2e/api/info.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/e2e/api/info.test.ts b/test/e2e/api/info.test.ts index b1768dd0..2974f698 100644 --- a/test/e2e/api/info.test.ts +++ b/test/e2e/api/info.test.ts @@ -1,6 +1,7 @@ import { Application } from 'express'; import * as request from 'supertest'; import { bootstrapApp } from '../utils/app'; +import { env } from '../../../src/core/env'; describe('/api', () => { @@ -13,7 +14,7 @@ describe('/api', () => { .expect('Content-Type', /json/) .expect(200); - console.log(response); + expect(response.body.version).toBe(env.app.version); done(); }); From d165a4cf1be71f8c4d49613a64038e6c22da17a5 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 16:04:38 +0100 Subject: [PATCH 03/25] Add nock for http mocking --- package.json | 3 ++- yarn.lock | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 17969379..752e3b3f 100644 --- a/package.json +++ b/package.json @@ -113,11 +113,12 @@ "license": "MIT", "devDependencies": { "@types/jest": "^21.1.5", + "@types/nock": "^8.2.1", "cross-env": "^5.1.1", "jest": "^21.2.1", "mock-express-request": "^0.2.0", "mock-express-response": "^0.2.1", - "nock": "^9.1.0", + "nock": "^9.1.4", "sqlite3": "^3.1.13", "ts-jest": "^21.1.4" } diff --git a/yarn.lock b/yarn.lock index 698e9a02..c9d0c495 100644 --- a/yarn.lock +++ b/yarn.lock @@ -85,6 +85,12 @@ dependencies: "@types/express" "*" +"@types/nock@^8.2.1": + version "8.2.1" + resolved "https://registry.yarnpkg.com/@types/nock/-/nock-8.2.1.tgz#1fbe5bdecb943c109a778553fa4d2401cb9394b4" + dependencies: + "@types/node" "*" + "@types/node@*": version "8.0.57" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.57.tgz#e5d8b4dc112763e35cfc51988f4f38da3c486d99" @@ -2973,7 +2979,7 @@ nocache@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.0.0.tgz#202b48021a0c4cbde2df80de15a17443c8b43980" -nock@^9.1.0: +nock@^9.1.4: version "9.1.4" resolved "https://registry.yarnpkg.com/nock/-/nock-9.1.4.tgz#5cdda89c5effaed0f448486f0135bf7b1e7bf1dc" dependencies: From 75eee5ef04f4bc322cd611ad5d7d7db6e4509c62 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 16:04:46 +0100 Subject: [PATCH 04/25] Remove comming soon part --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index ccf4598d..580b7f5d 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,6 @@ Try it!! We are happy to hear your feedback or any kind of new features. - **GraphQL** provides as a awesome query language for our api [GraphQL](http://graphql.org/). - **DataLoaders** helps with performance thanks to caching and batching [DataLoaders](https://github.com/facebook/dataloader). -### Comming soon - -- **Custom Commands** are also available in our setup and really easy to use or even extend. -- **Scaffolding Commands** will speed up your development tremendously as you should focus on business code and not scaffolding. - # Table of Contents - [Getting Started](#getting-started) From 3e90830b0f22bb7b3a0e34e5bdbeebf68cefbfba Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 16:05:02 +0100 Subject: [PATCH 05/25] Refactor seeds --- src/database/seeds/CreateBruce.ts | 18 ++++++++++++++++++ .../{0001-CreatePets.ts => CreatePets.ts} | 0 .../{0000-CreateUsers.ts => CreateUsers.ts} | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/database/seeds/CreateBruce.ts rename src/database/seeds/{0001-CreatePets.ts => CreatePets.ts} (100%) rename src/database/seeds/{0000-CreateUsers.ts => CreateUsers.ts} (91%) diff --git a/src/database/seeds/CreateBruce.ts b/src/database/seeds/CreateBruce.ts new file mode 100644 index 00000000..3b9b58df --- /dev/null +++ b/src/database/seeds/CreateBruce.ts @@ -0,0 +1,18 @@ +import { SeedsInterface, FactoryInterface } from '../../lib/seeds'; +import { User } from '../../../src/api/models/User'; + + +export class CreateBruce implements SeedsInterface { + + public async seed(factory: FactoryInterface): Promise { + const connection = await factory.getConnection(); + const em = connection.createEntityManager(); + + const user = new User(); + user.firstName = 'Bruce'; + user.lastName = 'Wayne'; + user.email = 'bruce.wayne@wayne-enterprises.com'; + return await em.save(user); + } + +} diff --git a/src/database/seeds/0001-CreatePets.ts b/src/database/seeds/CreatePets.ts similarity index 100% rename from src/database/seeds/0001-CreatePets.ts rename to src/database/seeds/CreatePets.ts diff --git a/src/database/seeds/0000-CreateUsers.ts b/src/database/seeds/CreateUsers.ts similarity index 91% rename from src/database/seeds/0000-CreateUsers.ts rename to src/database/seeds/CreateUsers.ts index 4d314298..e73c637d 100644 --- a/src/database/seeds/0000-CreateUsers.ts +++ b/src/database/seeds/CreateUsers.ts @@ -7,7 +7,7 @@ export class CreateUsers implements SeedsInterface { public async seed(factory: FactoryInterface): Promise { await factory .get(User) - .create(); + .createMany(10); } } From acac1f8c6645abc42ac46b00817e139b42fe9bba Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 16:05:29 +0100 Subject: [PATCH 06/25] Improve seeding --- src/lib/seeds/Factory.ts | 6 ++++++ src/lib/seeds/FactoryInterface.ts | 5 +++++ src/lib/seeds/SeedsInterface.ts | 8 ++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/lib/seeds/Factory.ts b/src/lib/seeds/Factory.ts index 76155ee6..23357d25 100644 --- a/src/lib/seeds/Factory.ts +++ b/src/lib/seeds/Factory.ts @@ -4,6 +4,7 @@ import { Connection } from 'typeorm/connection/Connection'; import { FactoryInterface } from './FactoryInterface'; import { EntityFactory } from './EntityFactory'; import { BluePrint } from './BluePrint'; +import { SeedsConstructorInterface } from './SeedsInterface'; export class Factory implements FactoryInterface { @@ -32,6 +33,11 @@ export class Factory implements FactoryInterface { this.connection = connection; } + public async runSeed(seedClass: SeedsConstructorInterface): Promise { + const seeder = new seedClass(); + return await seeder.seed(this); + } + public define(entityClass: ObjectType, callback: (faker: typeof Faker, args: any[]) => Entity): void { this.blueprints[this.getNameOfEntity(entityClass)] = new BluePrint(entityClass, callback); } diff --git a/src/lib/seeds/FactoryInterface.ts b/src/lib/seeds/FactoryInterface.ts index 5f25bc39..580f2804 100644 --- a/src/lib/seeds/FactoryInterface.ts +++ b/src/lib/seeds/FactoryInterface.ts @@ -2,6 +2,7 @@ import * as Faker from 'faker'; import { ObjectType } from 'typeorm'; import { EntityFactoryInterface } from './EntityFactoryInterface'; import { Connection } from 'typeorm/connection/Connection'; +import { SeedsConstructorInterface } from 'src/lib/seeds'; /** * This interface is used to define new entity faker factories or to get such a * entity faker factory to start seeding. @@ -15,6 +16,10 @@ export interface FactoryInterface { * Sets the typeorm database connection. */ setConnection(connection: Connection): void; + /** + * Runs the given seed class + */ + runSeed(seedClass: SeedsConstructorInterface): Promise; /** * Returns an EntityFactoryInterface */ diff --git a/src/lib/seeds/SeedsInterface.ts b/src/lib/seeds/SeedsInterface.ts index 81bf2397..b27667e4 100644 --- a/src/lib/seeds/SeedsInterface.ts +++ b/src/lib/seeds/SeedsInterface.ts @@ -1,4 +1,4 @@ -import { FactoryInterface } from './FactoryInterface'; +import { Factory } from './Factory'; /** * Seeds should implement this interface and all its methods. */ @@ -6,5 +6,9 @@ export interface SeedsInterface { /** * Seed data into the databas. */ - seed(factory: FactoryInterface): Promise; + seed(factory: Factory): Promise; +} + +export interface SeedsConstructorInterface { + new(): SeedsInterface; } From 9699915c775d2d08338d87082e0d5411a72ca1d9 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 16:05:51 +0100 Subject: [PATCH 07/25] Add e2e test examples --- test/e2e/.gitkeep | 0 test/e2e/api/info.test.ts | 10 ++-- test/e2e/api/users.test.ts | 65 +++++++++++++++++++++++++ test/e2e/utils/auth.ts | 13 +++++ test/e2e/utils/{app.ts => bootstrap.ts} | 20 ++++++-- test/e2e/utils/typeormLoader.ts | 15 ++++++ 6 files changed, 114 insertions(+), 9 deletions(-) delete mode 100644 test/e2e/.gitkeep create mode 100644 test/e2e/api/users.test.ts create mode 100644 test/e2e/utils/auth.ts rename test/e2e/utils/{app.ts => bootstrap.ts} (62%) create mode 100644 test/e2e/utils/typeormLoader.ts diff --git a/test/e2e/.gitkeep b/test/e2e/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/test/e2e/api/info.test.ts b/test/e2e/api/info.test.ts index 2974f698..ab0ba911 100644 --- a/test/e2e/api/info.test.ts +++ b/test/e2e/api/info.test.ts @@ -1,15 +1,17 @@ import { Application } from 'express'; import * as request from 'supertest'; -import { bootstrapApp } from '../utils/app'; +import { bootstrapApp, BootstrapSettings } from '../utils/bootstrap'; import { env } from '../../../src/core/env'; +import { synchronizeDatabase } from '../../integration/utils/database'; describe('/api', () => { - let app: Application; - beforeAll(async () => app = await bootstrapApp()); + let settings: BootstrapSettings; + beforeAll(async () => settings = await bootstrapApp()); + beforeAll(async () => synchronizeDatabase(settings.connection)); test('GET: / should return the api-version', async (done) => { - const response = await request(app) + const response = await request(settings.app) .get('/api') .expect('Content-Type', /json/) .expect(200); diff --git a/test/e2e/api/users.test.ts b/test/e2e/api/users.test.ts new file mode 100644 index 00000000..4cc72192 --- /dev/null +++ b/test/e2e/api/users.test.ts @@ -0,0 +1,65 @@ +import * as nock from 'nock'; +import * as request from 'supertest'; +import { Application } from 'express'; +import { CreateBruce } from './../../../src/database/seeds/CreateBruce'; +import { getFactory } from './../../../src/lib/seeds/index'; +import { Factory } from './../../../src/lib/seeds/Factory'; +import { User } from './../../../src/api/models/User'; +import { bootstrapApp, BootstrapSettings } from '../utils/bootstrap'; +import { env } from '../../../src/core/env'; +import { synchronizeDatabase, closeDatabase } from '../../integration/utils/database'; +import { fakeAuthenticationForUser } from '../utils/auth'; + +describe('/api/users', () => { + + // ------------------------------------------------------------------------- + // Tear up + // ------------------------------------------------------------------------- + + let settings: BootstrapSettings; + let factory: Factory; + let bruce: User; + let authServer: nock.Scope; + beforeAll(async () => settings = await bootstrapApp()); + beforeAll(async () => synchronizeDatabase(settings.connection)); + beforeAll(async () => factory = getFactory(settings.connection)); + beforeAll(async () => bruce = await factory.runSeed(CreateBruce)); + beforeAll(async () => authServer = fakeAuthenticationForUser(bruce)); + + // ------------------------------------------------------------------------- + // Tear down + // ------------------------------------------------------------------------- + + afterAll(() => nock.cleanAll()); + afterAll(async () => closeDatabase(settings.connection)); + + // ------------------------------------------------------------------------- + // Test cases + // ------------------------------------------------------------------------- + + test('GET: / should return a list of users', async (done) => { + const response = await request(settings.app) + .get('/api/users') + .set('Authorization', `Bearer 1234`) + .expect('Content-Type', /json/) + .expect(200); + + expect(response.body.length).toBe(1); + done(); + }); + + test('GET: /:id should return bruce', async (done) => { + const response = await request(settings.app) + .get(`/api/users/${bruce.id}`) + .set('Authorization', `Bearer 1234`) + .expect('Content-Type', /json/) + .expect(200); + + expect(response.body.id).toBe(bruce.id); + expect(response.body.firstName).toBe(bruce.firstName); + expect(response.body.lastName).toBe(bruce.lastName); + expect(response.body.email).toBe(bruce.email); + done(); + }); + +}); diff --git a/test/e2e/utils/auth.ts b/test/e2e/utils/auth.ts new file mode 100644 index 00000000..6aa44bb8 --- /dev/null +++ b/test/e2e/utils/auth.ts @@ -0,0 +1,13 @@ +import * as nock from 'nock'; +import { User } from '../../../src/api/models/User'; +import { env } from '../../../src/core/env'; + + +export const fakeAuthenticationForUser = (user: User): nock.Scope => { + return nock(env.auth.route) + .persist() + .post('') + .reply(200, { + user_id: `auth0|${user.email}`, + }); +}; diff --git a/test/e2e/utils/app.ts b/test/e2e/utils/bootstrap.ts similarity index 62% rename from test/e2e/utils/app.ts rename to test/e2e/utils/bootstrap.ts index 02987921..c0ddefac 100644 --- a/test/e2e/utils/app.ts +++ b/test/e2e/utils/bootstrap.ts @@ -1,19 +1,27 @@ +import * as http from 'http'; import { bootstrapMicroframework, Microframework } from 'microframework'; import { Application } from 'express'; +import { Connection } from 'typeorm/connection/Connection'; import { expressLoader } from './../../../src/loaders/expressLoader'; import { winstonLoader } from './../../../src/loaders/winstonLoader'; -import { typeormLoader } from './../../../src/loaders/typeormLoader'; import { swaggerLoader } from './../../../src/loaders/swaggerLoader'; import { monitorLoader } from './../../../src/loaders/monitorLoader'; import { homeLoader } from './../../../src/loaders/homeLoader'; +import { typeormLoader } from '../utils/typeormLoader'; import { publicLoader } from './../../../src/loaders/publicLoader'; import { iocLoader } from './../../../src/loaders/iocLoader'; import { graphqlLoader } from './../../../src/loaders/graphqlLoader'; import { eventDispatchLoader } from './../../../src/loaders/eventDispatchLoader'; -export const bootstrapApp = async (): Promise => { +export interface BootstrapSettings { + app: Application; + server: http.Server; + connection: Connection; +} + +export const bootstrapApp = async (): Promise => { const framework = await bootstrapMicroframework({ loaders: [ winstonLoader, @@ -22,9 +30,11 @@ export const bootstrapApp = async (): Promise => { typeormLoader, expressLoader, homeLoader, - // publicLoader, - // graphqlLoader, ], }); - return framework.settings.getData('express_app') as Application; + return { + app: framework.settings.getData('express_app') as Application, + server: framework.settings.getData('express_server') as http.Server, + connection: framework.settings.getData('connection') as Connection, + } as BootstrapSettings; }; diff --git a/test/e2e/utils/typeormLoader.ts b/test/e2e/utils/typeormLoader.ts new file mode 100644 index 00000000..50933070 --- /dev/null +++ b/test/e2e/utils/typeormLoader.ts @@ -0,0 +1,15 @@ +import { createDatabaseConnection } from './../../integration/utils/database'; +import { Connection } from 'typeorm/connection/Connection'; +import { createConnection } from 'typeorm'; +import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework'; +import { env } from '../../../src/core/env'; + + +export const typeormLoader: MicroframeworkLoader = async (settings: MicroframeworkSettings | undefined) => { + + const connection = await createDatabaseConnection(); + if (settings) { + settings.setData('connection', connection); + settings.onShutdown(() => connection.close()); + } +}; From 7c95460e893da5221c784542078d7944a13a8267 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 16:06:02 +0100 Subject: [PATCH 08/25] Add types --- src/loaders/expressLoader.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/loaders/expressLoader.ts b/src/loaders/expressLoader.ts index a63d131a..0a885107 100644 --- a/src/loaders/expressLoader.ts +++ b/src/loaders/expressLoader.ts @@ -1,3 +1,4 @@ +import { Application } from 'express'; import { createExpressServer } from 'routing-controllers'; import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework'; import { env } from '../core/env'; @@ -13,7 +14,7 @@ export const expressLoader: MicroframeworkLoader = (settings: MicroframeworkSett * We create a new express server instance. * We could have also use useExpressServer here to attach controllers to an existing express instance. */ - const expressApp = createExpressServer({ + const expressApp: Application = createExpressServer({ cors: true, classTransformer: true, routePrefix: env.app.routePrefix, From 2b25741a2428506f723c53d7c4243b8b9bf213fb Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 16:08:45 +0100 Subject: [PATCH 09/25] Refactor seeding --- package-scripts.js | 2 +- src/lib/seeds/cli.ts | 87 +++++++++++++++++++++++++++++++++++++ src/lib/seeds/index.ts | 98 ++++++------------------------------------ 3 files changed, 102 insertions(+), 85 deletions(-) create mode 100644 src/lib/seeds/cli.ts diff --git a/package-scripts.js b/package-scripts.js index 0d1513fc..8c3ca2ff 100644 --- a/package-scripts.js +++ b/package-scripts.js @@ -53,7 +53,7 @@ module.exports = { seed: series( 'nps banner.seed', 'nps db.config', - runFast('./src/lib/seeds/') + runFast('./src/lib/seeds/cli.ts') ), config: runFast('./src/lib/ormconfig.ts'), drop: runFast('./node_modules/typeorm/cli.js schema:drop') diff --git a/src/lib/seeds/cli.ts b/src/lib/seeds/cli.ts new file mode 100644 index 00000000..a6511d96 --- /dev/null +++ b/src/lib/seeds/cli.ts @@ -0,0 +1,87 @@ +import 'reflect-metadata'; +import * as path from 'path'; +import * as glob from 'glob'; +import * as commander from 'commander'; +import * as Chalk from 'chalk'; +import { Connection } from 'typeorm'; +import { Factory } from './Factory'; +import { getConnection } from './connection'; + + +// Get executiuon path to look from there for seeds and factories +const runDir = process.cwd(); + +// Cli helper +commander + .version('0.0.0') + .description('Run database seeds of your project') + .option('-L, --logging', 'enable sql query logging') + .option('--factories ', 'add filepath for your factories') + .option('--seeds ', 'add filepath for your seeds') + .option('--config ', 'add filepath to your database config (must be a json)') + .parse(process.argv); + +// Get cli parameter for a different factory path +const factoryPath = (commander.factories) + ? commander.factories + : 'src/database/'; + +// Get cli parameter for a different seeds path +const seedsPath = (commander.seeds) + ? commander.seeds + : 'src/database/seeds/'; + +// Search for seeds and factories +glob(path.join(runDir, factoryPath, '**/*Factory{.js,.ts}'), (errFactories: any, factories: string[]) => { + glob(path.join(runDir, seedsPath, '*{.js,.ts}'), (errSeeds: any, seeds: string[]) => { + const log = console.log; + const chalk = Chalk.default; + + // Status logging to print out the amount of factories and seeds. + log(chalk.bold('seeds')); + log('🔎 ', chalk.gray.underline(`found:`), + chalk.blue.bold(`${factories.length} factories`, chalk.gray('&'), chalk.blue.bold(`${seeds.length} seeds`))); + + // Initialize all factories + for (const factory of factories) { + require(factory); + } + + // Get typeorm database connection and pass them to the factory instance + getConnection().then((connection: Connection) => { + const factory = Factory.getInstance(); + factory.setConnection(connection); + + // Initialize and seed all seeds. + const queue: Array> = []; + for (const seed of seeds) { + try { + const seedFile: any = require(seed); + let className = seed.split('/')[seed.split('/').length - 1]; + className = className.replace('.ts', '').replace('.js', ''); + className = className.split('-')[className.split('-').length - 1]; + log('\n' + chalk.gray.underline(`executing seed: `), chalk.green.bold(`${className}`)); + queue.push((new seedFile[className]()).seed(factory)); + } catch (error) { + console.error('Could not run seed ' + seed, error); + } + } + + // Promise to catch the end for termination and logging + Promise + .all(queue) + .then(() => { + log('\n👍 ', chalk.gray.underline(`finished seeding`)); + process.exit(0); + }) + .catch((error) => { + console.error('Could not run seed ' + error); + process.exit(1); + }); + + }).catch((error) => { + console.error('Could not connection to database ' + error); + process.exit(1); + }); + }); +}); diff --git a/src/lib/seeds/index.ts b/src/lib/seeds/index.ts index 7336ebd3..de75f430 100644 --- a/src/lib/seeds/index.ts +++ b/src/lib/seeds/index.ts @@ -1,93 +1,23 @@ -import 'reflect-metadata'; -import * as path from 'path'; -import * as glob from 'glob'; -import * as commander from 'commander'; -import * as Chalk from 'chalk'; import { Connection } from 'typeorm'; +import 'reflect-metadata'; import { Factory } from './Factory'; -import { getConnection } from './connection'; - - -// Get executiuon path to look from there for seeds and factories -const runDir = process.cwd(); - -// Cli helper -commander - .version('0.0.0') - .description('Run database seeds of your project') - .option('-L, --logging', 'enable sql query logging') - .option('--factories ', 'add filepath for your factories') - .option('--seeds ', 'add filepath for your seeds') - .option('--config ', 'add filepath to your database config (must be a json)') - .parse(process.argv); - -// Get cli parameter for a different factory path -const factoryPath = (commander.factories) - ? commander.factories - : 'src/database/'; - -// Get cli parameter for a different seeds path -const seedsPath = (commander.seeds) - ? commander.seeds - : 'src/database/seeds/'; - -// Search for seeds and factories -glob(path.join(runDir, factoryPath, '**/*Factory{.js,.ts}'), (errFactories: any, factories: string[]) => { - glob(path.join(runDir, seedsPath, '*{.js,.ts}'), (errSeeds: any, seeds: string[]) => { - const log = console.log; - const chalk = Chalk.default; - // Status logging to print out the amount of factories and seeds. - log(chalk.bold('seeds')); - log('🔎 ', chalk.gray.underline(`found:`), - chalk.blue.bold(`${factories.length} factories`, chalk.gray('&'), chalk.blue.bold(`${seeds.length} seeds`))); - - // Initialize all factories - for (const factory of factories) { - require(factory); - } - - // Get typeorm database connection and pass them to the factory instance - getConnection().then((connection: Connection) => { - const factory = Factory.getInstance(); - factory.setConnection(connection); - - // Initialize and seed all seeds. - const queue: Array> = []; - for (const seed of seeds) { - try { - const seedFile: any = require(seed); - let className = seed.split('/')[seed.split('/').length - 1]; - className = className.replace('.ts', '').replace('.js', ''); - className = className.split('-')[className.split('-').length - 1]; - log('\n' + chalk.gray.underline(`executing seed: `), chalk.green.bold(`${className}`)); - queue.push((new seedFile[className]()).seed(factory)); - } catch (error) { - console.error('Could not run seed ' + seed, error); - } - } - - // Promise to catch the end for termination and logging - Promise - .all(queue) - .then(() => { - log('\n👍 ', chalk.gray.underline(`finished seeding`)); - process.exit(0); - }) - .catch((error) => { - console.error('Could not run seed ' + error); - process.exit(1); - }); - - }).catch((error) => { - console.error('Could not connection to database ' + error); - process.exit(1); - }); - }); -}); +// ------------------------------------------------------------------------- +// Handy Exports +// ------------------------------------------------------------------------- export * from './FactoryInterface'; export * from './EntityFactoryInterface'; export * from './SeedsInterface'; export * from './Factory'; export * from './utils'; + +// ------------------------------------------------------------------------- +// Facade functions +// ------------------------------------------------------------------------- + +export const getFactory = (connection: Connection) => { + const factory = Factory.getInstance(); + factory.setConnection(connection); + return factory; +}; From fa4cbabefe76ef4d5a2d3589bf6b459ebce6d565 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 16:10:22 +0100 Subject: [PATCH 10/25] Update documentation --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 580b7f5d..345bac23 100644 --- a/README.md +++ b/README.md @@ -325,7 +325,7 @@ export class CreateUsers implements SeedsInterface { public async seed(factory: FactoryInterface): Promise { await factory .get(User) - .create(10); + .createMany(10); } } @@ -339,7 +339,7 @@ export class CreateUsers implements SeedsInterface { public async seed(factory: FactoryInterface): Promise { await factory .get(User, 'admin') - .create(1); + .create(); } } @@ -353,7 +353,7 @@ await factory.get(User) .each(async (user: User) => { const pets: Pet[] = await factory.get(Pet) - .create(2); + .createMany(2); const petIds = pets.map((pet: Pet) => pet.Id); await user.pets().attach(petIds); @@ -387,6 +387,7 @@ npm start db.seed | [Auth0 API Documentation](https://auth0.com/docs/api/management/v2) | Authentification service | | [Jest](http://facebook.github.io/jest/) | Delightful JavaScript Testing Library for unit and e2e tests | | [supertest](https://github.com/visionmedia/supertest) | Super-agent driven library for testing node.js HTTP servers using a fluent API | +| [nock](https://github.com/node-nock/nock) | HTTP mocking and expectations library | | [swagger Documentation](http://swagger.io/) | API Tool to describe and document your api. | | [SQLite Documentation](https://www.sitepoint.com/getting-started-sqlite3-basic-commands/) | Getting Started with SQLite3 – Basic Commands. | | [GraphQL Documentation](http://graphql.org/graphql-js/) | A query language for your API. | From 25271bc61a69033b3a3f886e2f42b480683bf386 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 16:16:37 +0100 Subject: [PATCH 11/25] Improve auth faker --- test/e2e/api/users.test.ts | 2 +- test/e2e/utils/auth.ts | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/test/e2e/api/users.test.ts b/test/e2e/api/users.test.ts index 4cc72192..f21707f1 100644 --- a/test/e2e/api/users.test.ts +++ b/test/e2e/api/users.test.ts @@ -24,7 +24,7 @@ describe('/api/users', () => { beforeAll(async () => synchronizeDatabase(settings.connection)); beforeAll(async () => factory = getFactory(settings.connection)); beforeAll(async () => bruce = await factory.runSeed(CreateBruce)); - beforeAll(async () => authServer = fakeAuthenticationForUser(bruce)); + beforeAll(async () => authServer = fakeAuthenticationForUser(bruce, true)); // ------------------------------------------------------------------------- // Tear down diff --git a/test/e2e/utils/auth.ts b/test/e2e/utils/auth.ts index 6aa44bb8..778af840 100644 --- a/test/e2e/utils/auth.ts +++ b/test/e2e/utils/auth.ts @@ -3,11 +3,15 @@ import { User } from '../../../src/api/models/User'; import { env } from '../../../src/core/env'; -export const fakeAuthenticationForUser = (user: User): nock.Scope => { - return nock(env.auth.route) - .persist() +export const fakeAuthenticationForUser = (user: User, persist = false): nock.Scope => { + const scope = nock(env.auth.route) + // .persist() .post('') .reply(200, { user_id: `auth0|${user.email}`, }); + if (persist) { + scope.persist(); + } + return scope; }; From 7b70468fb15c3b62c6bc8f19a49d8aea8731e180 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 16:18:09 +0100 Subject: [PATCH 12/25] Remove unused code --- test/e2e/utils/auth.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/e2e/utils/auth.ts b/test/e2e/utils/auth.ts index 778af840..eb8d832c 100644 --- a/test/e2e/utils/auth.ts +++ b/test/e2e/utils/auth.ts @@ -5,7 +5,6 @@ import { env } from '../../../src/core/env'; export const fakeAuthenticationForUser = (user: User, persist = false): nock.Scope => { const scope = nock(env.auth.route) - // .persist() .post('') .reply(200, { user_id: `auth0|${user.email}`, From f518ecbc5575de14b6632ce0e758793c8cc5f1c1 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 16:19:44 +0100 Subject: [PATCH 13/25] Refactoring e2e testing --- test/e2e/api/info.test.ts | 17 ++++++++++++++++- test/e2e/api/users.test.ts | 1 + test/e2e/utils/bootstrap.ts | 3 +-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/test/e2e/api/info.test.ts b/test/e2e/api/info.test.ts index ab0ba911..810ff40b 100644 --- a/test/e2e/api/info.test.ts +++ b/test/e2e/api/info.test.ts @@ -2,14 +2,29 @@ import { Application } from 'express'; import * as request from 'supertest'; import { bootstrapApp, BootstrapSettings } from '../utils/bootstrap'; import { env } from '../../../src/core/env'; -import { synchronizeDatabase } from '../../integration/utils/database'; +import { synchronizeDatabase, closeDatabase } from '../../integration/utils/database'; + describe('/api', () => { + // ------------------------------------------------------------------------- + // Tear up + // ------------------------------------------------------------------------- + let settings: BootstrapSettings; beforeAll(async () => settings = await bootstrapApp()); beforeAll(async () => synchronizeDatabase(settings.connection)); + // ------------------------------------------------------------------------- + // Tear down + // ------------------------------------------------------------------------- + + afterAll(async () => closeDatabase(settings.connection)); + + // ------------------------------------------------------------------------- + // Test cases + // ------------------------------------------------------------------------- + test('GET: / should return the api-version', async (done) => { const response = await request(settings.app) .get('/api') diff --git a/test/e2e/api/users.test.ts b/test/e2e/api/users.test.ts index f21707f1..ce65b884 100644 --- a/test/e2e/api/users.test.ts +++ b/test/e2e/api/users.test.ts @@ -10,6 +10,7 @@ import { env } from '../../../src/core/env'; import { synchronizeDatabase, closeDatabase } from '../../integration/utils/database'; import { fakeAuthenticationForUser } from '../utils/auth'; + describe('/api/users', () => { // ------------------------------------------------------------------------- diff --git a/test/e2e/utils/bootstrap.ts b/test/e2e/utils/bootstrap.ts index c0ddefac..7e703b88 100644 --- a/test/e2e/utils/bootstrap.ts +++ b/test/e2e/utils/bootstrap.ts @@ -1,5 +1,3 @@ - - import * as http from 'http'; import { bootstrapMicroframework, Microframework } from 'microframework'; import { Application } from 'express'; @@ -15,6 +13,7 @@ import { iocLoader } from './../../../src/loaders/iocLoader'; import { graphqlLoader } from './../../../src/loaders/graphqlLoader'; import { eventDispatchLoader } from './../../../src/loaders/eventDispatchLoader'; + export interface BootstrapSettings { app: Application; server: http.Server; From 9c0b4589aacfc292ec26929e1b248be19a3309eb Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 16:34:34 +0100 Subject: [PATCH 14/25] Add fixed microframework version from w3tec --- package.json | 2 +- src/app.ts | 2 +- src/loaders/eventDispatchLoader.ts | 2 +- src/loaders/expressLoader.ts | 2 +- src/loaders/graphqlLoader.ts | 2 +- src/loaders/homeLoader.ts | 2 +- src/loaders/iocLoader.ts | 2 +- src/loaders/monitorLoader.ts | 2 +- src/loaders/publicLoader.ts | 2 +- src/loaders/swaggerLoader.ts | 2 +- src/loaders/typeormLoader.ts | 2 +- src/loaders/winstonLoader.ts | 2 +- test/e2e/utils/bootstrap.ts | 2 +- test/e2e/utils/typeormLoader.ts | 2 +- yarn.lock | 6 +++--- 15 files changed, 17 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 752e3b3f..4c70f385 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "helmet": "^3.9.0", "jsonfile": "^4.0.0", "lodash": "^4.17.4", - "microframework": "^0.6.4", + "microframework-w3tec": "^0.6.3", "morgan": "^1.9.0", "mysql": "^2.15.0", "nodemon": "^1.12.1", diff --git a/src/app.ts b/src/app.ts index 7f353a25..8a1b9b79 100644 --- a/src/app.ts +++ b/src/app.ts @@ -11,7 +11,7 @@ import { banner } from './core/banner'; import { Logger } from './core/Logger'; const log = new Logger(__filename); -import { bootstrapMicroframework } from 'microframework'; +import { bootstrapMicroframework } from 'microframework-w3tec'; import { expressLoader } from './loaders/expressLoader'; import { winstonLoader } from './loaders/winstonLoader'; import { typeormLoader } from './loaders/typeormLoader'; diff --git a/src/loaders/eventDispatchLoader.ts b/src/loaders/eventDispatchLoader.ts index 954e14a2..c1cc7756 100644 --- a/src/loaders/eventDispatchLoader.ts +++ b/src/loaders/eventDispatchLoader.ts @@ -1,5 +1,5 @@ import * as glob from 'glob'; -import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework'; +import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework-w3tec'; import { env } from '../core/env'; diff --git a/src/loaders/expressLoader.ts b/src/loaders/expressLoader.ts index 0a885107..309deda6 100644 --- a/src/loaders/expressLoader.ts +++ b/src/loaders/expressLoader.ts @@ -1,6 +1,6 @@ import { Application } from 'express'; import { createExpressServer } from 'routing-controllers'; -import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework'; +import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework-w3tec'; import { env } from '../core/env'; import { authorizationChecker } from '../auth/authorizationChecker'; import { currentUserChecker } from '../auth/currentUserChecker'; diff --git a/src/loaders/graphqlLoader.ts b/src/loaders/graphqlLoader.ts index b53e9ca9..c3350d99 100644 --- a/src/loaders/graphqlLoader.ts +++ b/src/loaders/graphqlLoader.ts @@ -1,4 +1,4 @@ -import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework'; +import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework-w3tec'; import { createGraphQLServer, createDataLoader } from '../lib/graphql'; import { env } from '../core/env'; import { PetRepository } from './../api/repositories/PetRepository'; diff --git a/src/loaders/homeLoader.ts b/src/loaders/homeLoader.ts index 0011d11d..6180ea1e 100644 --- a/src/loaders/homeLoader.ts +++ b/src/loaders/homeLoader.ts @@ -1,5 +1,5 @@ import * as express from 'express'; -import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework'; +import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework-w3tec'; import { env } from '../core/env'; diff --git a/src/loaders/iocLoader.ts b/src/loaders/iocLoader.ts index c1d44ca0..d225be4e 100644 --- a/src/loaders/iocLoader.ts +++ b/src/loaders/iocLoader.ts @@ -1,7 +1,7 @@ import { Container } from 'typedi'; import { useContainer as ormUseContainer } from 'typeorm'; import { useContainer as routingUseContainer } from 'routing-controllers'; -import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework'; +import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework-w3tec'; export const iocLoader: MicroframeworkLoader = (settings: MicroframeworkSettings | undefined) => { diff --git a/src/loaders/monitorLoader.ts b/src/loaders/monitorLoader.ts index 0bf12aea..54ff7a70 100644 --- a/src/loaders/monitorLoader.ts +++ b/src/loaders/monitorLoader.ts @@ -1,6 +1,6 @@ import * as monitor from 'express-status-monitor'; import * as basicAuth from 'express-basic-auth'; -import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework'; +import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework-w3tec'; import { env } from '../core/env'; diff --git a/src/loaders/publicLoader.ts b/src/loaders/publicLoader.ts index 491ca11e..18ea1183 100644 --- a/src/loaders/publicLoader.ts +++ b/src/loaders/publicLoader.ts @@ -1,7 +1,7 @@ import * as path from 'path'; import * as express from 'express'; import * as favicon from 'serve-favicon'; -import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework'; +import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework-w3tec'; export const publicLoader: MicroframeworkLoader = (settings: MicroframeworkSettings | undefined) => { diff --git a/src/loaders/swaggerLoader.ts b/src/loaders/swaggerLoader.ts index 3fadca29..16dbda79 100644 --- a/src/loaders/swaggerLoader.ts +++ b/src/loaders/swaggerLoader.ts @@ -1,7 +1,7 @@ import * as path from 'path'; import * as swaggerUi from 'swagger-ui-express'; import * as basicAuth from 'express-basic-auth'; -import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework'; +import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework-w3tec'; import { env } from '../core/env'; diff --git a/src/loaders/typeormLoader.ts b/src/loaders/typeormLoader.ts index 6d6871fc..9352d96a 100644 --- a/src/loaders/typeormLoader.ts +++ b/src/loaders/typeormLoader.ts @@ -1,5 +1,5 @@ import { createConnection } from 'typeorm'; -import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework'; +import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework-w3tec'; import { env } from '../core/env'; diff --git a/src/loaders/winstonLoader.ts b/src/loaders/winstonLoader.ts index c61f563c..4e693996 100644 --- a/src/loaders/winstonLoader.ts +++ b/src/loaders/winstonLoader.ts @@ -1,4 +1,4 @@ -import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework'; +import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework-w3tec'; import * as winston from 'winston'; import { env } from '../core/env'; diff --git a/test/e2e/utils/bootstrap.ts b/test/e2e/utils/bootstrap.ts index 7e703b88..1bdc8dbf 100644 --- a/test/e2e/utils/bootstrap.ts +++ b/test/e2e/utils/bootstrap.ts @@ -1,5 +1,5 @@ import * as http from 'http'; -import { bootstrapMicroframework, Microframework } from 'microframework'; +import { bootstrapMicroframework, Microframework } from 'microframework-w3tec'; import { Application } from 'express'; import { Connection } from 'typeorm/connection/Connection'; import { expressLoader } from './../../../src/loaders/expressLoader'; diff --git a/test/e2e/utils/typeormLoader.ts b/test/e2e/utils/typeormLoader.ts index 50933070..ca9013bf 100644 --- a/test/e2e/utils/typeormLoader.ts +++ b/test/e2e/utils/typeormLoader.ts @@ -1,7 +1,7 @@ import { createDatabaseConnection } from './../../integration/utils/database'; import { Connection } from 'typeorm/connection/Connection'; import { createConnection } from 'typeorm'; -import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework'; +import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework-w3tec'; import { env } from '../../../src/core/env'; diff --git a/yarn.lock b/yarn.lock index c9d0c495..209af1b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2804,9 +2804,9 @@ methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" -microframework@^0.6.4: - version "0.6.4" - resolved "https://registry.yarnpkg.com/microframework/-/microframework-0.6.4.tgz#668ad0a8f5d7acdfec1bbdc3c01d430ef70021fd" +microframework-w3tec@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/microframework-w3tec/-/microframework-w3tec-0.6.3.tgz#19a672d6a3b021ca3aaf30244b1b28bcea036ec0" dependencies: app-root-path "^2.0.1" From 141bf362149dbb40fe0fa240bd9a90e46fa5da50 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 21:33:07 +0100 Subject: [PATCH 15/25] Create tslint function --- package-scripts.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/package-scripts.js b/package-scripts.js index 8c3ca2ff..9dd39b50 100644 --- a/package-scripts.js +++ b/package-scripts.js @@ -69,7 +69,7 @@ module.exports = { 'nps test.unit.pretest', 'nps test.unit.run' ), - pretest: 'tslint -c ./tslint.json -t stylish ./test/unit/**/*.ts', + pretest: tslint(`./test/unit/**.ts`), run: 'cross-env NODE_ENV=test jest --testPathPattern=unit', verbose: 'nps "test --verbose"', coverage: 'nps "test --coverage"' @@ -80,7 +80,7 @@ module.exports = { 'nps test.integration.pretest', 'nps test.integration.run' ), - pretest: 'tslint -c ./tslint.json -t stylish ./test/integration/**/*.ts', + pretest: tslint(`./test/integration/**.ts`), verbose: 'nps "test.integration --verbose"', // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. run: 'cross-env NODE_ENV=test jest --testPathPattern=integration -i', @@ -91,7 +91,7 @@ module.exports = { 'nps test.e2e.pretest', 'nps test.e2e.run' ), - pretest: 'tslint -c ./tslint.json -t stylish ./test/e2e/**/*.ts', + pretest: tslint(`./test/e2e/**.ts`), verbose: 'nps "test.e2e --verbose"', // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. run: 'cross-env NODE_ENV=test jest --testPathPattern=e2e -i', @@ -100,7 +100,7 @@ module.exports = { /** * Runs TSLint over your project */ - lint: `tslint -c ./tslint.json -p tsconfig.json src/**/*.ts --format stylish`, + lint: tslint(`./src/**/*.ts`), /** * Transpile your app into javascript */ @@ -168,3 +168,7 @@ function run(path) { function runFast(path) { return run(`-F ${path}`); } + +function tslint(path) { + return `tslint -c './tslint.json' '${path}' --format stylish`; +} From af88e28ae26249eee526d738a6d2833b4e265345 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 21:33:28 +0100 Subject: [PATCH 16/25] Adjust e2e documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 345bac23..2ee68263 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ All script are defined in the package.json file, but the most important ones are - Run the unit tests using `npm start test` (There is also a vscode task for this called `test`). - Run the integration tests using `npm start test:integration`. -- Run the e2e tests using `npm start test:e2e` and don't forget to start your application and your [Auth0 Mock Server](https://github.com/hirsch88/auth0-mock-server). +- Run the e2e tests using `npm start test:e2e`. ### Running in dev mode From 262863c8ac59f9af448b6534eab69a15a57f166e Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Tue, 12 Dec 2017 21:33:44 +0100 Subject: [PATCH 17/25] Fix unuesed imports --- test/e2e/api/info.test.ts | 1 - test/e2e/api/users.test.ts | 2 -- test/e2e/utils/bootstrap.ts | 6 +----- test/e2e/utils/typeormLoader.ts | 3 --- test/integration/PetService.test.ts | 10 +++++++--- test/unit/lib/RepositoryMock.ts | 3 --- test/unit/services/UserService.test.ts | 1 - 7 files changed, 8 insertions(+), 18 deletions(-) diff --git a/test/e2e/api/info.test.ts b/test/e2e/api/info.test.ts index 810ff40b..f0663e90 100644 --- a/test/e2e/api/info.test.ts +++ b/test/e2e/api/info.test.ts @@ -1,4 +1,3 @@ -import { Application } from 'express'; import * as request from 'supertest'; import { bootstrapApp, BootstrapSettings } from '../utils/bootstrap'; import { env } from '../../../src/core/env'; diff --git a/test/e2e/api/users.test.ts b/test/e2e/api/users.test.ts index ce65b884..1d13e485 100644 --- a/test/e2e/api/users.test.ts +++ b/test/e2e/api/users.test.ts @@ -1,12 +1,10 @@ import * as nock from 'nock'; import * as request from 'supertest'; -import { Application } from 'express'; import { CreateBruce } from './../../../src/database/seeds/CreateBruce'; import { getFactory } from './../../../src/lib/seeds/index'; import { Factory } from './../../../src/lib/seeds/Factory'; import { User } from './../../../src/api/models/User'; import { bootstrapApp, BootstrapSettings } from '../utils/bootstrap'; -import { env } from '../../../src/core/env'; import { synchronizeDatabase, closeDatabase } from '../../integration/utils/database'; import { fakeAuthenticationForUser } from '../utils/auth'; diff --git a/test/e2e/utils/bootstrap.ts b/test/e2e/utils/bootstrap.ts index 1bdc8dbf..db29ae29 100644 --- a/test/e2e/utils/bootstrap.ts +++ b/test/e2e/utils/bootstrap.ts @@ -1,16 +1,12 @@ import * as http from 'http'; -import { bootstrapMicroframework, Microframework } from 'microframework-w3tec'; +import { bootstrapMicroframework } from 'microframework-w3tec'; import { Application } from 'express'; import { Connection } from 'typeorm/connection/Connection'; import { expressLoader } from './../../../src/loaders/expressLoader'; import { winstonLoader } from './../../../src/loaders/winstonLoader'; -import { swaggerLoader } from './../../../src/loaders/swaggerLoader'; -import { monitorLoader } from './../../../src/loaders/monitorLoader'; import { homeLoader } from './../../../src/loaders/homeLoader'; import { typeormLoader } from '../utils/typeormLoader'; -import { publicLoader } from './../../../src/loaders/publicLoader'; import { iocLoader } from './../../../src/loaders/iocLoader'; -import { graphqlLoader } from './../../../src/loaders/graphqlLoader'; import { eventDispatchLoader } from './../../../src/loaders/eventDispatchLoader'; diff --git a/test/e2e/utils/typeormLoader.ts b/test/e2e/utils/typeormLoader.ts index ca9013bf..495fc582 100644 --- a/test/e2e/utils/typeormLoader.ts +++ b/test/e2e/utils/typeormLoader.ts @@ -1,8 +1,5 @@ import { createDatabaseConnection } from './../../integration/utils/database'; -import { Connection } from 'typeorm/connection/Connection'; -import { createConnection } from 'typeorm'; import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework-w3tec'; -import { env } from '../../../src/core/env'; export const typeormLoader: MicroframeworkLoader = async (settings: MicroframeworkSettings | undefined) => { diff --git a/test/integration/PetService.test.ts b/test/integration/PetService.test.ts index e84c384d..fda0e20c 100644 --- a/test/integration/PetService.test.ts +++ b/test/integration/PetService.test.ts @@ -1,5 +1,5 @@ import { Container } from 'typedi'; -import { createConnection, useContainer, Connection } from 'typeorm'; +import { Connection } from 'typeorm'; import { Pet } from '../../src/api/models/Pet'; import { PetService } from './../../src/api/services/PetService'; @@ -22,8 +22,12 @@ describe('PetService', () => { expect(resultCreate.age).toBe(pet.age); const resultFind = await service.findOne(resultCreate.id); - expect(resultFind.name).toBe(pet.name); - expect(resultFind.age).toBe(pet.age); + if (resultFind) { + expect(resultFind.name).toBe(pet.name); + expect(resultFind.age).toBe(pet.age); + } else { + fail('Could not find pet'); + } done(); }); diff --git a/test/unit/lib/RepositoryMock.ts b/test/unit/lib/RepositoryMock.ts index 6b11d8de..90f72625 100644 --- a/test/unit/lib/RepositoryMock.ts +++ b/test/unit/lib/RepositoryMock.ts @@ -1,6 +1,3 @@ -import { validate } from 'class-validator'; - - export class RepositoryMock { public one: T; diff --git a/test/unit/services/UserService.test.ts b/test/unit/services/UserService.test.ts index ef55afc1..8a84ad2b 100644 --- a/test/unit/services/UserService.test.ts +++ b/test/unit/services/UserService.test.ts @@ -1,5 +1,4 @@ import { UserService } from '../../../src/api/services/UserService'; -import { UserRepository } from '../../../src/api/repositories/UserRepository'; import { User } from '../../../src/api/models/User'; import { events } from '../../../src/api/subscribers/events'; import { LogMock } from '../lib/LogMock'; From 06d2415cf7cdf4f149bf5e3b7709020bf20d7915 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Wed, 13 Dec 2017 06:36:59 +0100 Subject: [PATCH 18/25] Improve typescript configurations for test cases --- .gitignore | 1 + .vscode/settings.json | 6 +++++- package-scripts.js | 18 +++++++++++++----- src/lib/tsconfig.ts | 18 ++++++++++++++++++ tsconfig.json | 5 +---- 5 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 src/lib/tsconfig.ts diff --git a/.gitignore b/.gitignore index 73268fb2..5797fd36 100755 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ typings/ # Dist # dist/ ormconfig.json +tsconfig.build.json # IDE # .idea/ diff --git a/.vscode/settings.json b/.vscode/settings.json index 5fe3577b..91b76b5f 100755 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,8 @@ { "typescript.tsdk": "./node_modules/typescript/lib", - "cSpell.enabled": true + "cSpell.enabled": true, + "files.exclude": { + "tsconfig.build.json": true, + "ormconfig.json": true + } } diff --git a/package-scripts.js b/package-scripts.js index 9dd39b50..bbdd5f05 100644 --- a/package-scripts.js +++ b/package-scripts.js @@ -23,6 +23,7 @@ module.exports = { */ setup: series( 'yarn install', + 'nps config', 'nps db.migrate', 'nps db.seed' ), @@ -31,6 +32,7 @@ module.exports = { */ build: series( 'nps banner.build', + 'nps config', 'nps lint', 'nps clean.dist', 'nps transpile', @@ -42,22 +44,28 @@ module.exports = { db: { migrate: series( 'nps banner.migrate', - 'nps db.config', + 'nps config', runFast('./node_modules/typeorm/cli.js migrations:run') ), revert: series( 'nps banner.revert', - 'nps db.config', + 'nps config', runFast('./node_modules/typeorm/cli.js migrations:revert') ), seed: series( 'nps banner.seed', - 'nps db.config', + 'nps config', runFast('./src/lib/seeds/cli.ts') ), - config: runFast('./src/lib/ormconfig.ts'), drop: runFast('./node_modules/typeorm/cli.js schema:drop') }, + /** + * Creates the needed configuration files + */ + config: series( + runFast('./src/lib/tsconfig.ts'), + runFast('./src/lib/ormconfig.ts') + ), /** * These run various kinds of tests. Default is unit. */ @@ -104,7 +112,7 @@ module.exports = { /** * Transpile your app into javascript */ - transpile: `tsc`, + transpile: `tsc --project ./tsconfig.build.json`, /** * Clean files and folders */ diff --git a/src/lib/tsconfig.ts b/src/lib/tsconfig.ts new file mode 100644 index 00000000..ee375f22 --- /dev/null +++ b/src/lib/tsconfig.ts @@ -0,0 +1,18 @@ +import * as path from 'path'; +import * as jsonfile from 'jsonfile'; +import * as tsconfig from '../../tsconfig.json'; + + +const content: any = tsconfig; +content.include = [ + 'src/**/*', +]; + +const filePath = path.join(process.cwd(), 'tsconfig.build.json'); +jsonfile.writeFile(filePath, content, { spaces: 2 }, (err) => { + if (err === null) { + console.log('Successfully generated tsconfig.build.json form the .env file'); + } else { + console.error('Failed to generate the otsconfig.build.json', err); + } +}); diff --git a/tsconfig.json b/tsconfig.json index 9f803e59..897048de 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,8 +20,5 @@ "allowSyntheticDefaultImports": true, "experimentalDecorators": true, "emitDecoratorMetadata": true - }, - "include": [ - "src/**/*" - ] + } } From 441c1a699842b5eafee3a228d4f98f912b42e344 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Wed, 13 Dec 2017 07:14:40 +0100 Subject: [PATCH 19/25] Improve nps --- {src/lib => commands}/banner.ts | 0 {src/lib => commands}/ormconfig.ts | 10 +- {src/lib => commands}/tsconfig.ts | 10 +- package-scripts.js | 327 ++++++++++++++++++++--------- 4 files changed, 241 insertions(+), 106 deletions(-) rename {src/lib => commands}/banner.ts (100%) rename {src/lib => commands}/ormconfig.ts (72%) rename {src/lib => commands}/tsconfig.ts (58%) diff --git a/src/lib/banner.ts b/commands/banner.ts similarity index 100% rename from src/lib/banner.ts rename to commands/banner.ts diff --git a/src/lib/ormconfig.ts b/commands/ormconfig.ts similarity index 72% rename from src/lib/ormconfig.ts rename to commands/ormconfig.ts index 667e64f4..e29d45ff 100644 --- a/src/lib/ormconfig.ts +++ b/commands/ormconfig.ts @@ -2,8 +2,9 @@ import * as dotenv from 'dotenv'; dotenv.config(); import * as path from 'path'; +import * as Chalk from 'chalk'; import * as jsonfile from 'jsonfile'; -import { env } from '../core/env'; +import { env } from '../src/core/env'; const content = { @@ -23,8 +24,13 @@ const content = { const filePath = path.join(process.cwd(), 'ormconfig.json'); jsonfile.writeFile(filePath, content, { spaces: 2 }, (err) => { if (err === null) { - console.log('Successfully generated ormconfig.json form the .env file'); + const chalk = Chalk.default; + console.log('👍 ', + chalk.gray.underline('generated:'), + chalk.blue.bold('ormconfig.json') + ); } else { console.error('Failed to generate the ormconfig.json', err); + process.exit(1); } }); diff --git a/src/lib/tsconfig.ts b/commands/tsconfig.ts similarity index 58% rename from src/lib/tsconfig.ts rename to commands/tsconfig.ts index ee375f22..d259b055 100644 --- a/src/lib/tsconfig.ts +++ b/commands/tsconfig.ts @@ -1,6 +1,7 @@ import * as path from 'path'; +import * as Chalk from 'chalk'; import * as jsonfile from 'jsonfile'; -import * as tsconfig from '../../tsconfig.json'; +import * as tsconfig from '../tsconfig.json'; const content: any = tsconfig; @@ -11,8 +12,13 @@ content.include = [ const filePath = path.join(process.cwd(), 'tsconfig.build.json'); jsonfile.writeFile(filePath, content, { spaces: 2 }, (err) => { if (err === null) { - console.log('Successfully generated tsconfig.build.json form the .env file'); + const chalk = Chalk.default; + console.log('👍 ', + chalk.gray.underline('generated:'), + chalk.blue.bold('tsconfig.build.json') + ); } else { console.error('Failed to generate the otsconfig.build.json', err); + process.exit(1); } }); diff --git a/package-scripts.js b/package-scripts.js index bbdd5f05..d6060181 100644 --- a/package-scripts.js +++ b/package-scripts.js @@ -10,135 +10,257 @@ module.exports = { /** * Starts the builded app from the dist directory */ - start: 'node dist/app.js', + start: { + script: 'node dist/app.js', + description: 'Starts the builded app from the dist directory' + }, /** * Serves the current app and watches for changes to restart it */ - serve: series( - 'nps banner.serve', - 'nodemon --watch src --watch .env' - ), + serve: { + script: series( + 'nps banner.serve', + 'nodemon --watch src --watch .env' + ), + description: 'Serves the current app and watches for changes to restart it' + }, /** * Setup's the development environment and the database */ - setup: series( - 'yarn install', - 'nps config', - 'nps db.migrate', - 'nps db.seed' - ), - /** - * Builds the app into the dist directory - */ - build: series( - 'nps banner.build', - 'nps config', - 'nps lint', - 'nps clean.dist', - 'nps transpile', - 'nps copy' - ), - /** - * Database scripts - */ - db: { - migrate: series( - 'nps banner.migrate', - 'nps config', - runFast('./node_modules/typeorm/cli.js migrations:run') - ), - revert: series( - 'nps banner.revert', - 'nps config', - runFast('./node_modules/typeorm/cli.js migrations:revert') - ), - seed: series( - 'nps banner.seed', + setup: { + script: series( + 'yarn install', + copy( + '.env.example', + '.env' + ), 'nps config', - runFast('./src/lib/seeds/cli.ts') + 'nps db.migrate', + 'nps db.seed' ), - drop: runFast('./node_modules/typeorm/cli.js schema:drop') + description: 'Setup`s the development environment and the database' }, /** * Creates the needed configuration files */ - config: series( - runFast('./src/lib/tsconfig.ts'), - runFast('./src/lib/ormconfig.ts') - ), + config: { + script: series( + runFast('./commands/tsconfig.ts'), + runFast('./commands/ormconfig.ts') + ), + hiddenFromHelp: true + }, /** - * These run various kinds of tests. Default is unit. + * Builds the app into the dist directory */ - test: { - default: 'nps test.unit', - unit: { - default: series( - 'nps banner.test', - 'nps test.unit.pretest', - 'nps test.unit.run' - ), - pretest: tslint(`./test/unit/**.ts`), - run: 'cross-env NODE_ENV=test jest --testPathPattern=unit', - verbose: 'nps "test --verbose"', - coverage: 'nps "test --coverage"' - }, - integration: { - default: series( - 'nps banner.test', - 'nps test.integration.pretest', - 'nps test.integration.run' - ), - pretest: tslint(`./test/integration/**.ts`), - verbose: 'nps "test.integration --verbose"', - // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. - run: 'cross-env NODE_ENV=test jest --testPathPattern=integration -i', - }, - e2e: { - default: series( - 'nps banner.test', - 'nps test.e2e.pretest', - 'nps test.e2e.run' - ), - pretest: tslint(`./test/e2e/**.ts`), - verbose: 'nps "test.e2e --verbose"', - // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. - run: 'cross-env NODE_ENV=test jest --testPathPattern=e2e -i', - } + build: { + script: series( + 'nps banner.build', + 'nps config', + 'nps lint', + 'nps clean.dist', + 'nps transpile', + 'nps copy' + ), + description: 'Builds the app into the dist directory' }, /** * Runs TSLint over your project */ - lint: tslint(`./src/**/*.ts`), + lint: { + script: tslint(`./src/**/*.ts`), + hiddenFromHelp: true + }, /** * Transpile your app into javascript */ - transpile: `tsc --project ./tsconfig.build.json`, + transpile: { + script: `tsc --project ./tsconfig.build.json`, + hiddenFromHelp: true + }, /** * Clean files and folders */ clean: { - default: series( - `nps banner.clean`, - `nps clean.dist` - ), - dist: rimraf('./dist') + default: { + script: series( + `nps banner.clean`, + `nps clean.dist` + ), + description: 'Deletes the ./dist folder' + }, + dist: { + script: rimraf('./dist'), + hiddenFromHelp: true + } }, /** * Copies static files to the build folder */ copy: { - default: series( - `nps copy.swagger`, - `nps copy.public` - ), - swagger: copy( - './src/api/swagger.json', - './dist' - ), - public: copy( - './src/public/*', - './dist' - ) + default: { + script: series( + `nps copy.swagger`, + `nps copy.public` + ), + hiddenFromHelp: true + }, + swagger: { + script: copy( + './src/api/swagger.json', + './dist' + ), + hiddenFromHelp: true + }, + public: { + script: copy( + './src/public/*', + './dist' + ), + hiddenFromHelp: true + } + }, + /** + * Database scripts + */ + db: { + migrate: { + script: series( + 'nps banner.migrate', + 'nps config', + runFast('./node_modules/typeorm/cli.js migrations:run') + ), + description: 'Migrates the database to newest version available' + }, + revert: { + script: series( + 'nps banner.revert', + 'nps config', + runFast('./node_modules/typeorm/cli.js migrations:revert') + ), + description: 'Downgrades the database' + }, + seed: { + script: series( + 'nps banner.seed', + 'nps config', + runFast('./src/lib/seeds/cli.ts') + ), + description: 'Seeds generated records into the database' + }, + drop: { + script: runFast('./node_modules/typeorm/cli.js schema:drop'), + description: 'Drops the schema of the database' + } + + }, + /** + * These run various kinds of tests. Default is unit. + */ + test: { + default: 'nps test.unit', + unit: { + default: { + script: series( + 'nps banner.testUnit', + 'nps test.unit.pretest', + 'nps test.unit.run' + ), + description: 'Runs the unit tests' + }, + pretest: { + script: tslint(`./test/unit/**.ts`), + hiddenFromHelp: true + }, + run: { + script: 'cross-env NODE_ENV=test jest --testPathPattern=unit', + hiddenFromHelp: true + }, + verbose: { + script: 'nps "test --verbose"', + hiddenFromHelp: true + }, + coverage: { + script: 'nps "test --coverage"', + hiddenFromHelp: true + } + }, + integration: { + default: { + script: series( + 'nps banner.testIntegration', + 'nps test.integration.pretest', + 'nps test.integration.run' + ), + description: 'Runs the integration tests' + }, + pretest: { + script: tslint(`./test/integration/**.ts`), + hiddenFromHelp: true + }, + run: { + // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. + script: 'cross-env NODE_ENV=test jest --testPathPattern=integration -i', + hiddenFromHelp: true + }, + verbose: { + script: 'nps "test --verbose"', + hiddenFromHelp: true + }, + coverage: { + script: 'nps "test --coverage"', + hiddenFromHelp: true + } + }, + e2e: { + default: { + script: series( + 'nps banner.testE2E', + 'nps test.e2e.pretest', + 'nps test.e2e.run' + ), + description: 'Runs the e2e tests' + }, + pretest: { + script: tslint(`./test/e2e/**.ts`), + hiddenFromHelp: true + }, + run: { + // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. + script: 'cross-env NODE_ENV=test jest --testPathPattern=e2e -i', + hiddenFromHelp: true + }, + verbose: { + script: 'nps "test --verbose"', + hiddenFromHelp: true + }, + coverage: { + script: 'nps "test --coverage"', + hiddenFromHelp: true + } + }, + // integration: { + // default: series( + // 'nps banner.testIntegration', + // 'nps test.integration.pretest', + // 'nps test.integration.run' + // ), + // pretest: tslint(`./test/integration/**.ts`), + // verbose: 'nps "test.integration --verbose"', + // // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. + // run: 'cross-env NODE_ENV=test jest --testPathPattern=integration -i', + // }, + // e2e: { + // default: series( + // 'nps banner.testE2E', + // 'nps test.e2e.pretest', + // 'nps test.e2e.run' + // ), + // pretest: tslint(`./test/e2e/**.ts`), + // verbose: 'nps "test.e2e --verbose"', + // // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. + // run: 'cross-env NODE_ENV=test jest --testPathPattern=e2e -i', + // } }, /** * This creates pretty banner to the terminal @@ -146,7 +268,9 @@ module.exports = { banner: { build: banner('build'), serve: banner('serve'), - test: banner('test'), + testUnit: banner('test.unit'), + testIntegration: banner('test.integration'), + testE2E: banner('test.e2e'), migrate: banner('migrate'), seed: banner('seed'), revert: banner('revert'), @@ -159,9 +283,8 @@ function banner(name) { return { hiddenFromHelp: true, silent: true, - logLevel: 'error', description: `Shows ${name} banners to the console`, - script: runFast(`./src/lib/banner.ts ${name}`), + script: runFast(`./commands/banner.ts ${name}`), }; } From 1ddddf0fe3a4f73c0d66b841176aceebc92d7273 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Wed, 13 Dec 2017 07:44:45 +0100 Subject: [PATCH 20/25] Refactor test utils --- test/e2e/api/info.test.ts | 8 -------- test/e2e/api/users.test.ts | 2 +- test/integration/PetService.test.ts | 2 +- test/{integration => }/utils/database.ts | 0 4 files changed, 2 insertions(+), 10 deletions(-) rename test/{integration => }/utils/database.ts (100%) diff --git a/test/e2e/api/info.test.ts b/test/e2e/api/info.test.ts index f0663e90..fada4cc9 100644 --- a/test/e2e/api/info.test.ts +++ b/test/e2e/api/info.test.ts @@ -1,7 +1,6 @@ import * as request from 'supertest'; import { bootstrapApp, BootstrapSettings } from '../utils/bootstrap'; import { env } from '../../../src/core/env'; -import { synchronizeDatabase, closeDatabase } from '../../integration/utils/database'; describe('/api', () => { @@ -12,13 +11,6 @@ describe('/api', () => { let settings: BootstrapSettings; beforeAll(async () => settings = await bootstrapApp()); - beforeAll(async () => synchronizeDatabase(settings.connection)); - - // ------------------------------------------------------------------------- - // Tear down - // ------------------------------------------------------------------------- - - afterAll(async () => closeDatabase(settings.connection)); // ------------------------------------------------------------------------- // Test cases diff --git a/test/e2e/api/users.test.ts b/test/e2e/api/users.test.ts index 1d13e485..e6cf1d54 100644 --- a/test/e2e/api/users.test.ts +++ b/test/e2e/api/users.test.ts @@ -5,7 +5,7 @@ import { getFactory } from './../../../src/lib/seeds/index'; import { Factory } from './../../../src/lib/seeds/Factory'; import { User } from './../../../src/api/models/User'; import { bootstrapApp, BootstrapSettings } from '../utils/bootstrap'; -import { synchronizeDatabase, closeDatabase } from '../../integration/utils/database'; +import { synchronizeDatabase, closeDatabase } from '../../utils/database'; import { fakeAuthenticationForUser } from '../utils/auth'; diff --git a/test/integration/PetService.test.ts b/test/integration/PetService.test.ts index fda0e20c..7839413b 100644 --- a/test/integration/PetService.test.ts +++ b/test/integration/PetService.test.ts @@ -3,7 +3,7 @@ import { Connection } from 'typeorm'; import { Pet } from '../../src/api/models/Pet'; import { PetService } from './../../src/api/services/PetService'; -import { createDatabaseConnection, synchronizeDatabase, closeDatabase } from './utils/database'; +import { createDatabaseConnection, synchronizeDatabase, closeDatabase } from '../utils/database'; describe('PetService', () => { diff --git a/test/integration/utils/database.ts b/test/utils/database.ts similarity index 100% rename from test/integration/utils/database.ts rename to test/utils/database.ts From 819fc05d65b9b1576a9980d68409ff2abad07a60 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Wed, 13 Dec 2017 07:45:55 +0100 Subject: [PATCH 21/25] Refactor test utils --- test/e2e/api/info.test.ts | 2 +- test/e2e/api/users.test.ts | 2 +- test/integration/PetService.test.ts | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/test/e2e/api/info.test.ts b/test/e2e/api/info.test.ts index fada4cc9..b1025ab5 100644 --- a/test/e2e/api/info.test.ts +++ b/test/e2e/api/info.test.ts @@ -6,7 +6,7 @@ import { env } from '../../../src/core/env'; describe('/api', () => { // ------------------------------------------------------------------------- - // Tear up + // Setup up // ------------------------------------------------------------------------- let settings: BootstrapSettings; diff --git a/test/e2e/api/users.test.ts b/test/e2e/api/users.test.ts index e6cf1d54..17495a65 100644 --- a/test/e2e/api/users.test.ts +++ b/test/e2e/api/users.test.ts @@ -12,7 +12,7 @@ import { fakeAuthenticationForUser } from '../utils/auth'; describe('/api/users', () => { // ------------------------------------------------------------------------- - // Tear up + // Setup up // ------------------------------------------------------------------------- let settings: BootstrapSettings; diff --git a/test/integration/PetService.test.ts b/test/integration/PetService.test.ts index 7839413b..cca010ba 100644 --- a/test/integration/PetService.test.ts +++ b/test/integration/PetService.test.ts @@ -7,11 +7,24 @@ import { createDatabaseConnection, synchronizeDatabase, closeDatabase } from '.. describe('PetService', () => { + // ------------------------------------------------------------------------- + // Setup up + // ------------------------------------------------------------------------- + let connection: Connection; beforeAll(async () => connection = await createDatabaseConnection()); beforeEach(() => synchronizeDatabase(connection)); + + // ------------------------------------------------------------------------- + // Tear down + // ------------------------------------------------------------------------- + afterAll(() => closeDatabase(connection)); + // ------------------------------------------------------------------------- + // Test cases + // ------------------------------------------------------------------------- + test('should create a new pet in the database', async (done) => { const pet = new Pet(); pet.name = 'test'; From 09fbca09d3bd4fbe341560d24665cb8e23dcd0cc Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Wed, 13 Dec 2017 07:48:05 +0100 Subject: [PATCH 22/25] Refactor entity factory --- src/lib/seeds/EntityFactory.ts | 11 +++++++++++ src/lib/seeds/EntityFactoryInterface.ts | 6 ++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/lib/seeds/EntityFactory.ts b/src/lib/seeds/EntityFactory.ts index 839842bf..fc82131c 100644 --- a/src/lib/seeds/EntityFactory.ts +++ b/src/lib/seeds/EntityFactory.ts @@ -24,6 +24,17 @@ export class EntityFactory implements EntityFactoryInterface { return await this.makeEntity(this.blueprint.create(this.faker, this.args)); } + public async makeMany(amount: number): Promise { + const results: Entity[] = []; + for (let i = 0; i < amount; i++) { + const entity = await this.makeEntity(this.blueprint.create(this.faker, this.args)); + if (entity) { + results.push(entity); + } + } + return results; + } + public async create(): Promise { const entity = await this.build(); if (typeof this.eachFn === 'function') { diff --git a/src/lib/seeds/EntityFactoryInterface.ts b/src/lib/seeds/EntityFactoryInterface.ts index 10fbaf92..95547df2 100644 --- a/src/lib/seeds/EntityFactoryInterface.ts +++ b/src/lib/seeds/EntityFactoryInterface.ts @@ -10,14 +10,12 @@ export interface EntityFactoryInterface { * Creates a entity with faked data, but not persisted to the database. */ make(): Promise; + makeMany(amount: number): Promise; /** * Creates a new faked entity in the database. */ create(): Promise; - /** - * Creates an amount (default 1) of the defined entity. - */ - createMany(amount: number): Promise; + createMany(amount: number): Promise; /** * This is called after creating a enity to the database. Use this to * create other seeds but combined with this enitiy. From bc3b95043905391638c141ed06ab052b3e703e07 Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Wed, 13 Dec 2017 07:49:21 +0100 Subject: [PATCH 23/25] Refactor nps --- package-scripts.js | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/package-scripts.js b/package-scripts.js index d6060181..c214525e 100644 --- a/package-scripts.js +++ b/package-scripts.js @@ -239,28 +239,6 @@ module.exports = { hiddenFromHelp: true } }, - // integration: { - // default: series( - // 'nps banner.testIntegration', - // 'nps test.integration.pretest', - // 'nps test.integration.run' - // ), - // pretest: tslint(`./test/integration/**.ts`), - // verbose: 'nps "test.integration --verbose"', - // // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. - // run: 'cross-env NODE_ENV=test jest --testPathPattern=integration -i', - // }, - // e2e: { - // default: series( - // 'nps banner.testE2E', - // 'nps test.e2e.pretest', - // 'nps test.e2e.run' - // ), - // pretest: tslint(`./test/e2e/**.ts`), - // verbose: 'nps "test.e2e --verbose"', - // // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. - // run: 'cross-env NODE_ENV=test jest --testPathPattern=e2e -i', - // } }, /** * This creates pretty banner to the terminal @@ -301,5 +279,5 @@ function runFast(path) { } function tslint(path) { - return `tslint -c './tslint.json' '${path}' --format stylish`; + return `tslint -c ./tslint.json ${path} --format stylish`; } From 282e75830582dc7e45b7ea1ec7ae5fab9462e3ee Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Wed, 13 Dec 2017 17:12:11 +0100 Subject: [PATCH 24/25] Fix tests --- test/e2e/utils/typeormLoader.ts | 2 +- test/utils/database.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/e2e/utils/typeormLoader.ts b/test/e2e/utils/typeormLoader.ts index 495fc582..31129e27 100644 --- a/test/e2e/utils/typeormLoader.ts +++ b/test/e2e/utils/typeormLoader.ts @@ -1,4 +1,4 @@ -import { createDatabaseConnection } from './../../integration/utils/database'; +import { createDatabaseConnection } from './../../utils/database'; import { MicroframeworkSettings, MicroframeworkLoader } from 'microframework-w3tec'; diff --git a/test/utils/database.ts b/test/utils/database.ts index f3f3a569..fa5996be 100644 --- a/test/utils/database.ts +++ b/test/utils/database.ts @@ -1,6 +1,7 @@ import { Container } from 'typedi'; import { createConnection, useContainer, Connection } from 'typeorm'; -import { env } from '../../../src/core/env'; +import { env } from '../../src/core/env'; + export const createDatabaseConnection = async (): Promise => { useContainer(Container); From 7700cab853008e06c7ecd674798ddeaed2a000dd Mon Sep 17 00:00:00 2001 From: hirsch88 Date: Wed, 13 Dec 2017 21:11:03 +0100 Subject: [PATCH 25/25] Add migration to the e2e tests --- src/api/models/Pet.ts | 3 +- src/api/repositories/PetRepository.ts | 4 +- .../1511105183653-CreateUserTable.ts | 39 ++++++++++++++----- .../1512663524808-CreatePetTable.ts | 39 ++++++++++++++----- ...1512663990063-AddUserRelationToPetTable.ts | 22 +++++------ src/loaders/typeormLoader.ts | 1 + test/e2e/api/users.test.ts | 4 +- test/integration/PetService.test.ts | 4 +- test/utils/database.ts | 9 ++++- 9 files changed, 88 insertions(+), 37 deletions(-) diff --git a/src/api/models/Pet.ts b/src/api/models/Pet.ts index 8d353834..948c50f6 100644 --- a/src/api/models/Pet.ts +++ b/src/api/models/Pet.ts @@ -18,12 +18,13 @@ export class Pet { public age: number; @Column({ + name: 'user_id', nullable: true, }) public userId: number; @ManyToOne(type => User, user => user.pets) - @JoinColumn({ name: 'userId' }) + @JoinColumn({ name: 'user_id' }) public user: User; public toString(): string { diff --git a/src/api/repositories/PetRepository.ts b/src/api/repositories/PetRepository.ts index 44673f0c..dca710f5 100644 --- a/src/api/repositories/PetRepository.ts +++ b/src/api/repositories/PetRepository.ts @@ -5,12 +5,12 @@ import { Pet } from '../models/Pet'; export class PetRepository extends Repository { /** - * Find by userId is used for our data-loader to get all needed pets in one query. + * Find by user_id is used for our data-loader to get all needed pets in one query. */ public findByUserIds(ids: string[]): Promise { return this.createQueryBuilder() .select() - .where(`pet.userId IN (${ids.map(id => `'${id}'`).join(', ')})`) + .where(`pet.user_id IN (${ids.map(id => `'${id}'`).join(', ')})`) .getMany(); } diff --git a/src/database/migrations/1511105183653-CreateUserTable.ts b/src/database/migrations/1511105183653-CreateUserTable.ts index e79b1e1d..fee0ac03 100644 --- a/src/database/migrations/1511105183653-CreateUserTable.ts +++ b/src/database/migrations/1511105183653-CreateUserTable.ts @@ -1,19 +1,40 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import { MigrationInterface, QueryRunner, Table } from 'typeorm'; export class CreateUserTable1511105183653 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE TABLE \`user\` ( - \`id\` varchar(255) NOT NULL PRIMARY KEY, - \`first_name\` varchar(255) NOT NULL, - \`last_name\` varchar(255) NOT NULL, - \`email\` varchar(255) NOT NULL) ENGINE=InnoDB;` - ); + const table = new Table('user', [ + { + name: 'id', + type: 'varchar', + length: 255, + isPrimary: true, + isNullable: false, + }, { + name: 'first_name', + type: 'varchar', + length: 255, + isPrimary: false, + isNullable: false, + }, { + name: 'last_name', + type: 'varchar', + length: 255, + isPrimary: false, + isNullable: false, + }, { + name: 'email', + type: 'varchar', + length: 255, + isPrimary: false, + isNullable: false, + }, + ]); + await queryRunner.createTable(table); } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE \`user\`;`); + await queryRunner.dropTable('user'); } } diff --git a/src/database/migrations/1512663524808-CreatePetTable.ts b/src/database/migrations/1512663524808-CreatePetTable.ts index 479459b4..55f68e74 100644 --- a/src/database/migrations/1512663524808-CreatePetTable.ts +++ b/src/database/migrations/1512663524808-CreatePetTable.ts @@ -1,19 +1,40 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import { MigrationInterface, QueryRunner, Table } from 'typeorm'; export class CreatePetTable1512663524808 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE TABLE \`pet\` ( - \`id\` varchar(255) NOT NULL PRIMARY KEY, - \`name\` varchar(255) NOT NULL, - \`age\` int(11) NOT NULL, - \`userId\` varchar(255)) ENGINE=InnoDB;` - ); + const table = new Table('pet', [ + { + name: 'id', + type: 'varchar', + length: 255, + isPrimary: true, + isNullable: false, + }, { + name: 'name', + type: 'varchar', + length: 255, + isPrimary: false, + isNullable: false, + }, { + name: 'age', + type: 'int', + length: 11, + isPrimary: false, + isNullable: false, + }, { + name: 'user_id', + type: 'varchar', + length: 255, + isPrimary: false, + isNullable: true, + }, + ]); + await queryRunner.createTable(table); } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE \`pet\`;`); + await queryRunner.dropTable('pet'); } } diff --git a/src/database/migrations/1512663990063-AddUserRelationToPetTable.ts b/src/database/migrations/1512663990063-AddUserRelationToPetTable.ts index 3ee7be34..d83da0d0 100644 --- a/src/database/migrations/1512663990063-AddUserRelationToPetTable.ts +++ b/src/database/migrations/1512663990063-AddUserRelationToPetTable.ts @@ -1,21 +1,21 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import { MigrationInterface, QueryRunner, TableForeignKey } from 'typeorm'; export class AddUserRelationToPetTable1512663990063 implements MigrationInterface { + private tableForeignKey = new TableForeignKey( + 'fk_user_pet', + ['user_id'], + ['id'], + 'user', + '' + ); + public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE \`pet\` - ADD CONSTRAINT \`fk_user_pet\` - FOREIGN KEY (\`userId\`) - REFERENCES \`user\`(\`id\`);` - ); + await queryRunner.createForeignKey('pet', this.tableForeignKey); } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE \`pet\` - DROP FOREIGN KEY \`fk_user_pet\`;` - ); + await queryRunner.dropForeignKey('pet', this.tableForeignKey); } } diff --git a/src/loaders/typeormLoader.ts b/src/loaders/typeormLoader.ts index 9352d96a..0140f844 100644 --- a/src/loaders/typeormLoader.ts +++ b/src/loaders/typeormLoader.ts @@ -15,6 +15,7 @@ export const typeormLoader: MicroframeworkLoader = async (settings: Microframewo synchronize: env.db.synchronize, logging: env.db.logging, entities: env.app.dirs.entities, + migrations: env.app.dirs.migrations, }); if (settings) { diff --git a/test/e2e/api/users.test.ts b/test/e2e/api/users.test.ts index 17495a65..8daaf4ed 100644 --- a/test/e2e/api/users.test.ts +++ b/test/e2e/api/users.test.ts @@ -5,7 +5,7 @@ import { getFactory } from './../../../src/lib/seeds/index'; import { Factory } from './../../../src/lib/seeds/Factory'; import { User } from './../../../src/api/models/User'; import { bootstrapApp, BootstrapSettings } from '../utils/bootstrap'; -import { synchronizeDatabase, closeDatabase } from '../../utils/database'; +import { migrateDatabase, closeDatabase } from '../../utils/database'; import { fakeAuthenticationForUser } from '../utils/auth'; @@ -20,7 +20,7 @@ describe('/api/users', () => { let bruce: User; let authServer: nock.Scope; beforeAll(async () => settings = await bootstrapApp()); - beforeAll(async () => synchronizeDatabase(settings.connection)); + beforeAll(async () => migrateDatabase(settings.connection)); beforeAll(async () => factory = getFactory(settings.connection)); beforeAll(async () => bruce = await factory.runSeed(CreateBruce)); beforeAll(async () => authServer = fakeAuthenticationForUser(bruce, true)); diff --git a/test/integration/PetService.test.ts b/test/integration/PetService.test.ts index cca010ba..3676049f 100644 --- a/test/integration/PetService.test.ts +++ b/test/integration/PetService.test.ts @@ -3,7 +3,7 @@ import { Connection } from 'typeorm'; import { Pet } from '../../src/api/models/Pet'; import { PetService } from './../../src/api/services/PetService'; -import { createDatabaseConnection, synchronizeDatabase, closeDatabase } from '../utils/database'; +import { createDatabaseConnection, migrateDatabase, closeDatabase } from '../utils/database'; describe('PetService', () => { @@ -13,7 +13,7 @@ describe('PetService', () => { let connection: Connection; beforeAll(async () => connection = await createDatabaseConnection()); - beforeEach(() => synchronizeDatabase(connection)); + beforeEach(() => migrateDatabase(connection)); // ------------------------------------------------------------------------- // Tear down diff --git a/test/utils/database.ts b/test/utils/database.ts index fa5996be..38de7bc2 100644 --- a/test/utils/database.ts +++ b/test/utils/database.ts @@ -10,14 +10,21 @@ export const createDatabaseConnection = async (): Promise => { database: env.db.database, logging: env.db.logging, entities: env.app.dirs.entities, + migrations: env.app.dirs.migrations, }); return connection; }; -export const synchronizeDatabase = (connection: Connection) => { +export const synchronizeDatabase = async (connection: Connection) => { + await connection.dropDatabase(); return connection.synchronize(true); }; +export const migrateDatabase = async (connection: Connection) => { + await connection.dropDatabase(); + return connection.runMigrations(); +}; + export const closeDatabase = (connection: Connection) => { return connection.close(); };