diff --git a/.gitignore b/.gitignore index 5daf734..59dac7e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.nuxt-storybook +storybook-static + .DS_Store node_modules/ /dist/ @@ -23,4 +26,4 @@ yarn-error.log* git-crypt-key -backend/.git \ No newline at end of file +backend/.git diff --git a/backend/src/datasources/persons.datasource.ts b/backend/src/datasources/persons.datasource.ts index f9707fd..3b8d737 100644 --- a/backend/src/datasources/persons.datasource.ts +++ b/backend/src/datasources/persons.datasource.ts @@ -12,6 +12,8 @@ export default class PersonsDatasource extends DataSource { query($email: String!) { person(where: { email: $email }) { id + name + email passwordHash passwordSalt } @@ -46,6 +48,8 @@ export default class PersonsDatasource extends DataSource { } ) { id + name + email } } `; diff --git a/backend/src/jwt-payload.ts b/backend/src/jwt-payload.ts index e48507d..fd09f57 100644 --- a/backend/src/jwt-payload.ts +++ b/backend/src/jwt-payload.ts @@ -1,4 +1,6 @@ export interface JwtPayload { id: string; + name: string; + email: string; iat?: number; } diff --git a/backend/src/resolvers/persons.resolver.ts b/backend/src/resolvers/persons.resolver.ts index a5e4c06..beaab53 100644 --- a/backend/src/resolvers/persons.resolver.ts +++ b/backend/src/resolvers/persons.resolver.ts @@ -47,7 +47,11 @@ export function mutation(subSchemas: GraphQLSchema[], executor: Executor) { ); } else { const person: Partial = result.data.person; - const payload: JwtPayload = { id: person.id }; + const payload: JwtPayload = { + id: person.id, + name: person.name, + email: person.email, + }; return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRES_IN, @@ -76,7 +80,11 @@ export function mutation(subSchemas: GraphQLSchema[], executor: Executor) { const hash = await bcrypt.hash(password, person.passwordSalt); if (hash === result.data.person.passwordHash) { - const payload: JwtPayload = { id: person.id }; + const payload: JwtPayload = { + id: person.id, + name: person.name, + email: person.email, + }; return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRES_IN, diff --git a/webapp/.storybook/main.js b/webapp/.storybook/main.js deleted file mode 100644 index 90197e5..0000000 --- a/webapp/.storybook/main.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - "stories": [ - "../src/**/*.stories.mdx", - "../src/**/*.stories.@(js|jsx|ts|tsx)" - ], - "addons": [ - "@storybook/addon-links", - "@storybook/addon-essentials", - "@storybook/addon-a11y" - ] -} \ No newline at end of file diff --git a/webapp/.storybook/preview-head.html b/webapp/.storybook/preview-head.html deleted file mode 100644 index 9d28efd..0000000 --- a/webapp/.storybook/preview-head.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/webapp/.storybook/preview.js b/webapp/.storybook/preview.js deleted file mode 100644 index 5d00c02..0000000 --- a/webapp/.storybook/preview.js +++ /dev/null @@ -1,4 +0,0 @@ - -export const parameters = { - actions: { argTypesRegex: "^on[A-Z].*" }, -} \ No newline at end of file diff --git a/webapp/README.md b/webapp/README.md index 8f4c644..2fe880c 100644 --- a/webapp/README.md +++ b/webapp/README.md @@ -1,20 +1,29 @@ -# nuxt-webapp +# TheCodeNinjas Webapp -## Build Setup +## Installation Steps + +Follow these steps to build the webapp for production + +1. Clone this project + +2. Go to the "webapp" folder (Where this README is located) + +3. Run these commands ```bash -# install dependencies + +# Install all necessary dependencies $ npm install -# serve with hot reload at localhost:3000 -$ npm run dev +# Generate the static project +$ npm run generate -# build for production and launch server +# Build the app for production $ npm run build + +# Start the production server $ npm run start -# generate static project -$ npm run generate +#(Optional) Run the project in a development environment with hot reload +$ npm run dev ``` - -For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org). diff --git a/webapp/components/LoginForm/LoginForm.spec.ts b/webapp/components/LoginForm/LoginForm.spec.ts new file mode 100644 index 0000000..4ff82db --- /dev/null +++ b/webapp/components/LoginForm/LoginForm.spec.ts @@ -0,0 +1,51 @@ +import LoginForm from '~/components/LoginForm/LoginForm.vue' +import { setupWrapperAndStore } from '~/utils/tests' + +jest.mock('~/utils/globals') + +describe('Login form tests', () => { + it('should set up test utilities properly', () => { + const { wrapper, storeAccessor, localVue } = setupWrapperAndStore(LoginForm) + + expect(wrapper).toBeTruthy() + expect(storeAccessor).toBeTruthy() + expect(localVue).toBeTruthy() + }) + + describe('Form submit', () => { + const { wrapper, storeAccessor, localVue } = setupWrapperAndStore(LoginForm) + const inputEmail = wrapper.find('input#input-email') + const inputPassword = wrapper.find('input#input-password') + const form = wrapper.find('form') + + it('should shows no error', async () => { + inputEmail.setValue('correctCredentials@test.com') + inputPassword.setValue('rightPassword') + + await form.trigger('submit') + await localVue.nextTick() + + expect(storeAccessor.auth.getError).toBe(null) + }) + + it('should show wrong credentials error', async () => { + inputEmail.setValue('wrongCredentials@test.com') + inputPassword.setValue('wrongPassword') + + await form.trigger('submit') + await localVue.nextTick() + + expect(storeAccessor.auth.getError).toBe('Wrong credentials!') + }) + + it('should show friendly error message', async () => { + inputEmail.setValue('invalidToken@test.com') + inputPassword.setValue('somePassword') + + await form.trigger('submit') + await localVue.nextTick() + + expect(storeAccessor.auth.getError).toBe('Oops! Something went wrong.') + }) + }) +}) diff --git a/webapp/components/LoginForm/LoginForm.stories.js b/webapp/components/LoginForm/LoginForm.stories.js new file mode 100644 index 0000000..c75a9c0 --- /dev/null +++ b/webapp/components/LoginForm/LoginForm.stories.js @@ -0,0 +1,27 @@ +import NewsItem from './NewsItem.vue' + +const newsItem = { + id: 1, + title: 'Test News Item', + votes: 0, +} + +export default { + title: 'NewsItem', + component: NewsItem, + argTypes: { + update: { action: 'down- or upvote event action' }, + remove: { action: 'remove-event action' }, + }, +} + +const Template = (args, { argTypes }) => ({ + props: Object.keys(argTypes), + components: { NewsItem }, + template: '', +}) + +export const Default = Template.bind({}) +Default.args = { + newsItem, +} diff --git a/webapp/components/LoginForm/LoginForm.vue b/webapp/components/LoginForm/LoginForm.vue index a482f2b..13e518a 100644 --- a/webapp/components/LoginForm/LoginForm.vue +++ b/webapp/components/LoginForm/LoginForm.vue @@ -1,61 +1,46 @@