diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 000000000..ce4572cce
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+v17.9.0
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cb3b6a9a0..acc917e13 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,8 +4,27 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
+#### [1.0.7](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.0.6...1.0.7)
+
+- Bump rosie from 2.0.1 to 2.1.0 [`#4520`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4520)
+- fix: 🍰 Renew JWT In Decode Test [`#4798`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4798)
+- docs: 🍰 Refine Main README.md With Test Tech Stack And Video Link [`#4772`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4772)
+- docs: 🍰 Change README.md DB Commands For Docker [`#4765`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4765)
+- Bump date-fns from 2.23.0 to 2.25.0 [`#4753`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4753)
+- Bump neo4j-driver from 4.0.2 to 4.3.4 [`#4729`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4729)
+- chore: 🍰 Update Neode To v0.4.7 [`#4751`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4751)
+- doc: 🍰 Update README.md Etc. [`#4733`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4733)
+- feat: 🍰 New CSS For Internal Pages [`#4741`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4741)
+- fix: 🍰 Change Notification E-Mails Settings Page Link [`#4742`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4742)
+- Refactor internal pages to new CSS [`acad80c`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/acad80c3c8262934dd2e38961c08c0fde769099a)
+- Renew JWT in decode test [`46eb6b8`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/46eb6b82ea802d4d6ca7294cd32d1fe16425bfea)
+- Revert "Renew JWT in decode test" only for changing the Neode version [`a0d92b4`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/a0d92b4853d09d725c1fb7886cbfed2a00e1f05c)
+
#### [1.0.6](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.0.5...1.0.6)
+> 4 October 2021
+
+- chore: 🍰 Set 'sendNotification' Emails Init Admin [`#4690`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4690)
- feat: 🍰 Send Notification E-Mail [`#4623`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4623)
- feat: 🍰 Implement Progress Bar Again [`#4357`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4357)
- Bump nodemailer-html-to-text from 3.1.0 to 3.2.0 in /backend [`#4531`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4531)
diff --git a/README.md b/README.md
index 603ea8178..45006581c 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,11 @@ At the same time, it should be possible in the future to link these networks wit
In other words, we are interested in a network of networks and in keeping the data as close as possible to the user and the operator they trusts.
+## Introduction
+
+Have a look into our short video:
+[ocelot.social - GitHub - Developer Welcome - Tutorial (english)](https://www.youtube.com/watch?v=gZSL6KvBIiY&list=PLFMD5liPP01kbuReHxYXxv_1fI5rIgS1f&index=1)
+
## Directory Layout
There are three important directories:
@@ -192,6 +197,14 @@ The only deployment method in this repository for development purposes as descri
* [NodeJS](https://nodejs.org/en/)
* [Neo4J](https://neo4j.com/)
+### For Testing
+
+* [Cypress](https://docs.cypress.io/)
+* [Storybook](https://storybook.js.org/)
+* [Jest](https://jestjs.io/)
+* [Vue Test Utils](https://vue-test-utils.vuejs.org/)
+* [ESLint](https://eslint.org/)
+
## Attributions
Locale Icons made by [Freepik](http://www.freepik.com/) from [www.flaticon.com](https://www.flaticon.com/) is licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/).
diff --git a/backend/.nvmrc b/backend/.nvmrc
new file mode 100644
index 000000000..e4ab7d287
--- /dev/null
+++ b/backend/.nvmrc
@@ -0,0 +1 @@
+v12.19.0
\ No newline at end of file
diff --git a/backend/package.json b/backend/package.json
index 6779c63ff..e0d53b5b9 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,6 +1,6 @@
{
"name": "ocelot-social-backend",
- "version": "1.0.6",
+ "version": "1.0.7",
"description": "GraphQL Backend for ocelot.social",
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
"author": "ocelot.social Community",
@@ -66,7 +66,6 @@
"debug": "~4.1.1",
"dotenv": "~8.2.0",
"express": "^4.17.1",
- "faker": "Marak/faker.js#master",
"graphql": "^14.6.0",
"graphql-custom-directives": "~0.2.14",
"graphql-iso-date": "~3.6.1",
@@ -120,6 +119,7 @@
"xregexp": "^4.3.0"
},
"devDependencies": {
+ "@faker-js/faker": "5.1.0",
"apollo-server-testing": "~2.11.0",
"chai": "~4.2.0",
"cucumber": "~6.0.5",
diff --git a/backend/src/db/factories.js b/backend/src/db/factories.js
index 6173238ae..3e164d51b 100644
--- a/backend/src/db/factories.js
+++ b/backend/src/db/factories.js
@@ -1,8 +1,8 @@
import { v4 as uuid } from 'uuid'
-import faker from 'faker'
import slugify from 'slug'
import { hashSync } from 'bcryptjs'
import { Factory } from 'rosie'
+import faker from '@faker-js/faker'
import { getDriver, getNeode } from './neo4j'
import CONFIG from '../config/index.js'
import generateInviteCode from '../schema/resolvers/helpers/generateInviteCode.js'
diff --git a/backend/src/db/migrations/20210506150512-add-donations-node.js b/backend/src/db/migrations/20210506150512-add-donations-node.js
index 699c451cf..6cbc1e897 100644
--- a/backend/src/db/migrations/20210506150512-add-donations-node.js
+++ b/backend/src/db/migrations/20210506150512-add-donations-node.js
@@ -19,8 +19,8 @@ export async function up(next) {
SET donationInfo.createdAt = toString(datetime())
SET donationInfo.updatedAt = donationInfo.createdAt
SET donationInfo.showDonations = false
- SET donationInfo.goal = 15000
- SET donationInfo.progress = 1200
+ SET donationInfo.goal = 15000.0
+ SET donationInfo.progress = 1200.0
RETURN donationInfo {.*}
`,
{ donationId },
diff --git a/backend/src/db/seed.js b/backend/src/db/seed.js
index 7d9b5e954..786ad6fc8 100644
--- a/backend/src/db/seed.js
+++ b/backend/src/db/seed.js
@@ -1,7 +1,7 @@
-import faker from 'faker'
import sample from 'lodash/sample'
import { createTestClient } from 'apollo-server-testing'
import createServer from '../server'
+import faker from '@faker-js/faker'
import Factory from '../db/factories'
import { getNeode, getDriver } from '../db/neo4j'
import { gql } from '../helpers/jest'
diff --git a/backend/src/jwt/decode.spec.js b/backend/src/jwt/decode.spec.js
index 78ceadecb..bb247f028 100644
--- a/backend/src/jwt/decode.spec.js
+++ b/backend/src/jwt/decode.spec.js
@@ -21,8 +21,14 @@ const neode = getNeode()
// iss: 'http://localhost:4000',
// sub: 'u3'
// }
+// !!! if the token expires go into the GraphQL Playground in the browser at 'http://localhost:4000' with a running backend and a seeded Neo4j database
+// now do the login mutation:
+// mutation {
+// login(email:"user@example.org", password:"1234")
+// }
+// replace this token here with the one you received as the result
export const validAuthorizationHeader =
- 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsImxvY2F0aW9uTmFtZSI6bnVsbCwibmFtZSI6Ikplbm55IFJvc3RvY2siLCJhYm91dCI6bnVsbCwiYXZhdGFyIjoiaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL3VpZmFjZXMvZmFjZXMvdHdpdHRlci9zYXNoYV9zaGVzdGFrb3YvMTI4LmpwZyIsImlkIjoidTMiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5vcmciLCJzbHVnIjoiamVubnktcm9zdG9jayIsImlhdCI6MTU1MDg0NjY4MCwiZXhwIjoxNjM3MjQ2NjgwLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQwMDAiLCJzdWIiOiJ1MyJ9.eZ_mVKas4Wzoc_JrQTEWXyRn7eY64cdIg4vqQ-F_7Jc'
+ 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InUzIiwibmFtZSI6Ikplbm55IFJvc3RvY2siLCJzbHVnIjoiamVubnktcm9zdG9jayIsImlhdCI6MTYzNzY0NDMwMCwiZXhwIjoxNzAwNzU5NTAwLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQwMDAiLCJzdWIiOiJ1MyJ9.ispIfRfgkXuYoIhKx7x2jPxgvHDJVv1ogMycLmfUnsk'
beforeAll(async () => {
await cleanDatabase()
@@ -47,6 +53,7 @@ describe('decode', () => {
beforeEach(() => {
authorizationHeader = null
})
+
it('returns null', returnsNull)
})
@@ -54,6 +61,7 @@ describe('decode', () => {
beforeEach(() => {
authorizationHeader = undefined
})
+
it('returns null', returnsNull)
})
@@ -61,6 +69,7 @@ describe('decode', () => {
beforeEach(() => {
authorizationHeader = 'blah'
})
+
it('returns null', returnsNull)
})
@@ -68,6 +77,7 @@ describe('decode', () => {
beforeEach(() => {
authorizationHeader = validAuthorizationHeader
})
+
it('returns null', returnsNull)
describe('and corresponding user in the database', () => {
diff --git a/backend/src/middleware/notifications/notificationsMiddleware.js b/backend/src/middleware/notifications/notificationsMiddleware.js
index 2bc53ab7c..5419771ea 100644
--- a/backend/src/middleware/notifications/notificationsMiddleware.js
+++ b/backend/src/middleware/notifications/notificationsMiddleware.js
@@ -41,7 +41,6 @@ const publishNotifications = async (context, promises) => {
notifications.forEach((notificationAdded, index) => {
pubsub.publish(NOTIFICATION_ADDED, { notificationAdded })
if (notificationAdded.to.sendNotificationEmails) {
- // Wolle await
sendMail(
notificationTemplate({
email: notificationsEmailAddresses[index].email,
diff --git a/backend/src/schema/resolvers/users/location.spec.js b/backend/src/schema/resolvers/users/location.spec.js
index e69b48f50..f150e8594 100644
--- a/backend/src/schema/resolvers/users/location.spec.js
+++ b/backend/src/schema/resolvers/users/location.spec.js
@@ -38,8 +38,8 @@ const newlyCreatedNodesWithLocales = [
nameRU: 'Вельцхайм',
nameNL: 'Welzheim',
namePL: 'Welzheim',
- lng: 9.63444,
- lat: 48.87472,
+ lng: 9.634741,
+ lat: 48.874924,
},
state: {
id: expect.stringContaining('region'),
diff --git a/backend/yarn.lock b/backend/yarn.lock
index 117383e58..24bd00b3a 100644
--- a/backend/yarn.lock
+++ b/backend/yarn.lock
@@ -963,6 +963,11 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
+"@faker-js/faker@5.1.0":
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-5.1.0.tgz#cee1d77ada0d0dbbe77201d18b1ebabf432d9c0f"
+ integrity sha512-0VonSKh7fBCqvY+V2FLN2ZW4pR4ZtWJalWmwSaiaB7yK7y4qp8vDfuaq9QdLjf/cdZGx3M7Wc4Q+x4fZHxI21Q==
+
"@graphql-toolkit/common@0.10.4":
version "0.10.4"
resolved "https://registry.yarnpkg.com/@graphql-toolkit/common/-/common-0.10.4.tgz#7785f2a3f14559d0778859c49f4442078c196695"
@@ -4587,10 +4592,6 @@ extsprintf@^1.2.0:
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
-faker@Marak/faker.js#master:
- version "4.1.0"
- resolved "https://codeload.github.com/Marak/faker.js/tar.gz/3b2fa4aebccee52ae1bafc15d575061fb30c3cf1"
-
fast-deep-equal@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
diff --git a/cypress/integration/UserProfile.SocialMedia/I_add_a_social_media_link.js b/cypress/integration/UserProfile.SocialMedia/I_add_a_social_media_link.js
index 9253709f9..ca2e36818 100644
--- a/cypress/integration/UserProfile.SocialMedia/I_add_a_social_media_link.js
+++ b/cypress/integration/UserProfile.SocialMedia/I_add_a_social_media_link.js
@@ -1,9 +1,12 @@
import { When } from "cypress-cucumber-preprocessor/steps";
When('I add a social media link', () => {
- cy.get('input#addSocialMedia')
- .type('https://freeradical.zone/peter-pan')
- .get('button')
+ cy.get('button')
.contains('Add link')
.click()
+ .get('#editSocialMedia')
+ .type('https://freeradical.zone/peter-pan')
+ .get('button')
+ .contains('Save')
+ .click()
})
\ No newline at end of file
diff --git a/cypress/integration/UserProfile.SocialMedia/I_have_added_a_social_media_link.js b/cypress/integration/UserProfile.SocialMedia/I_have_added_a_social_media_link.js
index 203b97032..93e35bf28 100644
--- a/cypress/integration/UserProfile.SocialMedia/I_have_added_a_social_media_link.js
+++ b/cypress/integration/UserProfile.SocialMedia/I_have_added_a_social_media_link.js
@@ -2,9 +2,12 @@ import { Given } from "cypress-cucumber-preprocessor/steps";
Given('I have added a social media link', () => {
cy.visit('/settings/my-social-media')
- .get('input#addSocialMedia')
- .type('https://freeradical.zone/peter-pan')
.get('button')
.contains('Add link')
.click()
+ .get('#editSocialMedia')
+ .type('https://freeradical.zone/peter-pan')
+ .get('button')
+ .contains('Save')
+ .click()
})
\ No newline at end of file
diff --git a/package.json b/package.json
index 0fe64f55d..9b0e27d95 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ocelot-social",
- "version": "1.0.6",
+ "version": "1.0.7",
"description": "Free and open source software program code available to run social networks.",
"author": "ocelot.social Community",
"license": "MIT",
@@ -36,7 +36,7 @@
"date-fns": "^2.25.0",
"dotenv": "^8.2.0",
"expect": "^25.3.0",
- "faker": "Marak/faker.js#master",
+ "@faker-js/faker": "5.1.0",
"graphql-request": "^2.0.0",
"import": "^0.0.6",
"jsonwebtoken": "^8.5.1",
@@ -44,7 +44,7 @@
"neo4j-driver": "^4.3.4",
"neode": "^0.4.7",
"npm-run-all": "^4.1.5",
- "rosie": "^2.0.1",
+ "rosie": "^2.1.0",
"slug": "^5.1.0"
},
"resolutions": {
diff --git a/webapp/.nvmrc b/webapp/.nvmrc
new file mode 100644
index 000000000..e4ab7d287
--- /dev/null
+++ b/webapp/.nvmrc
@@ -0,0 +1 @@
+v12.19.0
\ No newline at end of file
diff --git a/webapp/components/CommentList/CommentList.story.js b/webapp/components/CommentList/CommentList.story.js
index 3ef474c80..50a2b1d1b 100644
--- a/webapp/components/CommentList/CommentList.story.js
+++ b/webapp/components/CommentList/CommentList.story.js
@@ -1,8 +1,8 @@
+import faker from '@faker-js/faker'
import { storiesOf } from '@storybook/vue'
import { withA11y } from '@storybook/addon-a11y'
import HcCommentList from './CommentList.vue'
import helpers from '~/storybook/helpers'
-import faker from 'faker'
helpers.init()
diff --git a/webapp/components/Editor/MenuLegend.vue b/webapp/components/Editor/MenuLegend.vue
index afc137b1f..2d897eeb5 100644
--- a/webapp/components/Editor/MenuLegend.vue
+++ b/webapp/components/Editor/MenuLegend.vue
@@ -3,12 +3,16 @@
@@ -60,6 +64,7 @@ export default {
{ iconName: 'quote-right', name: `editor.legend.quote`, shortcut: '> + space' },
{ iconName: 'minus', name: `editor.legend.ruler`, shortcut: '---' },
],
+ isDropdownOpen: false,
}
},
}
diff --git a/webapp/components/Embed/EmbedComponent.vue b/webapp/components/Embed/EmbedComponent.vue
index 6161f8f70..af9520538 100644
--- a/webapp/components/Embed/EmbedComponent.vue
+++ b/webapp/components/Embed/EmbedComponent.vue
@@ -179,10 +179,24 @@ export default {
}
.html {
- width: 100%;
+ // width: 100%;
+ // height: 100%;
+
+ // see this working solution here: https://stackoverflow.com/questions/35814653/automatic-height-when-embedding-a-youtube-video
+ position: relative;
+ padding-bottom: 56.25%; /* 16:9 */
+ height: 0;
iframe {
+ // width: 100%;
+ // height: auto;
+
+ // same solution example as above
+ position: absolute;
+ top: 0;
+ left: 0;
width: 100%;
+ height: 100%;
}
}
diff --git a/webapp/components/_new/features/MySomethingList/MySomethingList.spec.js b/webapp/components/_new/features/MySomethingList/MySomethingList.spec.js
new file mode 100644
index 000000000..513207110
--- /dev/null
+++ b/webapp/components/_new/features/MySomethingList/MySomethingList.spec.js
@@ -0,0 +1,128 @@
+import { mount } from '@vue/test-utils'
+import MySomethingList from './MySomethingList.vue'
+import Vue from 'vue'
+
+const localVue = global.localVue
+
+describe('MySomethingList.vue', () => {
+ let wrapper
+ let propsData
+ let data
+ let mocks
+
+ beforeEach(() => {
+ propsData = {
+ useFormData: { dummy: '' },
+ useItems: [{ id: 'id', dummy: 'dummy' }],
+ namePropertyKey: 'dummy',
+ callbacks: { edit: jest.fn(), submit: jest.fn(), delete: jest.fn() },
+ }
+ data = () => {
+ return {}
+ }
+ mocks = {
+ $t: jest.fn(),
+ $apollo: {
+ mutate: jest.fn(),
+ },
+ $toast: {
+ error: jest.fn(),
+ success: jest.fn(),
+ },
+ }
+ })
+
+ describe('mount', () => {
+ let form, slots
+ const Wrapper = () => {
+ slots = {
+ 'list-item': '
',
+ 'edit-item': '',
+ }
+ return mount(MySomethingList, {
+ propsData,
+ data,
+ mocks,
+ localVue,
+ slots,
+ })
+ }
+
+ describe('given existing item', () => {
+ beforeEach(() => {
+ wrapper = Wrapper()
+ })
+
+ describe('for each item it', () => {
+ it('displays the item as slot "list-item"', () => {
+ expect(wrapper.find('.list-item').exists()).toBe(true)
+ })
+
+ it('displays the edit button', () => {
+ expect(wrapper.find('.base-button[data-test="edit-button"]').exists()).toBe(true)
+ })
+
+ it('displays the delete button', () => {
+ expect(wrapper.find('.base-button[data-test="delete-button"]').exists()).toBe(true)
+ })
+ })
+
+ describe('editing item', () => {
+ beforeEach(async () => {
+ const editButton = wrapper.find('.base-button[data-test="edit-button"]')
+ editButton.trigger('click')
+ await Vue.nextTick()
+ })
+
+ it('disables adding items while editing', () => {
+ const submitButton = wrapper.find('.base-button[data-test="add-save-button"]')
+ expect(submitButton.text()).not.toContain('settings.social-media.submit')
+ })
+
+ it('allows the user to cancel editing', async () => {
+ expect(wrapper.find('.edit-item').exists()).toBe(true)
+ const cancelButton = wrapper.find('button#cancel')
+ cancelButton.trigger('click')
+ await Vue.nextTick()
+ expect(wrapper.find('.edit-item').exists()).toBe(false)
+ })
+ })
+
+ describe('calls callback functions', () => {
+ it('calls edit', async () => {
+ const editButton = wrapper.find('.base-button[data-test="edit-button"]')
+ editButton.trigger('click')
+ await Vue.nextTick()
+ const expectedItem = expect.objectContaining({ id: 'id', dummy: 'dummy' })
+ expect(propsData.callbacks.edit).toHaveBeenCalledTimes(1)
+ expect(propsData.callbacks.edit).toHaveBeenCalledWith(expect.any(Object), expectedItem)
+ })
+
+ it('calls submit', async () => {
+ form = wrapper.find('form')
+ form.trigger('submit')
+ await Vue.nextTick()
+ form.trigger('submit')
+ await Vue.nextTick()
+ const expectedItem = expect.objectContaining({ id: '' })
+ expect(propsData.callbacks.submit).toHaveBeenCalledTimes(1)
+ expect(propsData.callbacks.submit).toHaveBeenCalledWith(
+ expect.any(Object),
+ true,
+ expectedItem,
+ { dummy: '' },
+ )
+ })
+
+ it('calls delete', async () => {
+ const deleteButton = wrapper.find('.base-button[data-test="delete-button"]')
+ deleteButton.trigger('click')
+ await Vue.nextTick()
+ const expectedItem = expect.objectContaining({ id: 'id', dummy: 'dummy' })
+ expect(propsData.callbacks.delete).toHaveBeenCalledTimes(1)
+ expect(propsData.callbacks.delete).toHaveBeenCalledWith(expect.any(Object), expectedItem)
+ })
+ })
+ })
+ })
+})
diff --git a/webapp/components/_new/features/MySomethingList/MySomethingList.vue b/webapp/components/_new/features/MySomethingList/MySomethingList.vue
new file mode 100644
index 000000000..fbfcca932
--- /dev/null
+++ b/webapp/components/_new/features/MySomethingList/MySomethingList.vue
@@ -0,0 +1,185 @@
+
+
+
+
+
+ {{
+ isCreation
+ ? $t('settings.social-media.addNewTitle')
+ : $t('settings.social-media.editTitle', { name: editingItem[namePropertyKey] })
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+ {{ isEditing ? $t('actions.save') : $t('settings.social-media.submit') }}
+
+
+ {{ $t('actions.cancel') }}
+
+
+
+
+
+
+
+
+
diff --git a/webapp/components/_new/features/SocialMedia/SocialMediaListItem.spec.js b/webapp/components/_new/features/SocialMedia/SocialMediaListItem.spec.js
new file mode 100644
index 000000000..c7359d873
--- /dev/null
+++ b/webapp/components/_new/features/SocialMedia/SocialMediaListItem.spec.js
@@ -0,0 +1,36 @@
+import { shallowMount } from '@vue/test-utils'
+import SocialMediaListItem from './SocialMediaListItem.vue'
+
+describe('SocialMediaListItem.vue', () => {
+ let wrapper
+ let propsData
+ const socialMediaUrl = 'https://freeradical.zone/@mattwr18'
+ const faviconUrl = 'https://freeradical.zone/favicon.ico'
+
+ beforeEach(() => {
+ propsData = {}
+ })
+
+ describe('shallowMount', () => {
+ const Wrapper = () => {
+ return shallowMount(SocialMediaListItem, { propsData })
+ }
+
+ describe('given existing social media links', () => {
+ beforeEach(() => {
+ propsData = { item: { id: 's1', url: socialMediaUrl, favicon: faviconUrl } }
+ wrapper = Wrapper()
+ })
+
+ describe('for each link item it', () => {
+ it('displays the favicon', () => {
+ expect(wrapper.find(`img[src="${faviconUrl}"]`).exists()).toBe(true)
+ })
+
+ it('displays the url', () => {
+ expect(wrapper.find(`a[href="${socialMediaUrl}"]`).exists()).toBe(true)
+ })
+ })
+ })
+ })
+})
diff --git a/webapp/components/_new/features/SocialMedia/SocialMediaListItem.vue b/webapp/components/_new/features/SocialMedia/SocialMediaListItem.vue
new file mode 100644
index 000000000..75dea7ba8
--- /dev/null
+++ b/webapp/components/_new/features/SocialMedia/SocialMediaListItem.vue
@@ -0,0 +1,18 @@
+
+
+
+ {{ item.url }}
+
+
+
+
diff --git a/webapp/components/_new/generic/TabNavigation/TabNavigator.story.js b/webapp/components/_new/generic/TabNavigation/TabNavigator.story.js
index 457e78209..6da7a5763 100644
--- a/webapp/components/_new/generic/TabNavigation/TabNavigator.story.js
+++ b/webapp/components/_new/generic/TabNavigation/TabNavigator.story.js
@@ -1,3 +1,4 @@
+import faker from '@faker-js/faker'
import { storiesOf } from '@storybook/vue'
import { withA11y } from '@storybook/addon-a11y'
import HcEmpty from '~/components/Empty/Empty'
@@ -8,7 +9,6 @@ import TabNavigation from '~/components/_new/generic/TabNavigation/TabNavigation
import UserTeaser from '~/components/UserTeaser/UserTeaser'
import HcHashtag from '~/components/Hashtag/Hashtag'
import helpers from '~/storybook/helpers'
-import faker from 'faker'
import { post } from '~/components/PostTeaser/PostTeaser.story.js'
import { user } from '~/components/UserTeaser/UserTeaser.story.js'
diff --git a/webapp/locales/de.json b/webapp/locales/de.json
index 9816e167a..a98f0a320 100644
--- a/webapp/locales/de.json
+++ b/webapp/locales/de.json
@@ -773,6 +773,8 @@
"name": "Sicherheit"
},
"social-media": {
+ "addNewTitle": "Neuen Link hinzufügen",
+ "editTitle": "Link \"{name}\" ändern",
"name": "Soziale Netzwerke",
"placeholder": "Deine Webadresse des Sozialen Netzwerkes",
"requireUnique": "Dieser Link existiert bereits",
diff --git a/webapp/locales/en.json b/webapp/locales/en.json
index 11b920056..f6606b933 100644
--- a/webapp/locales/en.json
+++ b/webapp/locales/en.json
@@ -773,6 +773,8 @@
"name": "Security"
},
"social-media": {
+ "addNewTitle": "Add new link",
+ "editTitle": "Edit link \"{name}\"",
"name": "Social media",
"placeholder": "Your social media url",
"requireUnique": "You added this url already",
diff --git a/webapp/maintenance/source/package.json b/webapp/maintenance/source/package.json
index 2391b5c91..e38f34370 100644
--- a/webapp/maintenance/source/package.json
+++ b/webapp/maintenance/source/package.json
@@ -1,6 +1,6 @@
{
"name": "@ocelot-social/maintenance",
- "version": "1.0.6",
+ "version": "1.0.7",
"description": "Maintenance page for ocelot.social",
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
"author": "ocelot.social Community",
diff --git a/webapp/nuxt.config.js b/webapp/nuxt.config.js
index b4fc3aa7c..717aa0339 100644
--- a/webapp/nuxt.config.js
+++ b/webapp/nuxt.config.js
@@ -201,6 +201,7 @@ export default {
/** * A Boolean indicating if the cookie transmission requires a
* secure protocol (https). Defaults to false. */
secure: CONFIG.COOKIE_HTTPS_ONLY,
+ sameSite: 'lax', // for the meaning see https://www.thinktecture.com/de/identity/samesite/samesite-in-a-nutshell/
},
// includeNodeModules: true, // optional, default: false (this includes graphql-tag for node_modules folder)
diff --git a/webapp/package.json b/webapp/package.json
index 0df821b44..cf4354dde 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -1,6 +1,6 @@
{
"name": "ocelot-social-webapp",
- "version": "1.0.6",
+ "version": "1.0.7",
"description": "ocelot.social Frontend",
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
"author": "ocelot.social Community",
@@ -108,6 +108,7 @@
"@babel/core": "~7.12.3",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/preset-env": "~7.9.0",
+ "@faker-js/faker": "5.1.0",
"@storybook/addon-a11y": "^6.3.6",
"@storybook/addon-actions": "^5.3.21",
"@storybook/addon-notes": "^5.3.18",
@@ -136,7 +137,6 @@
"eslint-plugin-promise": "~4.3.1",
"eslint-plugin-standard": "~5.0.0",
"eslint-plugin-vue": "~6.2.2",
- "faker": "^5.1.0",
"flush-promises": "^1.0.2",
"identity-obj-proxy": "^3.0.0",
"jest": "~26.6.3",
diff --git a/webapp/pages/settings/my-social-media.spec.js b/webapp/pages/settings/my-social-media.spec.js
index 1aadbb750..7136e1e7c 100644
--- a/webapp/pages/settings/my-social-media.spec.js
+++ b/webapp/pages/settings/my-social-media.spec.js
@@ -33,7 +33,7 @@ describe('my-social-media.vue', () => {
})
describe('mount', () => {
- let form, input, submitButton
+ let form, input
const Wrapper = () => {
const store = new Vuex.Store({
getters,
@@ -42,11 +42,12 @@ describe('my-social-media.vue', () => {
}
describe('adding social media link', () => {
- beforeEach(() => {
+ beforeEach(async () => {
wrapper = Wrapper()
form = wrapper.find('form')
- input = wrapper.find('input#addSocialMedia')
- submitButton = wrapper.find('button')
+ form.trigger('submit')
+ await Vue.nextTick()
+ input = wrapper.find('input#editSocialMedia')
})
it('requires the link to be a valid url', async () => {
@@ -79,7 +80,6 @@ describe('my-social-media.vue', () => {
const expected = expect.objectContaining({
variables: { url: newSocialMediaUrl },
})
-
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
@@ -88,10 +88,10 @@ describe('my-social-media.vue', () => {
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
})
- it('clears the form', async () => {
+ it('switches back to list', async () => {
await flushPromises()
- expect(input.value).toBe(undefined)
- expect(submitButton.vm.$attrs.disabled).toBe(true)
+ const submitButton = wrapper.find('.base-button[data-test="add-save-button"]')
+ expect(submitButton.text()).not.toContain('settings.social-media.submit')
})
})
})
@@ -100,10 +100,9 @@ describe('my-social-media.vue', () => {
beforeEach(() => {
getters = {
'auth/user': () => ({
- socialMedia: [{ id: 's1', url: socialMediaUrl }],
+ socialMedia: [{ id: 's1', url: socialMediaUrl, favicon: faviconUrl }],
}),
}
-
wrapper = Wrapper()
form = wrapper.find('form')
})
@@ -116,18 +115,12 @@ describe('my-social-media.vue', () => {
it('displays the url', () => {
expect(wrapper.find(`a[href="${socialMediaUrl}"]`).exists()).toBe(true)
})
-
- it('displays the edit button', () => {
- expect(wrapper.find('.base-button[data-test="edit-button"]').exists()).toBe(true)
- })
-
- it('displays the delete button', () => {
- expect(wrapper.find('.base-button[data-test="delete-button"]').exists()).toBe(true)
- })
})
it('does not accept a duplicate url', async () => {
- wrapper.find('input#addSocialMedia').setValue(socialMediaUrl)
+ form.trigger('submit')
+ await Vue.nextTick()
+ wrapper.find('input#editSocialMedia').setValue(socialMediaUrl)
form.trigger('submit')
await Vue.nextTick()
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
@@ -141,12 +134,6 @@ describe('my-social-media.vue', () => {
input = wrapper.find('input#editSocialMedia')
})
- it('disables adding new links while editing', () => {
- const addInput = wrapper.find('input#addSocialMedia')
-
- expect(addInput.exists()).toBe(false)
- })
-
it('sends the new url to the backend', async () => {
const expected = expect.objectContaining({
variables: { id: 's1', url: newSocialMediaUrl },
@@ -156,13 +143,6 @@ describe('my-social-media.vue', () => {
await Vue.nextTick()
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
-
- it('allows the user to cancel editing', async () => {
- const cancelButton = wrapper.find('button#cancel')
- cancelButton.trigger('click')
- await Vue.nextTick()
- expect(wrapper.find('input#editSocialMedia').exists()).toBe(false)
- })
})
describe('deleting social media link', () => {
@@ -176,7 +156,6 @@ describe('my-social-media.vue', () => {
const expected = expect.objectContaining({
variables: { id: 's1' },
})
-
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
diff --git a/webapp/pages/settings/my-social-media.vue b/webapp/pages/settings/my-social-media.vue
index 5753fc405..a985371d6 100644
--- a/webapp/pages/settings/my-social-media.vue
+++ b/webapp/pages/settings/my-social-media.vue
@@ -1,97 +1,73 @@
-
-
- {{ $t('settings.social-media.name') }}
-
-
-
-
-
-
-
-
- {{ link.url }}
-
- |
-
-
-
-
-
-
-
-
+
+ {{ $t('settings.social-media.name') }}
+
+
+
+
+
-
-
- {{ editingLink.id ? $t('actions.save') : $t('settings.social-media.submit') }}
-
-
- {{ $t('actions.cancel') }}
-
-
-
-
-
+
+
+
-
-
diff --git a/webapp/plugins/i18n.js b/webapp/plugins/i18n.js
index 8c64ef0c3..f162408f6 100644
--- a/webapp/plugins/i18n.js
+++ b/webapp/plugins/i18n.js
@@ -21,18 +21,30 @@ export default ({ app, req, cookie, store }) => {
const changeHandler = async (mutation) => {
if (process.server) return
- const newLocale = mutation.payload.locale
- const currentLocale = await app.$cookies.get(key)
- const isDifferent = newLocale !== currentLocale
+ const localeInStore = mutation.payload.locale
+ let cookieExists = true
+ let localeInCookies = await app.$cookies.get(key)
+ if (!localeInCookies) {
+ cookieExists = false
+ localeInCookies = navigator.language.split('-')[0] // get browser language
+ }
+ const isLocaleStoreSameAsCookies = localeInStore === localeInCookies
- if (!isDifferent) {
+ // cookie has to be set, otherwise Cypress test does not work
+ if (cookieExists && isLocaleStoreSameAsCookies) {
return
}
- app.$cookies.set(key, newLocale)
- if (!app.$i18n.localeExists(newLocale)) {
- import(`~/locales/${newLocale}.json`).then((res) => {
- app.$i18n.add(newLocale, res.default)
+ const expires = new Date()
+ expires.setDate(expires.getDate() + app.$env.COOKIE_EXPIRE_TIME)
+ app.$cookies.set(key, localeInStore, {
+ expires,
+ // maxAge: app.$env.COOKIE_EXPIRE_TIME * 60 * 60 * 24, // days to seconds
+ sameSite: 'lax', // for the meaning see https://www.thinktecture.com/de/identity/samesite/samesite-in-a-nutshell/
+ })
+ if (!app.$i18n.localeExists(localeInStore)) {
+ import(`~/locales/${localeInStore}.json`).then((res) => {
+ app.$i18n.add(localeInStore, res.default)
})
}
@@ -42,7 +54,7 @@ export default ({ app, req, cookie, store }) => {
if (user && user._id && token) {
// TODO: SAVE LOCALE
// store.dispatch('usersettings/patch', {
- // uiLanguage: newLocale
+ // uiLanguage: localeInStore
// }, { root: true })
}
}
diff --git a/webapp/storybook/helpers.js b/webapp/storybook/helpers.js
index 5fcd8e64b..e53e1d554 100644
--- a/webapp/storybook/helpers.js
+++ b/webapp/storybook/helpers.js
@@ -1,14 +1,13 @@
import Vue from 'vue'
import Vuex from 'vuex'
-import faker from 'faker'
import vuexI18n from 'vuex-i18n/dist/vuex-i18n.umd.js'
import Styleguide from '@human-connection/styleguide'
+import faker from '@faker-js/faker'
import Filters from '~/plugins/vue-filters'
import Directives from '~/plugins/vue-directives'
import IziToast from '~/plugins/izi-toast'
import layout from './layout.vue'
import locales from '~/locales/index.js'
-
import '~/plugins/v-tooltip'
const helpers = {
diff --git a/webapp/yarn.lock b/webapp/yarn.lock
index 9ff5a19b7..ef71a2a53 100644
--- a/webapp/yarn.lock
+++ b/webapp/yarn.lock
@@ -2379,6 +2379,11 @@
ts-node "^8"
tslib "^1"
+"@faker-js/faker@5.1.0":
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-5.1.0.tgz#cee1d77ada0d0dbbe77201d18b1ebabf432d9c0f"
+ integrity sha512-0VonSKh7fBCqvY+V2FLN2ZW4pR4ZtWJalWmwSaiaB7yK7y4qp8vDfuaq9QdLjf/cdZGx3M7Wc4Q+x4fZHxI21Q==
+
"@hapi/address@2.x.x":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.0.0.tgz#9f05469c88cb2fd3dcd624776b54ee95c312126a"
@@ -10235,11 +10240,6 @@ fake-tag@^1.0.0:
resolved "https://registry.yarnpkg.com/fake-tag/-/fake-tag-1.0.1.tgz#1d59da482240a02bd83500ca98976530ed154b0d"
integrity sha512-qmewZoBpa71mM+y6oxXYW/d1xOYQmeIvnEXAt1oCmdP0sqcogWYLepR87QL1jQVLSVMVYDq2cjY6ec/Wu8/4pg==
-faker@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/faker/-/faker-5.1.0.tgz#e10fa1dec4502551aee0eb771617a7e7b94692e8"
- integrity sha512-RrWKFSSA/aNLP0g3o2WW1Zez7/MnMr7xkiZmoCfAGZmdkDQZ6l2KtuXHN5XjdvpRjDl8+3vf+Rrtl06Z352+Mw==
-
fast-deep-equal@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
diff --git a/yarn.lock b/yarn.lock
index 01666ca81..4e2303801 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1248,6 +1248,11 @@
debug "^3.1.0"
lodash.once "^4.1.1"
+"@faker-js/faker@5.1.0":
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-5.1.0.tgz#cee1d77ada0d0dbbe77201d18b1ebabf432d9c0f"
+ integrity sha512-0VonSKh7fBCqvY+V2FLN2ZW4pR4ZtWJalWmwSaiaB7yK7y4qp8vDfuaq9QdLjf/cdZGx3M7Wc4Q+x4fZHxI21Q==
+
"@hapi/address@2.x.x":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.0.0.tgz#9f05469c88cb2fd3dcd624776b54ee95c312126a"
@@ -3058,10 +3063,6 @@ extsprintf@^1.2.0:
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
-faker@Marak/faker.js#master:
- version "5.1.0"
- resolved "https://codeload.github.com/Marak/faker.js/tar.gz/91dc8a3372426bc691be56153b33e81a16459f49"
-
fast-deep-equal@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
@@ -5274,10 +5275,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
-rosie@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/rosie/-/rosie-2.0.1.tgz#c250c4787ce450b72aa9eff26509f68589814fa2"
- integrity sha1-wlDEeHzkULcqqe/yZQn2hYmBT6I=
+rosie@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/rosie/-/rosie-2.1.0.tgz#0213a9d2b0401a2549cbce5f1cd914caffa22358"
+ integrity sha512-Dbzdc+prLXZuB/suRptDnBUY29SdGvND3bLg6cll8n7PNqzuyCxSlRfrkn8PqjS9n4QVsiM7RCvxCkKAkTQRjA==
rxjs@^6.3.3, rxjs@^6.6.3:
version "6.6.7"