diff --git a/.gitignore b/.gitignore index 4fb759d..61a2fbc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules dist npm-debug.log -coverage \ No newline at end of file +coverage +/vscode \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..1176953 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "name": "vscode-jest-tests.v2", + "request": "launch", + "args": [ + "--runInBand", + "--watchAll=false", + "--testNamePattern", + "${jest.testNamePattern}", + "--runTestsByPath", + "${jest.testFile}" + ], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + // "disableOptimisticBPs": true, + "program": "${workspaceFolder}/node_modules/.bin/jest", + "windows": { + "program": "${workspaceFolder}/node_modules/jest/bin/jest" + } + } + ] +} diff --git a/src/index.test.ts b/src/index.test.ts index f3b13a9..e0410de 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,7 +1,111 @@ -//write tests here +import { UserAPI, IUser, IUserRecord } from './index' -describe('Tests will go here!', () => { - it('should pass', () => { - expect(true).toBeTruthy() +const seedUsers: Readonly = { + "1": { id: "1", name: "andy", favColor: "blue", age: 247 }, + "2": { id: "2", name: "Andy", favColor: "purple", age: 150 }, + "3": { id: "3", name: "Sarah", favColor: "Blue", age: 200} +} + +describe(UserAPI, () => { + const userAPI = new UserAPI(seedUsers) + + it('check that UserAPI is initialized', () => { + expect(userAPI).toBeInstanceOf(UserAPI) + }) + + describe('getUser()', () => { + const userAPI = new UserAPI(seedUsers) + + it("getUsers() returns intitial seed data", () => { + const actual = userAPI.getUsers() + const expected = Object.values(seedUsers) + expect(actual).toEqual(expected) + }) + }) + + describe('addUser()', () => { + const userAPI = new UserAPI(seedUsers) + + it('addUser() adds new user to the dataset', () => { + const user: IUser = { name: "Sarah", favColor: "Blue", age: 150 } + const newUser: IUser = userAPI.addUser(user) + const actual: ReadonlyArray = userAPI.getUsers() + const expected: ReadonlyArray = Object.values({...seedUsers, newUser}) + expect(actual).toEqual(expected) + }) + + it("addUser() throws error when duplicating user info", () => { + const userDuplicate = { name: "andy", favColor: "blue", age: 247 } + const actual= () => {userAPI.addUser(userDuplicate)} + expect(actual).toThrow(Error) + }) + + it("addUser() throws error supplied with pre-existing user.id", () => { + const userWithId = seedUsers['1'] + const actual = () => {userAPI.addUser(userWithId)} + expect(actual).toThrow(Error) + }) + }) + + describe('getUserById()', () => { + const userAPI = new UserAPI(seedUsers) + + it('getUserById() returns expected user', () => { + const actual: IUser = userAPI.getUserById("2") + const expected: IUser = seedUsers['2'] + expect(actual).toEqual(expected) + }) + + it('getUserById() throws error if user with id is not found', () => { + const actual = () => userAPI.getUserById("100") + expect(actual).toThrow(ReferenceError) + }) + }) + + describe('deleteUserById()', () => { + const userAPI = new UserAPI(seedUsers) + + it('deleteUserById() deletes user with given id', () => { + userAPI.deleteUserById("1") + const actual: ReadonlyArray = userAPI.getUsers() + const expected: ReadonlyArray = [ seedUsers['2'], seedUsers['3'] ] + + expect(actual).toEqual(expected) + }) + + it('deleteUserById() throws error if user is not found', () => { + const actual = () => {userAPI.deleteUserById("100")} + expect(actual).toThrow(ReferenceError) + }) + }) + + describe('searchUserbyName()', () => { + const userAPI = new UserAPI(seedUsers) + + it('searchUserByName() returns all instances of given name, independant of capitals', () => { + const actual: ReadonlyArray = userAPI.searchUserByName("Andy") + const expected: ReadonlyArray= [ seedUsers['1'], seedUsers['2'] ] + expect(actual).toEqual(expected) + }) + + it('searchUserByName() throws error if no user is found', () => { + const actual = () => userAPI.searchUserByName("Jessica") + expect(actual).toThrow(ReferenceError) + }) + }) + + describe('searchUserFavoriteColor()', () => { + const userAPI = new UserAPI(seedUsers) + + it('searchUserByFavoriteColor() returns all instances users with given fav color, independant of capitals', () => { + const actual: ReadonlyArray = userAPI.searchUsersByFavoriteColor("blue") + const expected: ReadonlyArray = [ seedUsers['1'], seedUsers['3'] ] + expect(actual).toEqual(expected) + }) + + it('searchUserByFavoriteColor() throws error if no user is found with given fav color', () => { + const actual = () => userAPI.searchUsersByFavoriteColor("Pink") + expect(actual).toThrow(ReferenceError) + }) }) -}) \ No newline at end of file +}) diff --git a/src/index.ts b/src/index.ts index bb6b54e..e3e45bf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,80 @@ -//Define class here \ No newline at end of file +export interface IUser { + readonly id?: string + readonly name: string + readonly favColor: string + readonly age: number +} + +export type IUserRecord = Record + +export class UserAPI { + private _users: Readonly + + constructor(seedData: IUserRecord = {}) { + this._users = {...seedData} + } + + private _assignId(): string { + const id: Readonly = Date.now().toString() + Math.floor(Math.random()*100) + return id + } + + addUser(user: IUser): IUser { + if (user.id) { + throw new SyntaxError("Please resubmit without pre-exisitng ID field.") + } + const result = (Object.values(this._users)).some( existingUser => ( + existingUser.name.toLowerCase() === user.name.toLowerCase() && + existingUser.favColor.toLowerCase() === user.favColor.toLowerCase() && + existingUser.age === user.age + )) + + if (result) { + throw new Error("A user with those properties already exists in the database.") + } + + const newUser = {...user, id: this._assignId()} + this._users = {...this._users, [newUser.id]: newUser} + return newUser + } + + getUserById(id: string): IUser { + if (this._users[id]) { + return this._users[id] + } + throw new ReferenceError(`No user found with id ${id}.`) + } + + getUsers(): Array { + if (this._users === null || this._users === undefined) { + throw new Error(`User Dataset not found`) + } + return Object.values(this._users) + } + + deleteUserById(id: string): IUser { + if (this._users[id]) { + const { [id]: deletedUser, ...rest } = this._users + this._users = {...rest} + return deletedUser + } + throw new ReferenceError(`No user found with id ${id}.`) + } + + searchUserByName(name: string): ReadonlyArray { + const users = Object.values(this._users).filter( user => user.name.toLowerCase() === name.toLowerCase()) + + if (users.length) { + return users + } + throw new ReferenceError(`No user found with name ${name}`) + } + + searchUsersByFavoriteColor(color: string): ReadonlyArray { + const users = Object.values(this._users).filter( user => user.favColor.toLowerCase() === color.toLowerCase()) + if (users.length) { + return users + } + throw new ReferenceError(`No users favorite color is ${color}`) + } +}