From 62637cc3ca59a7ab8a54b2a6c6c4f16efed9a5ab Mon Sep 17 00:00:00 2001 From: Nora Date: Wed, 19 Jun 2019 09:46:02 -0400 Subject: [PATCH] refactor(example-todo-list): move reused functions into helper --- .../acceptance/todo-list.acceptance.ts | 84 +++++++----------- .../__tests__/acceptance/todo.acceptance.ts | 85 +++++-------------- examples/todo-list/src/__tests__/helpers.ts | 44 ++++++++++ .../integration/todo-list.integration.ts | 65 +++++--------- .../__tests__/integration/todo.integration.ts | 66 +++++--------- .../src/repositories/todo-list.repository.ts | 4 + 6 files changed, 144 insertions(+), 204 deletions(-) diff --git a/examples/todo-list/src/__tests__/acceptance/todo-list.acceptance.ts b/examples/todo-list/src/__tests__/acceptance/todo-list.acceptance.ts index 5550542095d6..ddacc4ae3c38 100644 --- a/examples/todo-list/src/__tests__/acceptance/todo-list.acceptance.ts +++ b/examples/todo-list/src/__tests__/acceptance/todo-list.acceptance.ts @@ -4,17 +4,17 @@ // License text available at https://opensource.org/licenses/MIT import {EntityNotFoundError} from '@loopback/repository'; -import { - Client, - createRestAppClient, - expect, - givenHttpServerConfig, - toJSON, -} from '@loopback/testlab'; +import {Client, createRestAppClient, expect, toJSON} from '@loopback/testlab'; import {TodoListApplication} from '../../application'; -import {Todo, TodoList} from '../../models/'; +import {TodoList} from '../../models/'; import {TodoListRepository, TodoRepository} from '../../repositories/'; -import {givenTodo, givenTodoList} from '../helpers'; +import { + givenRunningApplicationWithCustomConfiguration, + givenTodoInstance, + givenTodoList, + givenTodoListInstance, + givenTodoRepositories, +} from '../helpers'; describe('TodoListApplication', () => { let app: TodoListApplication; @@ -22,10 +22,15 @@ describe('TodoListApplication', () => { let todoRepo: TodoRepository; let todoListRepo: TodoListRepository; - before(givenRunningApplicationWithCustomConfiguration); + before(async () => { + app = await givenRunningApplicationWithCustomConfiguration(); + }); after(() => app.stop()); - before(givenTodoRepositories); + before(async () => { + ({todoRepo, todoListRepo} = await givenTodoRepositories(app)); + }); + before(() => { client = createRestAppClient(app); }); @@ -91,8 +96,14 @@ describe('TodoListApplication', () => { it('updates selected todoLists', async () => { await todoListRepo.deleteAll(); - await givenTodoListInstance({title: 'red-list', color: 'red'}); - await givenTodoListInstance({title: 'green-list', color: 'green'}); + await givenTodoListInstance(todoListRepo, { + title: 'red-list', + color: 'red', + }); + await givenTodoListInstance(todoListRepo, { + title: 'green-list', + color: 'green', + }); const response = await client .patch('/todo-lists') @@ -119,7 +130,7 @@ describe('TodoListApplication', () => { let persistedTodoList: TodoList; beforeEach(async () => { - persistedTodoList = await givenTodoListInstance(); + persistedTodoList = await givenTodoListInstance(todoListRepo); }); it('gets a todoList by ID', async () => { @@ -166,9 +177,9 @@ describe('TodoListApplication', () => { }); it('queries todo-lists with a filter', async () => { - await givenTodoListInstance({title: 'day', color: 'white'}); + await givenTodoListInstance(todoListRepo, {title: 'day', color: 'white'}); - const listInBlack = await givenTodoListInstance({ + const listInBlack = await givenTodoListInstance(todoListRepo, { title: 'night', color: 'black', }); @@ -180,8 +191,8 @@ describe('TodoListApplication', () => { }); it('includes Todos in query result', async () => { - const list = await givenTodoListInstance(); - const todo = await givenTodoInstance({todoListId: list.id}); + const list = await givenTodoListInstance(todoListRepo); + const todo = await givenTodoInstance(todoRepo, {todoListId: list.id}); const filter = JSON.stringify({include: [{relation: 'todos'}]}); const response = await client.get('/todo-lists').query({filter: filter}); @@ -205,43 +216,10 @@ describe('TodoListApplication', () => { ============================================================================ */ - async function givenRunningApplicationWithCustomConfiguration() { - app = new TodoListApplication({ - rest: givenHttpServerConfig(), - }); - - await app.boot(); - - /** - * Override default config for DataSource for testing so we don't write - * test data to file when using the memory connector. - */ - app.bind('datasources.config.db').to({ - name: 'db', - connector: 'memory', - }); - - // Start Application - await app.start(); - } - - async function givenTodoRepositories() { - todoRepo = await app.getRepository(TodoRepository); - todoListRepo = await app.getRepository(TodoListRepository); - } - - async function givenTodoListInstance(todoList?: Partial) { - return await todoListRepo.create(givenTodoList(todoList)); - } - - async function givenTodoInstance(todo?: Partial) { - return await todoRepo.create(givenTodo(todo)); - } - function givenMutlipleTodoListInstances() { return Promise.all([ - givenTodoListInstance(), - givenTodoListInstance({title: 'so many things to do wow'}), + givenTodoListInstance(todoListRepo), + givenTodoListInstance(todoListRepo, {title: 'so many things to do wow'}), ]); } }); diff --git a/examples/todo-list/src/__tests__/acceptance/todo.acceptance.ts b/examples/todo-list/src/__tests__/acceptance/todo.acceptance.ts index 8dbb62d9e134..9b60bfc5497b 100644 --- a/examples/todo-list/src/__tests__/acceptance/todo.acceptance.ts +++ b/examples/todo-list/src/__tests__/acceptance/todo.acceptance.ts @@ -4,17 +4,17 @@ // License text available at https://opensource.org/licenses/MIT import {EntityNotFoundError} from '@loopback/repository'; -import { - Client, - createRestAppClient, - expect, - givenHttpServerConfig, - toJSON, -} from '@loopback/testlab'; +import {Client, createRestAppClient, expect, toJSON} from '@loopback/testlab'; import {TodoListApplication} from '../../application'; -import {Todo, TodoList} from '../../models/'; +import {Todo} from '../../models/'; import {TodoListRepository, TodoRepository} from '../../repositories/'; -import {givenTodo, givenTodoList} from '../helpers'; +import { + givenRunningApplicationWithCustomConfiguration, + givenTodo, + givenTodoInstance, + givenTodoListInstance, + givenTodoRepositories, +} from '../helpers'; describe('TodoListApplication', () => { let app: TodoListApplication; @@ -22,10 +22,14 @@ describe('TodoListApplication', () => { let todoRepo: TodoRepository; let todoListRepo: TodoListRepository; - before(givenRunningApplicationWithCustomConfiguration); + before(async () => { + app = await givenRunningApplicationWithCustomConfiguration(); + }); after(() => app.stop()); - before(givenTodoRepositories); + before(async () => { + ({todoRepo, todoListRepo} = await givenTodoRepositories(app)); + }); before(() => { client = createRestAppClient(app); }); @@ -58,7 +62,7 @@ describe('TodoListApplication', () => { let persistedTodo: Todo; beforeEach(async () => { - persistedTodo = await givenTodoInstance(); + persistedTodo = await givenTodoInstance(todoRepo); }); it('gets a todo by ID', () => { @@ -128,17 +132,17 @@ describe('TodoListApplication', () => { }); it('returns the owning todo-list', async () => { - const list = await givenTodoListInstance(); - const todo = await givenTodoInstance({todoListId: list.id}); + const list = await givenTodoListInstance(todoListRepo); + const todo = await givenTodoInstance(todoRepo, {todoListId: list.id}); await client.get(`/todos/${todo.id}/todo-list`).expect(200, toJSON(list)); }); }); it('queries todos with a filter', async () => { - await givenTodoInstance({title: 'wake up', isComplete: true}); + await givenTodoInstance(todoRepo, {title: 'wake up', isComplete: true}); - const todoInProgress = await givenTodoInstance({ + const todoInProgress = await givenTodoInstance(todoRepo, { title: 'go to sleep', isComplete: false, }); @@ -150,8 +154,8 @@ describe('TodoListApplication', () => { }); it('includes TodoList in query result', async () => { - const list = await givenTodoListInstance(); - const todo = await givenTodoInstance({todoListId: list.id}); + const list = await givenTodoListInstance(todoListRepo); + const todo = await givenTodoInstance(todoRepo, {todoListId: list.id}); const filter = JSON.stringify({include: [{relation: 'todoList'}]}); const response = await client.get('/todos').query({filter: filter}); @@ -162,49 +166,4 @@ describe('TodoListApplication', () => { todoList: toJSON(list), }); }); - - /* - ============================================================================ - TEST HELPERS - These functions help simplify setup of your test fixtures so that your tests - can: - - operate on a "clean" environment each time (a fresh in-memory database) - - avoid polluting the test with large quantities of setup logic to keep - them clear and easy to read - - keep them DRY (who wants to write the same stuff over and over?) - ============================================================================ - */ - - async function givenRunningApplicationWithCustomConfiguration() { - app = new TodoListApplication({ - rest: givenHttpServerConfig(), - }); - - await app.boot(); - - /** - * Override default config for DataSource for testing so we don't write - * test data to file when using the memory connector. - */ - app.bind('datasources.config.db').to({ - name: 'db', - connector: 'memory', - }); - - // Start Application - await app.start(); - } - - async function givenTodoRepositories() { - todoRepo = await app.getRepository(TodoRepository); - todoListRepo = await app.getRepository(TodoListRepository); - } - - async function givenTodoInstance(todo?: Partial) { - return await todoRepo.create(givenTodo(todo)); - } - - async function givenTodoListInstance(data?: Partial) { - return await todoListRepo.create(givenTodoList(data)); - } }); diff --git a/examples/todo-list/src/__tests__/helpers.ts b/examples/todo-list/src/__tests__/helpers.ts index 1f81a61401ec..ecddab142267 100644 --- a/examples/todo-list/src/__tests__/helpers.ts +++ b/examples/todo-list/src/__tests__/helpers.ts @@ -3,7 +3,10 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT +import {givenHttpServerConfig} from '@loopback/testlab'; +import {TodoListApplication} from '../application'; import {Todo, TodoList, TodoListImage} from '../models'; +import {TodoListRepository, TodoRepository} from '../repositories'; /* ============================================================================== @@ -69,3 +72,44 @@ export function givenTodoListImage(todoListImage?: Partial) { ); return new TodoListImage(data); } + +export async function givenRunningApplicationWithCustomConfiguration() { + const app = new TodoListApplication({ + rest: givenHttpServerConfig(), + }); + + await app.boot(); + + /** + * Override default config for DataSource for testing so we don't write + * test data to file when using the memory connector. + */ + app.bind('datasources.config.db').to({ + name: 'db', + connector: 'memory', + }); + + // Start Application + await app.start(); + return app; +} + +export async function givenTodoRepositories(app: TodoListApplication) { + const todoRepo = await app.getRepository(TodoRepository); + const todoListRepo = await app.getRepository(TodoListRepository); + return {todoRepo, todoListRepo}; +} + +export async function givenTodoInstance( + todoRepo: TodoRepository, + todo?: Partial, +) { + return await todoRepo.create(givenTodo(todo)); +} + +export async function givenTodoListInstance( + todoListRepo: TodoListRepository, + data?: Partial, +) { + return await todoListRepo.create(givenTodoList(data)); +} diff --git a/examples/todo-list/src/__tests__/integration/todo-list.integration.ts b/examples/todo-list/src/__tests__/integration/todo-list.integration.ts index 4bebdda9fb07..2e1c60a898f6 100644 --- a/examples/todo-list/src/__tests__/integration/todo-list.integration.ts +++ b/examples/todo-list/src/__tests__/integration/todo-list.integration.ts @@ -1,22 +1,34 @@ -import {expect, givenHttpServerConfig} from '@loopback/testlab'; +import {expect} from '@loopback/testlab'; import {TodoListApplication} from '../../application'; -import {Todo, TodoList} from '../../models'; import {TodoListRepository, TodoRepository} from '../../repositories'; -import {givenTodo, givenTodoList} from '../helpers'; +import { + givenRunningApplicationWithCustomConfiguration, + givenTodoInstance, + givenTodoListInstance, + givenTodoRepositories, +} from '../helpers'; describe('TodoListApplication', () => { let app: TodoListApplication; let todoRepo: TodoRepository; let todoListRepo: TodoListRepository; - before(givenRunningApplicationWithCustomConfiguration); + before(async () => { + app = await givenRunningApplicationWithCustomConfiguration(); + }); after(() => app.stop()); - before(givenTodoRepositories); + before(async () => { + ({todoRepo, todoListRepo} = await givenTodoRepositories(app)); + }); + + beforeEach(async () => { + await todoRepo.deleteAll(); + }); it('includes Todos in find method result', async () => { - const list = await givenTodoListInstance(); - const todo = await givenTodoInstance({todoListId: list.id}); + const list = await givenTodoListInstance(todoListRepo); + const todo = await givenTodoInstance(todoRepo, {todoListId: list.id}); const response = await todoListRepo.find({ include: [{relation: 'todos'}], @@ -29,8 +41,8 @@ describe('TodoListApplication', () => { }); it('includes Todos in findById result', async () => { - const list = await givenTodoListInstance(); - const todo = await givenTodoInstance({todoListId: list.id}); + const list = await givenTodoListInstance(todoListRepo); + const todo = await givenTodoInstance(todoRepo, {todoListId: list.id}); const response = await todoListRepo.findById(list.id, { include: [{relation: 'todos'}], @@ -41,39 +53,4 @@ describe('TodoListApplication', () => { todos: [todo], }); }); - - // TOOD: refactor this to the helpers file so it's not duplicated - - async function givenRunningApplicationWithCustomConfiguration() { - app = new TodoListApplication({ - rest: givenHttpServerConfig(), - }); - - await app.boot(); - - /** - * Override default config for DataSource for testing so we don't write - * test data to file when using the memory connector. - */ - app.bind('datasources.config.db').to({ - name: 'db', - connector: 'memory', - }); - - // Start Application - await app.start(); - } - - async function givenTodoRepositories() { - todoRepo = await app.getRepository(TodoRepository); - todoListRepo = await app.getRepository(TodoListRepository); - } - - async function givenTodoInstance(todo?: Partial) { - return await todoRepo.create(givenTodo(todo)); - } - - async function givenTodoListInstance(data?: Partial) { - return await todoListRepo.create(givenTodoList(data)); - } }); diff --git a/examples/todo-list/src/__tests__/integration/todo.integration.ts b/examples/todo-list/src/__tests__/integration/todo.integration.ts index 0b3b0d9845c2..18a64a3863d1 100644 --- a/examples/todo-list/src/__tests__/integration/todo.integration.ts +++ b/examples/todo-list/src/__tests__/integration/todo.integration.ts @@ -1,22 +1,34 @@ -import {expect, givenHttpServerConfig} from '@loopback/testlab'; +import {expect} from '@loopback/testlab'; import {TodoListApplication} from '../../application'; -import {Todo, TodoList} from '../../models'; import {TodoListRepository, TodoRepository} from '../../repositories'; -import {givenTodo, givenTodoList} from '../helpers'; +import { + givenRunningApplicationWithCustomConfiguration, + givenTodoInstance, + givenTodoListInstance, + givenTodoRepositories, +} from '../helpers'; describe('TodoApplication', () => { let app: TodoListApplication; let todoRepo: TodoRepository; let todoListRepo: TodoListRepository; - before(givenRunningApplicationWithCustomConfiguration); + before(async () => { + app = await givenRunningApplicationWithCustomConfiguration(); + }); after(() => app.stop()); - before(givenTodoRepositories); + before(async () => { + ({todoRepo, todoListRepo} = await givenTodoRepositories(app)); + }); + + beforeEach(async () => { + await todoRepo.deleteAll(); + }); it('includes TodoList in find method result', async () => { - const list = await givenTodoListInstance(); - const todo = await givenTodoInstance({todoListId: list.id}); + const list = await givenTodoListInstance(todoListRepo); + const todo = await givenTodoInstance(todoRepo, {todoListId: list.id}); const response = await todoRepo.find({ include: [{relation: 'todoList'}], @@ -29,8 +41,9 @@ describe('TodoApplication', () => { }); it('includes TodoList in findById result', async () => { - const list = await givenTodoListInstance({color: 'grey'}); - const todo = await givenTodoInstance({todoListId: list.id}); + // remove grey? + const list = await givenTodoListInstance(todoListRepo, {}); + const todo = await givenTodoInstance(todoRepo, {todoListId: list.id}); const response = await todoRepo.findById(todo.id, { include: [{relation: 'todoList'}], @@ -41,39 +54,4 @@ describe('TodoApplication', () => { todoList: list, }); }); - - // TOOD: refactor this to the helpers file so it's not duplicated - - async function givenRunningApplicationWithCustomConfiguration() { - app = new TodoListApplication({ - rest: givenHttpServerConfig(), - }); - - await app.boot(); - - /** - * Override default config for DataSource for testing so we don't write - * test data to file when using the memory connector. - */ - app.bind('datasources.config.db').to({ - name: 'db', - connector: 'memory', - }); - - // Start Application - await app.start(); - } - - async function givenTodoRepositories() { - todoRepo = await app.getRepository(TodoRepository); - todoListRepo = await app.getRepository(TodoListRepository); - } - - async function givenTodoInstance(todo?: Partial) { - return await todoRepo.create(givenTodo(todo)); - } - - async function givenTodoListInstance(data?: Partial) { - return await todoListRepo.create(givenTodoList(data)); - } }); diff --git a/examples/todo-list/src/repositories/todo-list.repository.ts b/examples/todo-list/src/repositories/todo-list.repository.ts index 4cfac4df06de..512f8c4fbe1c 100644 --- a/examples/todo-list/src/repositories/todo-list.repository.ts +++ b/examples/todo-list/src/repositories/todo-list.repository.ts @@ -63,10 +63,14 @@ export class TodoListRepository extends DefaultCrudRepository< filter?: Filter, options?: Options, ): Promise { + // Prevent juggler for applying "include" filter + // Juggler is not aware of LB4 relations const include = filter && filter.include; filter = filter && Object.assign(filter, {include: undefined}); const result = await super.find(filter, options); + // poor-mans inclusion resolver, this should be handled by DefaultCrudRepo + // and use `inq` operator to fetch related todos in fewer DB queries if (include && include.length && include[0].relation === 'todos') { await Promise.all( result.map(async r => {