Jest tests for the Terms and Conditions in frontend and backend

This commit is contained in:
ogerly 2019-09-03 09:46:18 +02:00
parent a8e1583481
commit 1b9303ceaf
11 changed files with 118 additions and 66 deletions

View File

@ -87,10 +87,10 @@ module.exports = {
type: 'string',
allow: [null],
},
termsAndConditionsAgreedAt: {
/*termsAndConditionsAgreedAt: {
type: 'string',
isoDate: true,
allow: [null],
/* required: true, TODO */
},
// required: true, TODO
},*/
}

View File

@ -1,4 +1,4 @@
import { UserInputError } from 'apollo-server'
import { ForbiddenError, UserInputError } from 'apollo-server'
import uuid from 'uuid/v4'
import { neode } from '../../bootstrap/neo4j'
import fileUpload from './fileUpload'
@ -77,6 +77,13 @@ export default {
}
},
SignupVerification: async (object, args, context, resolveInfo) => {
let { termsAndConditionsAgreedVersion } = args
const regEx = new RegExp(/^[0-9]+\.[0-9]+\.[0-9]+$/g)
if (!regEx.test(termsAndConditionsAgreedVersion)) {
throw new ForbiddenError('Invalid version format!')
}
let { nonce, email } = args
email = email.toLowerCase()
const result = await instance.cypher(

View File

@ -297,17 +297,22 @@ describe('SignupVerification', () => {
mutation($name: String!, $password: String!, $email: String!, $nonce: String!, $termsAndConditionsAgreedVersion: String!) {
SignupVerification(name: $name, password: $password, email: $email, nonce: $nonce, termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion) {
id
termsAndConditionsAgreedVersion
}
}
`
describe('given valid password and email', () => {
const variables = {
nonce: '123456',
name: 'John Doe',
password: '123',
email: 'john@example.org',
termsAndConditionsAgreedVersion: '0.0.1',
}
let variables
beforeEach(async () => {
variables = {
nonce: '123456',
name: 'John Doe',
password: '123',
email: 'john@example.org',
termsAndConditionsAgreedVersion: '0.0.1',
}
})
describe('unauthenticated', () => {
beforeEach(async () => {
@ -351,9 +356,9 @@ describe('SignupVerification', () => {
describe('sending a valid nonce', () => {
it('creates a user account', async () => {
const expected = {
SignupVerification: {
SignupVerification: expect.objectContaining({
id: expect.any(String),
},
}),
}
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
})
@ -387,6 +392,26 @@ describe('SignupVerification', () => {
const { records: emails } = await instance.cypher(cypher, { name: 'John Doe' })
expect(emails).toHaveLength(1)
})
it('is version of terms and conditions saved correctly', async () => {
const expected = {
SignupVerification: expect.objectContaining({
termsAndConditionsAgreedVersion: '0.0.1',
}),
}
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
})
it('rejects if version of terms and conditions has wrong format', async () => {
const expected = {
SignupVerification: expect.objectContaining({
termsAndConditionsAgreedVersion: 'invalid version format',
}),
}
await expect(client.request(mutation, {...variables, termsAndConditionsAgreedVersion: 'invalid version format'})).rejects.toThrow(
'Invalid version format!',
)
})
})
describe('sending invalid nonce', () => {

View File

@ -3,7 +3,8 @@ import bcrypt from 'bcryptjs'
import { neo4jgraphql } from 'neo4j-graphql-js'
import fileUpload from './fileUpload'
import { neode } from '../../bootstrap/neo4j'
import { AuthenticationError, UserInputError } from 'apollo-server'
import { AuthenticationError, UserInputError, ForbiddenError } from 'apollo-server'
import Resolver from './helpers/Resolver'
const instance = neode()
@ -148,6 +149,16 @@ export default {
return blockedUser.toJson()
},
UpdateUser: async (object, args, context, resolveInfo) => {
let { termsAndConditionsAgreedVersion } = args
if (!! termsAndConditionsAgreedVersion ){
const regEx = new RegExp(/^[0-9]+\.[0-9]+\.[0-9]+$/g)
if (!regEx.test(termsAndConditionsAgreedVersion)) {
throw new ForbiddenError('Invalid version format!')
}
}
args = await fileUpload(args, { file: 'avatarUpload', url: 'avatar' })
try {
const user = await instance.find('User', args.id)

View File

@ -52,26 +52,35 @@ describe('users', () => {
})
describe('UpdateUser', () => {
const userParams = {
email: 'user@example.org',
password: '1234',
id: 'u47',
name: 'John Doe',
}
const variables = {
id: 'u47',
name: 'John Doughnut',
}
let userParams
let variables
const mutation = `
mutation($id: ID!, $name: String) {
UpdateUser(id: $id, name: $name) {
beforeEach(async () => {
userParams = {
email: 'user@example.org',
password: '1234',
id: 'u47',
name: 'John Doe',
}
variables = {
id: 'u47',
name: 'John Doughnut',
}
})
const UpdateUserMutation = gql`
mutation($id: ID!, $name: String, $termsAndConditionsAgreedVersion: String) {
UpdateUser(id: $id, name: $name, termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion) {
id
name
termsAndConditionsAgreedVersion
}
}
`
beforeEach(async () => {
await factory.create('User', userParams)
})
@ -90,7 +99,7 @@ describe('users', () => {
})
it('is not allowed to change other user accounts', async () => {
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
await expect(client.request(UpdateUserMutation, variables)).rejects.toThrow('Not Authorised')
})
})
@ -102,21 +111,18 @@ describe('users', () => {
it('name within specifications', async () => {
const expected = {
UpdateUser: {
UpdateUser: expect.objectContaining({
id: 'u47',
name: 'John Doughnut',
},
}),
}
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
await expect(client.request(UpdateUserMutation, variables)).resolves.toEqual(expected)
})
it('with `null` as name', async () => {
const variables = {
id: 'u47',
name: null,
}
variables.name = null
const expected = '"name" must be a string'
await expect(client.request(mutation, variables)).rejects.toThrow(expected)
await expect(client.request(UpdateUserMutation, variables)).rejects.toThrow(expected)
})
it('with too short name', async () => {
@ -125,8 +131,33 @@ describe('users', () => {
name: ' ',
}
const expected = '"name" length must be at least 3 characters long'
await expect(client.request(mutation, variables)).rejects.toThrow(expected)
await expect(client.request(UpdateUserMutation, variables)).rejects.toThrow(expected)
})
it ('given a new agreed version of terms and conditions', async () => {
variables = { ...variables, termsAndConditionsAgreedVersion: '0.0.2' }
const expected = {
UpdateUser: expect.objectContaining({
termsAndConditionsAgreedVersion: '0.0.2'
})
}
await expect(client.request(UpdateUserMutation, variables)).resolves.toEqual(expected)
})
it('rejects if version of terms and conditions has wrong format', async () => {
const expected = {
UpdateUser: expect.objectContaining({
termsAndConditionsAgreedVersion: 'invalid version format'
})
}
await expect(client.request(UpdateUserMutation, {...variables, termsAndConditionsAgreedVersion: 'invalid version format'})).rejects.toThrow(
'Invalid version format!',
)
})
})
})
@ -181,6 +212,8 @@ describe('users', () => {
client = new GraphQLClient(host, { headers })
})
describe("attempting to delete another user's account", () => {
it('throws an authorization error', async () => {
deleteUserVariables = { id: 'u565' }

View File

@ -1,24 +0,0 @@
describe('SignupVerification', () => {
describe('given a valid version', () => {
// const version = '1.2.3'
it.todo('saves the version with the new created user account')
it.todo('saves the current datetime in `termsAndConditionsAgreedAt`')
})
describe('given an invalid version string', () => {
// const version = 'this string does not follow semantic versioning'
it.todo('rejects')
})
})
describe('UpdateUser', () => {
describe('given terms and conditions are not updated', () => {
it.todo('does not update `termsAndConditionsAgreedAt`')
})
describe('given a new agreed version of terms and conditions', () => {
it.todo('updates `termsAndConditionsAgreedAt`')
it.todo('updates `termsAndConditionsAgreedVersion`')
})
})

View File

@ -18,6 +18,6 @@ type Mutation {
avatarUpload: Upload
locationName: String
about: String
termsAndConditionsAgreedVersion: String!
termsAndConditionsAgreedVersion: String
): User
}

View File

@ -14,7 +14,7 @@ export default function create() {
role: 'user',
avatar: faker.internet.avatar(),
about: faker.lorem.paragraph(),
termsAndConditionsAgreedAt: new Date().toISOString(),
// termsAndConditionsAgreedAt: new Date().toISOString(),
termsAndConditionsAgreedVersion: '0.0.1',
}
defaults.slug = slugify(defaults.name, { lower: true })

View File

@ -21,7 +21,7 @@
}
},
"site": {
"thx": "Danke!",
"thanks": "Danke!",
"made": "Mit ❤ gemacht",
"imprint": "Impressum",
"data-privacy": "Datenschutz",

View File

@ -21,7 +21,7 @@
}
},
"site": {
"thx": "Thanks!",
"thanks": "Thanks!",
"made": "Made with ❤",
"imprint": "Imprint",
"termsAndConditions": "Terms and conditions",

View File

@ -87,7 +87,7 @@ export default {
})
},
})
this.$toast.success(this.$t('site.thx'))
this.$toast.success(this.$t('site.thanks'))
this.$router.replace(this.$route.query.path || '/')
} catch (err) {
this.$toast.error(err.message)