diff --git a/.travis.yml b/.travis.yml index 04fc54e60..f48b0bb36 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ script: - docker-compose exec backend yarn run test:jest --ci --verbose=false --coverage - docker-compose exec backend yarn run db:reset - docker-compose exec backend yarn run db:seed - - docker-compose exec backend yarn run test:cucumber + - docker-compose exec backend yarn run test:cucumber --tags "not @wip" - docker-compose exec backend yarn run db:reset - docker-compose exec backend yarn run db:seed # Frontend diff --git a/backend/package.json b/backend/package.json index f6cb0de6b..c8537ae0b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -47,12 +47,12 @@ "apollo-client": "~2.6.2", "apollo-link-context": "~1.0.14", "apollo-link-http": "~1.5.14", - "apollo-server": "~2.6.2", + "apollo-server": "~2.6.3", "bcryptjs": "~2.4.3", "cheerio": "~1.0.0-rc.3", "cors": "~2.8.5", "cross-env": "~5.2.0", - "date-fns": "2.0.0-alpha.31", + "date-fns": "2.0.0-alpha.33", "debug": "~4.1.1", "dotenv": "~8.0.0", "express": "~4.17.1", @@ -87,7 +87,7 @@ "@babel/plugin-proposal-throw-expressions": "^7.2.0", "@babel/preset-env": "~7.4.5", "@babel/register": "~7.4.4", - "apollo-server-testing": "~2.6.2", + "apollo-server-testing": "~2.6.3", "babel-core": "~7.0.0-0", "babel-eslint": "~10.0.1", "babel-jest": "~24.8.0", diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index bc9b4c525..1f0ffd79a 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -1,4 +1,4 @@ -import { rule, shield, allow, or } from 'graphql-shield' +import { rule, shield, deny, allow, or } from 'graphql-shield' /* * TODO: implement @@ -16,6 +16,12 @@ const isAdmin = rule()(async (parent, args, { user }, info) => { return user && user.role === 'admin' }) +const onlyYourself = rule({ + cache: 'no_cache', +})(async (parent, args, context, info) => { + return context.user.id === args.id +}) + const isMyOwn = rule({ cache: 'no_cache', })(async (parent, args, context, info) => { @@ -48,6 +54,13 @@ const belongsToMe = rule({ return Boolean(notification) }) +/* TODO: decide if we want to remove this check: the check + * `onlyEnabledContent` throws authorization errors only if you have + * arguments for `disabled` or `deleted` assuming these are filter + * parameters. Soft-delete middleware obfuscates data on its way out + * anyways. Furthermore, `neo4j-graphql-js` offers many ways to filter for + * data so I believe, this is not a good check anyways. + */ const onlyEnabledContent = rule({ cache: 'strict', })(async (parent, args, ctx, info) => { @@ -81,46 +94,62 @@ const isAuthor = rule({ }) // Permissions -const permissions = shield({ - Query: { - Notification: isAdmin, - statistics: allow, - currentUser: allow, - Post: or(onlyEnabledContent, isModerator), +const permissions = shield( + { + Query: { + '*': deny, + findPosts: allow, + Category: isAdmin, + Tag: isAdmin, + Report: isModerator, + Notification: isAdmin, + statistics: allow, + currentUser: allow, + Post: or(onlyEnabledContent, isModerator), + Comment: allow, + User: allow, + isLoggedIn: allow, + }, + Mutation: { + '*': deny, + login: allow, + UpdateNotification: belongsToMe, + CreateUser: isAdmin, + UpdateUser: onlyYourself, + CreatePost: isAuthenticated, + UpdatePost: isAuthor, + DeletePost: isAuthor, + report: isAuthenticated, + CreateBadge: isAdmin, + UpdateBadge: isAdmin, + DeleteBadge: isAdmin, + AddUserBadges: isAdmin, + CreateSocialMedia: isAuthenticated, + DeleteSocialMedia: isAuthenticated, + // AddBadgeRewarded: isAdmin, + // RemoveBadgeRewarded: isAdmin, + reward: isAdmin, + unreward: isAdmin, + // addFruitToBasket: isAuthenticated + follow: isAuthenticated, + unfollow: isAuthenticated, + shout: isAuthenticated, + unshout: isAuthenticated, + changePassword: isAuthenticated, + enable: isModerator, + disable: isModerator, + CreateComment: isAuthenticated, + DeleteComment: isAuthor, + }, + User: { + email: isMyOwn, + password: isMyOwn, + privateKey: isMyOwn, + }, }, - Mutation: { - UpdateNotification: belongsToMe, - CreatePost: isAuthenticated, - UpdatePost: isAuthor, - DeletePost: isAuthor, - report: isAuthenticated, - CreateBadge: isAdmin, - UpdateBadge: isAdmin, - DeleteBadge: isAdmin, - AddUserBadges: isAdmin, - CreateSocialMedia: isAuthenticated, - DeleteSocialMedia: isAuthenticated, - // AddBadgeRewarded: isAdmin, - // RemoveBadgeRewarded: isAdmin, - reward: isAdmin, - unreward: isAdmin, - // addFruitToBasket: isAuthenticated - follow: isAuthenticated, - unfollow: isAuthenticated, - shout: isAuthenticated, - unshout: isAuthenticated, - changePassword: isAuthenticated, - enable: isModerator, - disable: isModerator, - CreateComment: isAuthenticated, - DeleteComment: isAuthor, - // CreateUser: allow, + { + fallbackRule: allow, }, - User: { - email: isMyOwn, - password: isMyOwn, - privateKey: isMyOwn, - }, -}) +) export default permissions diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js index 79bba0a5d..4e060dc90 100644 --- a/backend/src/middleware/slugifyMiddleware.spec.js +++ b/backend/src/middleware/slugifyMiddleware.spec.js @@ -7,12 +7,14 @@ let headers const factory = Factory() beforeEach(async () => { - await factory.create('User', { email: 'user@example.org', password: '1234' }) + const adminParams = { role: 'admin', email: 'admin@example.org', password: '1234' } + await factory.create('User', adminParams) await factory.create('User', { email: 'someone@example.org', password: '1234', }) - headers = await login({ email: 'user@example.org', password: '1234' }) + // we need to be an admin, otherwise we're not authorized to create a user + headers = await login(adminParams) authenticatedClient = new GraphQLClient(host, { headers }) }) diff --git a/backend/src/schema/resolvers/user_management.spec.js b/backend/src/schema/resolvers/user_management.spec.js index cf648a6bd..463c5ea6d 100644 --- a/backend/src/schema/resolvers/user_management.spec.js +++ b/backend/src/schema/resolvers/user_management.spec.js @@ -315,6 +315,8 @@ describe('change password', () => { describe('do not expose private RSA key', () => { let headers let client + let authenticatedClient + const queryUserPuplicKey = gql` query($queriedUserSlug: String) { User(slug: $queriedUserSlug) { @@ -332,7 +334,7 @@ describe('do not expose private RSA key', () => { } ` - const actionGenUserWithKeys = async () => { + const generateUserWithKeys = async authenticatedClient => { // Generate user with "privateKey" via 'CreateUser' mutation instead of using the factories "factory.create('User', {...})", see above. const variables = { id: 'bcb2d923-f3af-479e-9f00-61b12e864667', @@ -341,7 +343,7 @@ describe('do not expose private RSA key', () => { name: 'Apfel Strudel', email: 'apfel-strudel@test.org', } - await client.request( + await authenticatedClient.request( gql` mutation($id: ID, $password: String!, $slug: String, $name: String, $email: String!) { CreateUser(id: $id, password: $password, slug: $slug, name: $name, email: $email) { @@ -353,14 +355,23 @@ describe('do not expose private RSA key', () => { ) } - // not authenticate beforeEach(async () => { + const adminParams = { + role: 'admin', + email: 'admin@example.org', + password: '1234', + } + // create an admin user who has enough permissions to create other users + await factory.create('User', adminParams) + const headers = await login(adminParams) + authenticatedClient = new GraphQLClient(host, { headers }) + // but also create an unauthenticated client to issue the `User` query client = new GraphQLClient(host) }) describe('unauthenticated query of "publicKey" (does the RSA key pair get generated at all?)', () => { it('returns publicKey', async () => { - await actionGenUserWithKeys() + await generateUserWithKeys(authenticatedClient) await expect( await client.request(queryUserPuplicKey, { queriedUserSlug: 'apfel-strudel' }), ).toEqual( @@ -378,7 +389,7 @@ describe('do not expose private RSA key', () => { describe('unauthenticated query of "privateKey"', () => { it('throws "Not Authorised!"', async () => { - await actionGenUserWithKeys() + await generateUserWithKeys(authenticatedClient) await expect( client.request(queryUserPrivateKey, { queriedUserSlug: 'apfel-strudel' }), ).rejects.toThrow('Not Authorised') @@ -393,7 +404,7 @@ describe('do not expose private RSA key', () => { describe('authenticated query of "publicKey"', () => { it('returns publicKey', async () => { - await actionGenUserWithKeys() + await generateUserWithKeys(authenticatedClient) await expect( await client.request(queryUserPuplicKey, { queriedUserSlug: 'apfel-strudel' }), ).toEqual( @@ -411,7 +422,7 @@ describe('do not expose private RSA key', () => { describe('authenticated query of "privateKey"', () => { it('throws "Not Authorised!"', async () => { - await actionGenUserWithKeys() + await generateUserWithKeys(authenticatedClient) await expect( client.request(queryUserPrivateKey, { queriedUserSlug: 'apfel-strudel' }), ).rejects.toThrow('Not Authorised') diff --git a/backend/src/schema/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js index a5c50f4f9..6334272dd 100644 --- a/backend/src/schema/resolvers/users.spec.js +++ b/backend/src/schema/resolvers/users.spec.js @@ -1,5 +1,5 @@ import { GraphQLClient } from 'graphql-request' -import { host } from '../../jest/helpers' +import { login, host } from '../../jest/helpers' import Factory from '../../seed/factories' const factory = Factory() @@ -18,27 +18,58 @@ describe('users', () => { } } ` - client = new GraphQLClient(host) - - it('with password and email', async () => { + describe('given valid password and email', () => { const variables = { name: 'John Doe', password: '123', email: '123@123.de', } - const expected = { - CreateUser: { - id: expect.any(String), - }, - } - await expect(client.request(mutation, variables)).resolves.toEqual(expected) + + describe('unauthenticated', () => { + beforeEach(async () => { + client = new GraphQLClient(host) + }) + + it('is not allowed to create users', async () => { + await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised') + }) + }) + + describe('authenticated admin', () => { + beforeEach(async () => { + const adminParams = { + role: 'admin', + email: 'admin@example.org', + password: '1234', + } + await factory.create('User', adminParams) + const headers = await login(adminParams) + client = new GraphQLClient(host, { headers }) + }) + + it('is allowed to create new users', async () => { + const expected = { + CreateUser: { + id: expect.any(String), + }, + } + await expect(client.request(mutation, variables)).resolves.toEqual(expected) + }) + }) }) }) describe('UpdateUser', () => { - beforeEach(async () => { - await factory.create('User', { id: 'u47', name: 'John Doe' }) - }) + const userParams = { + email: 'user@example.org', + password: '1234', + id: 'u47', + name: 'John Doe', + } + const variables = { + id: 'u47', + name: 'John Doughnut', + } const mutation = ` mutation($id: ID!, $name: String) { @@ -48,38 +79,62 @@ describe('users', () => { } } ` - client = new GraphQLClient(host) - it('name within specifications', async () => { - const variables = { - id: 'u47', - name: 'James Doe', - } - const expected = { - UpdateUser: { - id: 'u47', + beforeEach(async () => { + await factory.create('User', userParams) + }) + + describe('as another user', () => { + beforeEach(async () => { + const someoneElseParams = { + email: 'someoneElse@example.org', + password: '1234', name: 'James Doe', - }, - } - await expect(client.request(mutation, variables)).resolves.toEqual(expected) + } + + await factory.create('User', someoneElseParams) + const headers = await login(someoneElseParams) + client = new GraphQLClient(host, { headers }) + }) + + it('is not allowed to change other user accounts', async () => { + await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised') + }) }) - it('with no name', async () => { - const variables = { - id: 'u47', - name: null, - } - const expected = 'Username must be at least 3 characters long!' - await expect(client.request(mutation, variables)).rejects.toThrow(expected) - }) + describe('as the same user', () => { + beforeEach(async () => { + const headers = await login(userParams) + client = new GraphQLClient(host, { headers }) + }) - it('with too short name', async () => { - const variables = { - id: 'u47', - name: ' ', - } - const expected = 'Username must be at least 3 characters long!' - await expect(client.request(mutation, variables)).rejects.toThrow(expected) + it('name within specifications', async () => { + const expected = { + UpdateUser: { + id: 'u47', + name: 'John Doughnut', + }, + } + await expect(client.request(mutation, variables)).resolves.toEqual(expected) + }) + + it('with no name', async () => { + const variables = { + id: 'u47', + name: null, + } + const expected = 'Username must be at least 3 characters long!' + await expect(client.request(mutation, variables)).rejects.toThrow(expected) + }) + + it('with too short name', async () => { + const variables = { + id: 'u47', + name: ' ', + } + const expected = 'Username must be at least 3 characters long!' + await expect(client.request(mutation, variables)).rejects.toThrow(expected) + }) }) }) }) diff --git a/backend/test/features/activity-follow.feature b/backend/test/features/activity-follow.feature index 3cfe73340..7aa0c447d 100644 --- a/backend/test/features/activity-follow.feature +++ b/backend/test/features/activity-follow.feature @@ -10,6 +10,7 @@ Feature: Follow a user | stuart-little | | tero-vota | + @wip Scenario: Send a follow to a user inbox and make sure it's added to the right followers collection When I send a POST request with the following activity to "/activitypub/users/tero-vota/inbox": """ diff --git a/backend/test/features/activity-like.feature b/backend/test/features/activity-like.feature index ec8c99110..26ef9c857 100644 --- a/backend/test/features/activity-like.feature +++ b/backend/test/features/activity-like.feature @@ -27,6 +27,7 @@ Feature: Like an object like an article or note } """ + @wip Scenario: Send a like of a person to an users inbox and make sure it's added to the likes collection When I send a POST request with the following activity to "/activitypub/users/karl-heinz/inbox": """ diff --git a/backend/yarn.lock b/backend/yarn.lock index e92070fe9..d2c5da176 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1029,10 +1029,10 @@ "@types/express-serve-static-core" "*" "@types/serve-static" "*" -"@types/express@4.16.1": - version "4.16.1" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.16.1.tgz#d756bd1a85c34d87eaf44c888bad27ba8a4b7cf0" - integrity sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg== +"@types/express@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.0.tgz#49eaedb209582a86f12ed9b725160f12d04ef287" + integrity sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "*" @@ -1351,14 +1351,14 @@ apollo-engine-reporting-protobuf@0.3.1: dependencies: protobufjs "^6.8.6" -apollo-engine-reporting@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.3.0.tgz#50151811a0f5e70f4a73e7092a61fec422d8e722" - integrity sha512-xP+Z+wdQH4ee7xfuP3WsJcIe30AH68gpp2lQm2+rnW5JfjIqD5YehSoO2Svi2jK3CSv8Y561i3QMW9i34P7hEQ== +apollo-engine-reporting@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.3.1.tgz#f2c2c63f865871a57c15cdbb2a3bcd4b4af28115" + integrity sha512-e0Xp+0yite8DH/xm9fnJt42CxfWAcY6waiq3icCMAgO9T7saXzVOPpl84SkuA+hIJUBtfaKrTnC+7Jxi/I7OrQ== dependencies: apollo-engine-reporting-protobuf "0.3.1" apollo-graphql "^0.3.0" - apollo-server-core "2.6.2" + apollo-server-core "2.6.3" apollo-server-env "2.4.0" async-retry "^1.2.1" graphql-extensions "0.7.2" @@ -1431,17 +1431,17 @@ apollo-server-caching@0.4.0: dependencies: lru-cache "^5.0.0" -apollo-server-core@2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.6.2.tgz#a792b50d4df9e26ec03759a31fbcbce38361b218" - integrity sha512-AbAnfoQ26NPsNIyBa/BVKBtA/wRsNL/E6eEem1VIhzitfgO25bVXFbEZDLxbgz6wvJ+veyRFpse7Qi1bvRpxOw== +apollo-server-core@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.6.3.tgz#786c8251c82cf29acb5cae9635a321f0644332ae" + integrity sha512-tfC0QO1NbJW3ShkB5pRCnUaYEkW2AwnswaTeedkfv//EO3yiC/9LeouCK5F22T8stQG+vGjvCqf0C8ldI/XsIA== dependencies: "@apollographql/apollo-tools" "^0.3.6" "@apollographql/graphql-playground-html" "1.6.20" "@types/ws" "^6.0.0" apollo-cache-control "0.7.2" apollo-datasource "0.5.0" - apollo-engine-reporting "1.3.0" + apollo-engine-reporting "1.3.1" apollo-server-caching "0.4.0" apollo-server-env "2.4.0" apollo-server-errors "2.3.0" @@ -1479,18 +1479,18 @@ apollo-server-errors@2.3.0: resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-2.3.0.tgz#700622b66a16dffcad3b017e4796749814edc061" integrity sha512-rUvzwMo2ZQgzzPh2kcJyfbRSfVKRMhfIlhY7BzUfM4x6ZT0aijlgsf714Ll3Mbf5Fxii32kD0A/DmKsTecpccw== -apollo-server-express@2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.6.2.tgz#526297c01a7a32fe9215566f9fd7ff92e82f1fa0" - integrity sha512-nbL3noJ5KxKGg+hT8UsAA7++oHWq/KNSevfdCluWTfUNqH1vYRTvAnARx/6JM06S9zcPTfOLcqwHnDnY9zYFxA== +apollo-server-express@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.6.3.tgz#62034c978f84207615c0430fb37ab006f71146fe" + integrity sha512-8ca+VpKArgNzFar0D3DesWnn0g9YDtFLhO56TQprHh2Spxu9WxTnYNjsYs2MCCNf+iV/uy7vTvEknErvnIcZaQ== dependencies: "@apollographql/graphql-playground-html" "1.6.20" "@types/accepts" "^1.3.5" "@types/body-parser" "1.17.0" "@types/cors" "^2.8.4" - "@types/express" "4.16.1" + "@types/express" "4.17.0" accepts "^1.3.5" - apollo-server-core "2.6.2" + apollo-server-core "2.6.3" body-parser "^1.18.3" cors "^2.8.4" graphql-subscriptions "^1.0.0" @@ -1523,20 +1523,20 @@ apollo-server-plugin-base@0.5.2: resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.5.2.tgz#f97ba983f1e825fec49cba8ff6a23d00e1901819" integrity sha512-j81CpadRLhxikBYHMh91X4aTxfzFnmmebEiIR9rruS6dywWCxV2aLW87l9ocD1MiueNam0ysdwZkX4F3D4csNw== -apollo-server-testing@~2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.6.2.tgz#e0ecddd565fce1c38a346f9fbe6118f543ccf6a6" - integrity sha512-I9QLFk4I/z9oOIXfnLc8RPBYAKih6Olrg3RDeRvWhDjLQ8gfALXVhCO+7WuvM35wNZcZVn7aXBeZ8Y3mlgkj8w== +apollo-server-testing@~2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.6.3.tgz#a0199a5d42000e60ecf0dea44b851f5f581e280e" + integrity sha512-LTkegcGVSkM+pA0FINDSYVl3TiFYKZyfjlKrEr/LN6wLiL6gbRgy6LMtk2j+qli/bnTDqqQREX8OEqmV8FKUoQ== dependencies: - apollo-server-core "2.6.2" + apollo-server-core "2.6.3" -apollo-server@~2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.6.2.tgz#33fe894b740588f059a7679346516ffce50377d5" - integrity sha512-fMXaAKIb0dX0lzcZ4zlu7ay1L596d9HTNkdn8cKuM7zmTpugZSAL966COguJUDSjUS9CaB1Kh5hl1yRuRqHXSA== +apollo-server@~2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.6.3.tgz#71235325449c6d3881a5143975ca44c07a07d2d7" + integrity sha512-pTIXE5xEMAikKLTIBIqLNvimMETiZbzmiqDb6BGzIUicAz4Rxa1/+bDi1ZeJWrZQjE/TfBLd2Si3qam7dZGrjw== dependencies: - apollo-server-core "2.6.2" - apollo-server-express "2.6.2" + apollo-server-core "2.6.3" + apollo-server-express "2.6.3" express "^4.0.0" graphql-subscriptions "^1.0.0" graphql-tools "^4.0.0" @@ -2586,10 +2586,10 @@ data-urls@^1.0.0: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" -date-fns@2.0.0-alpha.31: - version "2.0.0-alpha.31" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.31.tgz#51bcfdca25dfc9bea334a556ab33dfc0bb00421c" - integrity sha512-S19PwMqnbYsqcbCg02Yj9gv4veVNZ0OX7v2+zcd+Mq0RI7LoDKJipJjnMrTZ3Cc6blDuTce5G/pHXcVIGRwJWQ== +date-fns@2.0.0-alpha.33: + version "2.0.0-alpha.33" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.33.tgz#c2f73c3cc50ac301c9217eb93603c9bc40e891bf" + integrity sha512-tqUVEk3oxnJuNIvwAMKHAMo4uFRG0zXvjxZQll+BonoPt+m4NMcUgO14NDxbHuy7uYcrVErd2GdSsw02EDZQ7w== debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" diff --git a/package.json b/package.json index dd7454c54..fed6c742b 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ "codecov": "^3.5.0", "cross-env": "^5.2.0", "cypress": "^3.3.1", - "cypress-cucumber-preprocessor": "^1.11.2", - "cypress-file-upload": "^3.1.2", + "cypress-cucumber-preprocessor": "^1.12.0", + "cypress-file-upload": "^3.1.4", "cypress-plugin-retries": "^1.2.2", "dotenv": "^8.0.0", "faker": "^4.1.0", diff --git a/webapp/components/Avatar/Avatar.spec.js b/webapp/components/Avatar/Avatar.spec.js index ae91fecfe..d3ebcb030 100644 --- a/webapp/components/Avatar/Avatar.spec.js +++ b/webapp/components/Avatar/Avatar.spec.js @@ -1,9 +1,11 @@ import { mount, createLocalVue } from '@vue/test-utils' import Styleguide from '@human-connection/styleguide' import Avatar from './Avatar.vue' +import Filters from '~/plugins/vue-filters' const localVue = createLocalVue() localVue.use(Styleguide) +localVue.use(Filters) describe('Avatar.vue', () => { let propsData = {} diff --git a/webapp/components/Avatar/Avatar.vue b/webapp/components/Avatar/Avatar.vue index 0d997c745..ec2f9b28b 100644 --- a/webapp/components/Avatar/Avatar.vue +++ b/webapp/components/Avatar/Avatar.vue @@ -1,5 +1,10 @@ diff --git a/webapp/components/Image/spec.js b/webapp/components/Image/spec.js deleted file mode 100644 index be568964a..000000000 --- a/webapp/components/Image/spec.js +++ /dev/null @@ -1,39 +0,0 @@ -import { shallowMount } from '@vue/test-utils' -import Image from '.' - -describe('Image', () => { - let propsData = { imageProps: { class: 'hc-badge', src: '' } } - - const Wrapper = () => { - return shallowMount(Image, { propsData }) - } - - it('renders', () => { - expect(Wrapper().is('img')).toBe(true) - }) - - it('passes properties down to `img`', () => { - expect(Wrapper().classes()).toEqual(['hc-badge']) - }) - - describe('given a relative `src`', () => { - beforeEach(() => { - propsData.imageProps.src = '/img/badges/fundraisingbox_de_airship.svg' - }) - - it('adds a prefix to load the image from the backend', () => { - expect(Wrapper().attributes('src')).toBe('/api/img/badges/fundraisingbox_de_airship.svg') - }) - }) - - describe('given an absolute `src`', () => { - beforeEach(() => { - propsData.imageProps.src = 'http://lorempixel.com/640/480/animals' - }) - - it('keeps the URL as is', () => { - // e.g. our seeds have absolute image URLs - expect(Wrapper().attributes('src')).toBe('http://lorempixel.com/640/480/animals') - }) - }) -}) diff --git a/webapp/components/Password/Change.spec.js b/webapp/components/Password/Change.spec.js index cffe86466..a15695a55 100644 --- a/webapp/components/Password/Change.spec.js +++ b/webapp/components/Password/Change.spec.js @@ -1,10 +1,12 @@ import { mount, createLocalVue } from '@vue/test-utils' import ChangePassword from './Change.vue' import Styleguide from '@human-connection/styleguide' +import Filters from '~/plugins/vue-filters' const localVue = createLocalVue() localVue.use(Styleguide) +localVue.use(Filters) describe('ChangePassword.vue', () => { let mocks diff --git a/webapp/components/PostCard/index.vue b/webapp/components/PostCard/index.vue index d8172cb77..64c96f634 100644 --- a/webapp/components/PostCard/index.vue +++ b/webapp/components/PostCard/index.vue @@ -1,6 +1,9 @@