From c3a3c39ae0ec23d219a1d0322d9fe3f7cbb25b0b Mon Sep 17 00:00:00 2001 From: hirsch Date: Fri, 7 Jun 2019 20:31:18 +0200 Subject: [PATCH] :recycle: Improve code and add unit tests --- README.md | 25 +++++++++++--- examples/ormconfig.js | 8 ----- src/commands/seed.command.ts | 1 + src/entity-factory.test.ts | 66 ++++++++++++++++++++++++++++++++++++ src/entity-factory.ts | 4 ++- src/helpers.test.ts | 10 ++++++ src/typeorm-seeding.ts | 3 +- src/utils/factory.test.ts | 2 +- src/utils/file.util.test.ts | 26 ++++++++++++++ src/utils/file.util.ts | 5 ++- 10 files changed, 130 insertions(+), 20 deletions(-) delete mode 100644 examples/ormconfig.js create mode 100644 src/entity-factory.test.ts create mode 100644 src/helpers.test.ts create mode 100644 src/utils/file.util.test.ts diff --git a/README.md b/README.md index dbeba494..3bdf0f3a 100755 --- a/README.md +++ b/README.md @@ -33,6 +33,10 @@ How does it work? Just create a factory for your entities (models) and a seed sc ## ❯ Installation +Before using this TypeORM extension please read the [TypeORM Getting Started](https://typeorm.io/#/) documentation. This explains how to setup a TypeORM project. + +You can install our extension with `npm` or `yarn`. + ```bash npm i typeorm-seeding ``` @@ -43,6 +47,18 @@ or with yarn yarn add typeorm-seeding ``` +### Configuration + +To configure the path to your seeds and factories change the TypeORM config file(ormconfig.js or ormconfig.json). + +```JavaScript +module.exports = { + ... + seeds: ['seeds/**/*.seed.ts'], + factories: ['factories/**/*.factory.ts'], +} +``` + ![divider](./w3tec-divider.png) ## ❯ Writing Seeders @@ -171,11 +187,10 @@ Now you are able to execute your seeds with this command `npm run seed`. ### CLI Options -| Option | Default | Description | -| ---------------------- | -------------------- | --------------------------------------------- | -| `--config` or `--c` | `ormconfig.js` | Path to the typeorm config file (json or js). | -| `--seeds` or `--s` | `database/seeds` | Directory where seeds are. | -| `--factories` or `--f` | `database/factories` | Directory where enity factories are. | +| Option | Default | Description | +| ------------------ | -------------- | --------------------------------------------- | +| `--class` or `--c` | null | Option to specify a specific seeder class to run individually | +| `--config` | `ormconfig.js` | Path to the typeorm config file (json or js). | ## ❯ Example diff --git a/examples/ormconfig.js b/examples/ormconfig.js deleted file mode 100644 index c020a280..00000000 --- a/examples/ormconfig.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - seeds: ['seeds/**/*.seed.ts'], - factories: ['factories/**/*.factory.ts'], - cli: { - seedDir: 'seeds', - factoryDir: 'factories', - }, -} diff --git a/src/commands/seed.command.ts b/src/commands/seed.command.ts index 86b1aa16..877305a3 100644 --- a/src/commands/seed.command.ts +++ b/src/commands/seed.command.ts @@ -20,6 +20,7 @@ export class SeedCommand implements yargs.CommandModule { describe: 'Path to the typeorm config file (json or js).', }) .option('class', { + alias: 'c', describe: 'Specific seed class to run.', }) } diff --git a/src/entity-factory.test.ts b/src/entity-factory.test.ts new file mode 100644 index 00000000..3e6a8bb3 --- /dev/null +++ b/src/entity-factory.test.ts @@ -0,0 +1,66 @@ +import { EntityFactory } from './entity-factory' + +describe('make', () => { + // tslint:disable-next-line + class User { + constructor(public name: string) {} + } + // tslint:disable-next-line + class Pet { + constructor(public name: string, public user: User) {} + } + + test('Should make a new entity', async () => { + const mockUserFactory = jest.fn() + const userFactory = new EntityFactory('User', User, mockUserFactory) + mockUserFactory.mockReturnValue(new User('Steve')) + + const newUser = await userFactory.make() + + expect(newUser.name).toBe('Steve') + }) + + test('Should override the enitys props', async () => { + const mockUserFactory = jest.fn() + const userFactory = new EntityFactory('User', User, mockUserFactory) + mockUserFactory.mockReturnValue(new User('Steve')) + + const newUser = await userFactory.make({ name: 'Tony' }) + + expect(newUser.name).toBe('Tony') + }) + + test('Should call the nested entity factories', async () => { + const mockUserFactory = jest.fn() + const userFactory = new EntityFactory('User', User, mockUserFactory) + + const mockPetFactory = jest.fn() + const petFactory = new EntityFactory('Pet', Pet, mockPetFactory) + + mockUserFactory.mockReturnValue({ + name: 'Pepper', + }) + + mockPetFactory.mockReturnValue({ + name: 'Bunny', + user: userFactory, + }) + + const newPet = await petFactory.make() + + expect(newPet.name).toBe('Bunny') + expect(newPet.user.name).toBe('Pepper') + }) + + test('Should call the map function', async () => { + const mockUserFactory = jest.fn() + const userFactory = new EntityFactory('User', User, mockUserFactory) + mockUserFactory.mockReturnValue(new User('Steve')) + const mockMap = jest.fn() + + const newUsers = await userFactory.map(mockMap).makeMany(2) + + expect(newUsers.length).toBe(2) + expect(mockMap).toBeCalledTimes(2) + }) +}) diff --git a/src/entity-factory.ts b/src/entity-factory.ts index 4026b55d..0e4ca5f9 100644 --- a/src/entity-factory.ts +++ b/src/entity-factory.ts @@ -38,7 +38,9 @@ export class EntityFactory { } for (const key in overrideParams) { - entity[key] = overrideParams[key] + if (overrideParams.hasOwnProperty(key)) { + entity[key] = overrideParams[key] + } } return entity diff --git a/src/helpers.test.ts b/src/helpers.test.ts new file mode 100644 index 00000000..b858d22c --- /dev/null +++ b/src/helpers.test.ts @@ -0,0 +1,10 @@ +import { times } from './helpers' + +describe('times', () => { + test('Should call the func 2 times ', async () => { + const result = await times(2, async () => 'a') + + expect(result.length).toBe(2) + expect(result).toStrictEqual(['a', 'a']) + }) +}) diff --git a/src/typeorm-seeding.ts b/src/typeorm-seeding.ts index 6b1a985a..b479edd6 100644 --- a/src/typeorm-seeding.ts +++ b/src/typeorm-seeding.ts @@ -1,10 +1,9 @@ import 'reflect-metadata' -import { Connection, ObjectType, ConnectionOptions } from 'typeorm' +import { Connection, ObjectType } from 'typeorm' import { EntityFactory } from './entity-factory' import { EntityFactoryDefinition, Factory, FactoryFunction, SeederConstructor, Seeder } from './types' import { getNameOfEntity } from './utils/factory.util' -import { getConnectionOptions, createConnection } from './connection' // ------------------------------------------------------------------------- // Handy Exports diff --git a/src/utils/factory.test.ts b/src/utils/factory.test.ts index 2444ff67..b7addbae 100644 --- a/src/utils/factory.test.ts +++ b/src/utils/factory.test.ts @@ -6,7 +6,7 @@ describe('getNameOfClass', () => { expect(getNameOfEntity(UserEntity)).toBe('UserEntity') }) test('Passing UserEnity function should return the name of the function', () => { - function UserEntity() {} + const UserEntity = () => void 0 expect(getNameOfEntity(UserEntity)).toBe('UserEntity') }) test('Passing undefinde as a enity-class should throw an error', () => { diff --git a/src/utils/file.util.test.ts b/src/utils/file.util.test.ts new file mode 100644 index 00000000..1389a734 --- /dev/null +++ b/src/utils/file.util.test.ts @@ -0,0 +1,26 @@ +import { loadFiles } from './file.util' +import * as path from 'path' +import * as glob from 'glob' + +describe('loadFiles', () => { + let syncMock: jest.Mock + beforeEach(() => { + syncMock = jest.fn() + ;(glob as any).sync = syncMock + syncMock.mockReturnValue(['fileA', 'fileB']) + }) + + test('Should return a flat array', () => { + const results = loadFiles(['path/to/files', 'other/path/to/files']) + + expect(results.length).toBe(4) + expect(results[0]).toBe('fileA') + expect(results[1]).toBe('fileB') + }) + + test('Should call the sync method with the cwd path', () => { + const results = loadFiles(['path/to/files']) + + expect(syncMock).toBeCalledWith(path.join(process.cwd(), 'path/to/files')) + }) +}) diff --git a/src/utils/file.util.ts b/src/utils/file.util.ts index 8b96a466..89bf9214 100644 --- a/src/utils/file.util.ts +++ b/src/utils/file.util.ts @@ -1,11 +1,10 @@ -import glob from 'glob' +import * as glob from 'glob' import * as path from 'path' export const importFiles = (filePaths: string[]) => filePaths.forEach(require) export const loadFiles = (filePattern: string[]): string[] => { - const filePaths: string[] = filePattern + return filePattern .map(pattern => glob.sync(path.join(process.cwd(), pattern))) .reduce((acc, filePath) => acc.concat(filePath), []) - return filePaths }