Merge branch 'master' of github.com:Human-Connection/Human-Connection into 759-teaser-image-create-posts

This commit is contained in:
Matt Rider 2019-06-25 09:41:40 -03:00
commit 145f488556
66 changed files with 588 additions and 392 deletions

View File

@ -27,6 +27,7 @@
* [Kubernetes Dashboard](deployment/digital-ocean/dashboard/README.md)
* [HTTPS](deployment/digital-ocean/https/README.md)
* [Human Connection](deployment/human-connection/README.md)
* [Mailserver](deployment/human-connection/mailserver/README.md)
* [Volumes](deployment/volumes/README.md)
* [Neo4J Offline-Backups](deployment/volumes/neo4j-offline-backup/README.md)
* [Volume Snapshots](deployment/volumes/volume-snapshots/README.md)

View File

@ -52,11 +52,11 @@
"cheerio": "~1.0.0-rc.3",
"cors": "~2.8.5",
"cross-env": "~5.2.0",
"date-fns": "2.0.0-alpha.35",
"date-fns": "2.0.0-alpha.36",
"debug": "~4.1.1",
"dotenv": "~8.0.0",
"express": "~4.17.1",
"faker": "~4.1.0",
"faker": "Marak/faker.js#master",
"graphql": "~14.3.1",
"graphql-custom-directives": "~0.2.14",
"graphql-iso-date": "~3.6.1",
@ -70,7 +70,7 @@
"lodash": "~4.17.11",
"merge-graphql-schemas": "^1.5.8",
"neo4j-driver": "~1.7.4",
"neo4j-graphql-js": "git+https://github.com/Human-Connection/neo4j-graphql-js.git#temporary_fixes",
"neo4j-graphql-js": "^2.6.3",
"node-fetch": "~2.6.0",
"nodemailer": "^6.2.1",
"npm-run-all": "~4.1.5",
@ -97,8 +97,8 @@
"eslint": "~5.16.0",
"eslint-config-prettier": "~5.0.0",
"eslint-config-standard": "~12.0.0",
"eslint-plugin-import": "~2.17.3",
"eslint-plugin-jest": "~22.7.0",
"eslint-plugin-import": "~2.18.0",
"eslint-plugin-jest": "~22.7.1",
"eslint-plugin-node": "~9.1.0",
"eslint-plugin-prettier": "~3.1.0",
"eslint-plugin-promise": "~4.1.1",

View File

@ -38,22 +38,41 @@ export default {
if (!post) {
throw new UserInputError(NO_POST_ERR_MESSAGE)
}
const comment = await neo4jgraphql(object, params, context, resolveInfo, false)
const commentWithoutRelationships = await neo4jgraphql(
object,
params,
context,
resolveInfo,
false,
)
await session.run(
let transactionRes = await session.run(
`
MATCH (post:Post {id: $postId}), (comment:Comment {id: $commentId}), (author:User {id: $userId})
MERGE (post)<-[:COMMENTS]-(comment)<-[:WROTE]-(author)
RETURN post`,
RETURN comment, author`,
{
userId: context.user.id,
postId,
commentId: comment.id,
commentId: commentWithoutRelationships.id,
},
)
session.close()
return comment
const [commentWithAuthor] = transactionRes.records.map(record => {
return {
comment: record.get('comment'),
author: record.get('author'),
}
})
const { comment, author } = commentWithAuthor
const commentReturnedWithAuthor = {
...comment.properties,
author: author.properties,
}
session.close()
return commentReturnedWithAuthor
},
DeleteComment: async (object, params, context, resolveInfo) => {
const comment = await neo4jgraphql(object, params, context, resolveInfo, false)

View File

@ -13,7 +13,7 @@ export default function(params) {
faker.lorem.sentence(),
faker.lorem.sentence(),
].join('. '),
image = faker.image.image(),
image = faker.image.unsplash.imageUrl(),
visibility = 'public',
deleted = false,
} = params

View File

@ -214,21 +214,21 @@ import Factory from './factories'
'Hey <a class="mention" href="/profile/u3">@jenny-rostock</a>, here is another notification for you!'
await Promise.all([
asAdmin.create('Post', { id: 'p0' }),
asModerator.create('Post', { id: 'p1' }),
asAdmin.create('Post', { id: 'p0', image: faker.image.unsplash.food() }),
asModerator.create('Post', { id: 'p1', image: faker.image.unsplash.technology() }),
asUser.create('Post', { id: 'p2' }),
asTick.create('Post', { id: 'p3' }),
asTrick.create('Post', { id: 'p4' }),
asTrack.create('Post', { id: 'p5' }),
asAdmin.create('Post', { id: 'p6' }),
asAdmin.create('Post', { id: 'p6', image: faker.image.unsplash.buildings() }),
asModerator.create('Post', { id: 'p7', content: `${mention1} ${faker.lorem.paragraph()}` }),
asUser.create('Post', { id: 'p8' }),
asUser.create('Post', { id: 'p8', image: faker.image.unsplash.nature() }),
asTick.create('Post', { id: 'p9' }),
asTrick.create('Post', { id: 'p10' }),
asTrack.create('Post', { id: 'p11' }),
asTrack.create('Post', { id: 'p11', image: faker.image.unsplash.people() }),
asAdmin.create('Post', { id: 'p12', content: `${mention2} ${faker.lorem.paragraph()}` }),
asModerator.create('Post', { id: 'p13' }),
asUser.create('Post', { id: 'p14' }),
asUser.create('Post', { id: 'p14', image: faker.image.unsplash.objects() }),
asTick.create('Post', { id: 'p15' }),
])

View File

@ -2584,10 +2584,10 @@ data-urls@^1.0.0:
whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0"
date-fns@2.0.0-alpha.35:
version "2.0.0-alpha.35"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.35.tgz#185813cdc51b05cc1468a95116494bb3f3440934"
integrity sha512-dAY1ujqRtyUsa9mVeupyMzUluWo1d7kAMwyXTQHFImKYSHKvxDw/dipiY6fdswQOs8CwpGoiKysGfaaRP5r3bA==
date-fns@2.0.0-alpha.36:
version "2.0.0-alpha.36"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.36.tgz#e3d106fbc6a7247e951916229b948d47848d6102"
integrity sha512-VcO89v0bmgw1V6wVlg9iYiOfyS9/m8T/TDhLBlERkF9NxVEatVr3LcwronDtUGMwGAK6KxUzs2TrOx7TzANMkw==
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"
@ -3036,10 +3036,10 @@ eslint-plugin-es@^1.4.0:
eslint-utils "^1.3.0"
regexpp "^2.0.1"
eslint-plugin-import@~2.17.3:
version "2.17.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.17.3.tgz#00548b4434c18faebaba04b24ae6198f280de189"
integrity sha512-qeVf/UwXFJbeyLbxuY8RgqDyEKCkqV7YC+E5S5uOjAp4tOc8zj01JP3ucoBM8JcEqd1qRasJSg6LLlisirfy0Q==
eslint-plugin-import@~2.18.0:
version "2.18.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.0.tgz#7a5ba8d32622fb35eb9c8db195c2090bd18a3678"
integrity sha512-PZpAEC4gj/6DEMMoU2Df01C5c50r7zdGIN52Yfi7CvvWaYssG7Jt5R9nFG5gmqodxNOz9vQS87xk6Izdtpdrig==
dependencies:
array-includes "^3.0.3"
contains-path "^0.1.0"
@ -3053,10 +3053,10 @@ eslint-plugin-import@~2.17.3:
read-pkg-up "^2.0.0"
resolve "^1.11.0"
eslint-plugin-jest@~22.7.0:
version "22.7.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.7.0.tgz#a1d325bccb024b04f5354c56fe790baba54a454c"
integrity sha512-0U9nBd9V6+GKpM/KvRDcmMuPsewSsdM7NxCozgJkVAh8IrwHmQ0aw44/eYuVkhT8Fcdhsz0zYiyPtKg147eXMQ==
eslint-plugin-jest@~22.7.1:
version "22.7.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.7.1.tgz#5dcdf8f7a285f98040378220d6beca581f0ab2a1"
integrity sha512-CrT3AzA738neimv8G8iK2HCkrCwHnAJeeo7k5TEHK86VMItKl6zdJT/tHBDImfnVVAYsVs4Y6BUdBZQCCgfiyw==
eslint-plugin-node@~9.1.0:
version "9.1.0"
@ -3373,10 +3373,9 @@ extsprintf@^1.2.0:
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
faker@~4.1.0:
faker@Marak/faker.js#master:
version "4.1.0"
resolved "https://registry.yarnpkg.com/faker/-/faker-4.1.0.tgz#1e45bbbecc6774b3c195fad2835109c6d748cc3f"
integrity sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=
resolved "https://codeload.github.com/Marak/faker.js/tar.gz/10bfb9f467b0ac2b8912ffc15690b50ef3244f09"
fast-deep-equal@^2.0.1:
version "2.0.1"
@ -5614,9 +5613,10 @@ neo4j-driver@^1.7.3, neo4j-driver@~1.7.4:
text-encoding "^0.6.4"
uri-js "^4.2.1"
"neo4j-graphql-js@git+https://github.com/Human-Connection/neo4j-graphql-js.git#temporary_fixes":
version "2.6.1"
resolved "git+https://github.com/Human-Connection/neo4j-graphql-js.git#84d529b9ecbc5c284cce4f86238c6d19b192cf0f"
neo4j-graphql-js@^2.6.3:
version "2.6.3"
resolved "https://registry.yarnpkg.com/neo4j-graphql-js/-/neo4j-graphql-js-2.6.3.tgz#8f28c2479adda08c90abcc32a784587ef49b8b95"
integrity sha512-WZdEqQ8EL9GOIB1ZccbLk1BZz5Dqdbk9i8BDXqxhp1SOI07P9y2cZ244f2Uz4zyES9AVXGmv+861N5xLhrSL2A==
dependencies:
graphql "^14.2.1"
graphql-auth-directives "^2.1.0"

View File

@ -1,347 +1,361 @@
import { Given, When, Then } from 'cypress-cucumber-preprocessor/steps'
import { getLangByName } from '../../support/helpers'
import { Given, When, Then } from "cypress-cucumber-preprocessor/steps";
import { getLangByName } from "../../support/helpers";
/* global cy */
let lastPost = {}
let lastPost = {};
let loginCredentials = {
email: 'peterpan@example.org',
password: '1234'
}
email: "peterpan@example.org",
password: "1234"
};
const narratorParams = {
name: 'Peter Pan',
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/nerrsoft/128.jpg',
name: "Peter Pan",
avatar: "https://s3.amazonaws.com/uifaces/faces/twitter/nerrsoft/128.jpg",
...loginCredentials
}
};
Given('I am logged in', () => {
cy.login(loginCredentials)
})
Given("I am logged in", () => {
cy.login(loginCredentials);
});
Given('we have a selection of tags and categories as well as posts', () => {
Given("we have a selection of tags and categories as well as posts", () => {
cy.factory()
.authenticateAs(loginCredentials)
.create('Category', {
id: 'cat1',
name: 'Just For Fun',
slug: 'justforfun',
icon: 'smile'
.create("Category", {
id: "cat1",
name: "Just For Fun",
slug: "justforfun",
icon: "smile"
})
.create('Category', {
id: 'cat2',
name: 'Happyness & Values',
slug: 'happyness-values',
icon: 'heart-o'
.create("Category", {
id: "cat2",
name: "Happyness & Values",
slug: "happyness-values",
icon: "heart-o"
})
.create('Category', {
id: 'cat3',
name: 'Health & Wellbeing',
slug: 'health-wellbeing',
icon: 'medkit'
.create("Category", {
id: "cat3",
name: "Health & Wellbeing",
slug: "health-wellbeing",
icon: "medkit"
})
.create('Tag', { id: 't1', name: 'Ecology' })
.create('Tag', { id: 't2', name: 'Nature' })
.create('Tag', { id: 't3', name: 'Democracy' })
.create("Tag", { id: "t1", name: "Ecology" })
.create("Tag", { id: "t2", name: "Nature" })
.create("Tag", { id: "t3", name: "Democracy" });
const someAuthor = {
id: 'authorId',
email: 'author@example.org',
password: '1234'
}
id: "authorId",
email: "author@example.org",
password: "1234"
};
const yetAnotherAuthor = {
id: 'yetAnotherAuthor',
email: 'yet-another-author@example.org',
password: '1234'
}
id: "yetAnotherAuthor",
email: "yet-another-author@example.org",
password: "1234"
};
cy.factory()
.create('User', someAuthor)
.create("User", someAuthor)
.authenticateAs(someAuthor)
.create('Post', { id: 'p0' })
.create('Post', { id: 'p1' })
.create("Post", { id: "p0" })
.create("Post", { id: "p1" });
cy.factory()
.create('User', yetAnotherAuthor)
.create("User", yetAnotherAuthor)
.authenticateAs(yetAnotherAuthor)
.create('Post', { id: 'p2' })
.create("Post", { id: "p2" });
cy.factory()
.authenticateAs(loginCredentials)
.create('Post', { id: 'p3' })
.relate('Post', 'Categories', { from: 'p0', to: 'cat1' })
.relate('Post', 'Categories', { from: 'p1', to: 'cat2' })
.relate('Post', 'Categories', { from: 'p2', to: 'cat1' })
.relate('Post', 'Tags', { from: 'p0', to: 't1' })
.relate('Post', 'Tags', { from: 'p0', to: 't2' })
.relate('Post', 'Tags', { from: 'p0', to: 't3' })
.relate('Post', 'Tags', { from: 'p1', to: 't2' })
.relate('Post', 'Tags', { from: 'p1', to: 't3' })
.relate('Post', 'Tags', { from: 'p2', to: 't2' })
.relate('Post', 'Tags', { from: 'p2', to: 't3' })
.relate('Post', 'Tags', { from: 'p3', to: 't3' })
})
.create("Post", { id: "p3" })
.relate("Post", "Categories", { from: "p0", to: "cat1" })
.relate("Post", "Categories", { from: "p1", to: "cat2" })
.relate("Post", "Categories", { from: "p2", to: "cat1" })
.relate("Post", "Tags", { from: "p0", to: "t1" })
.relate("Post", "Tags", { from: "p0", to: "t2" })
.relate("Post", "Tags", { from: "p0", to: "t3" })
.relate("Post", "Tags", { from: "p1", to: "t2" })
.relate("Post", "Tags", { from: "p1", to: "t3" })
.relate("Post", "Tags", { from: "p2", to: "t2" })
.relate("Post", "Tags", { from: "p2", to: "t3" })
.relate("Post", "Tags", { from: "p3", to: "t3" });
});
Given('we have the following user accounts:', table => {
Given("we have the following user accounts:", table => {
table.hashes().forEach(params => {
cy.factory().create('User', params)
})
})
cy.factory().create("User", params);
});
});
Given('I have a user account', () => {
cy.factory().create('User', narratorParams)
})
Given("I have a user account", () => {
cy.factory().create("User", narratorParams);
});
Given('my user account has the role {string}', role => {
cy.factory().create('User', {
Given("my user account has the role {string}", role => {
cy.factory().create("User", {
role,
...loginCredentials
})
})
});
});
When('I log out', cy.logout)
When("I log out", cy.logout);
When('I visit {string}', page => {
cy.openPage(page)
})
When("I visit {string}", page => {
cy.openPage(page);
});
When('I visit the {string} page', page => {
cy.openPage(page)
})
When("I visit the {string} page", page => {
cy.openPage(page);
});
Given('I am on the {string} page', page => {
cy.openPage(page)
})
Given("I am on the {string} page", page => {
cy.openPage(page);
});
When('I fill in my email and password combination and click submit', () => {
cy.login(loginCredentials)
})
When("I fill in my email and password combination and click submit", () => {
cy.login(loginCredentials);
});
When(/(?:when )?I refresh the page/, () => {
cy.reload()
})
cy.reload();
});
When('I log out through the menu in the top right corner', () => {
cy.get('.avatar-menu').click()
cy.get('.avatar-menu-popover')
When("I log out through the menu in the top right corner", () => {
cy.get(".avatar-menu").click();
cy.get(".avatar-menu-popover")
.find('a[href="/logout"]')
.click()
})
.click();
});
Then('I can see my name {string} in the dropdown menu', () => {
cy.get('.avatar-menu-popover').should('contain', narratorParams.name)
})
Then("I can see my name {string} in the dropdown menu", () => {
cy.get(".avatar-menu-popover").should("contain", narratorParams.name);
});
Then('I see the login screen again', () => {
cy.location('pathname').should('contain', '/login')
})
Then("I see the login screen again", () => {
cy.location("pathname").should("contain", "/login");
});
Then('I can click on my profile picture in the top right corner', () => {
cy.get('.avatar-menu').click()
cy.get('.avatar-menu-popover')
})
Then("I can click on my profile picture in the top right corner", () => {
cy.get(".avatar-menu").click();
cy.get(".avatar-menu-popover");
});
Then('I am still logged in', () => {
cy.get('.avatar-menu').click()
cy.get('.avatar-menu-popover').contains(narratorParams.name)
})
Then("I am still logged in", () => {
cy.get(".avatar-menu").click();
cy.get(".avatar-menu-popover").contains(narratorParams.name);
});
When('I select {string} in the language menu', name => {
cy.switchLanguage(name, true)
})
Given('I previously switched the language to {string}', name => {
cy.switchLanguage(name, true)
})
Then('the whole user interface appears in {string}', name => {
const lang = getLangByName(name)
cy.get(`html[lang=${lang.code}]`)
cy.getCookie('locale').should('have.property', 'value', lang.code)
})
Then('I see a button with the label {string}', label => {
cy.contains('button', label)
})
When("I select {string} in the language menu", name => {
cy.switchLanguage(name, true);
});
Given("I previously switched the language to {string}", name => {
cy.switchLanguage(name, true);
});
Then("the whole user interface appears in {string}", name => {
const lang = getLangByName(name);
cy.get(`html[lang=${lang.code}]`);
cy.getCookie("locale").should("have.property", "value", lang.code);
});
Then("I see a button with the label {string}", label => {
cy.contains("button", label);
});
When(`I click on {string}`, linkOrButton => {
cy.contains(linkOrButton).click()
})
cy.contains(linkOrButton).click();
});
When(`I click on the menu item {string}`, linkOrButton => {
cy.contains('.ds-menu-item', linkOrButton).click()
})
cy.contains(".ds-menu-item", linkOrButton).click();
});
When('I press {string}', label => {
cy.contains(label).click()
})
When("I press {string}", label => {
cy.contains(label).click();
});
Given('we have the following posts in our database:', table => {
Given("we have the following posts in our database:", table => {
table.hashes().forEach(({ Author, ...postAttributes }) => {
const userAttributes = {
name: Author,
email: `${Author}@example.org`,
password: '1234'
}
postAttributes.deleted = Boolean(postAttributes.deleted)
const disabled = Boolean(postAttributes.disabled)
password: "1234"
};
postAttributes.deleted = Boolean(postAttributes.deleted);
const disabled = Boolean(postAttributes.disabled);
cy.factory()
.create('User', userAttributes)
.create("User", userAttributes)
.authenticateAs(userAttributes)
.create('Post', postAttributes)
.create("Post", postAttributes);
if (disabled) {
const moderatorParams = {
email: 'moderator@example.org',
role: 'moderator',
password: '1234'
}
email: "moderator@example.org",
role: "moderator",
password: "1234"
};
cy.factory()
.create('User', moderatorParams)
.create("User", moderatorParams)
.authenticateAs(moderatorParams)
.mutate('mutation($id: ID!) { disable(id: $id) }', postAttributes)
.mutate("mutation($id: ID!) { disable(id: $id) }", postAttributes);
}
})
})
});
});
Then('I see a success message:', message => {
cy.contains(message)
})
Then("I see a success message:", message => {
cy.contains(message);
});
When('I click on the avatar menu in the top right corner', () => {
cy.get('.avatar-menu').click()
})
When("I click on the avatar menu in the top right corner", () => {
cy.get(".avatar-menu").click();
});
When(
'I click on the big plus icon in the bottom right corner to create post',
"I click on the big plus icon in the bottom right corner to create post",
() => {
cy.get('.post-add-button').click()
cy.get(".post-add-button").click();
}
)
);
Given('I previously created a post', () => {
Given("I previously created a post", () => {
lastPost.title = "previously created post";
lastPost.content = "with some content";
cy.factory()
.authenticateAs(loginCredentials)
.create('Post', lastPost)
})
.create("Post", lastPost);
});
When('I choose {string} as the title of the post', title => {
lastPost.title = title.replace('\n', ' ')
cy.get('input[name="title"]').type(lastPost.title)
})
When("I choose {string} as the title of the post", title => {
lastPost.title = title.replace("\n", " ");
cy.get('input[name="title"]').type(lastPost.title);
});
When('I type in the following text:', text => {
lastPost.content = text.replace('\n', ' ')
cy.get('.ProseMirror').type(lastPost.content)
})
When("I type in the following text:", text => {
lastPost.content = text.replace("\n", " ");
cy.get(".ProseMirror").type(lastPost.content);
});
Then('the post shows up on the landing page at position {int}', index => {
cy.openPage('landing')
const selector = `.post-card:nth-child(${index}) > .ds-card-content`
cy.get(selector).should('contain', lastPost.title)
cy.get(selector).should('contain', lastPost.content)
})
Then("the post shows up on the landing page at position {int}", index => {
cy.openPage("landing");
const selector = `.post-card:nth-child(${index}) > .ds-card-content`;
cy.get(selector).should("contain", lastPost.title);
cy.get(selector).should("contain", lastPost.content);
});
Then('I get redirected to {string}', route => {
cy.location('pathname').should('contain', route.replace('...', ''))
})
Then("I get redirected to {string}", route => {
cy.location("pathname").should("contain", route.replace("...", ""));
});
Then('the post was saved successfully', () => {
cy.get('.ds-card-content > .ds-heading').should('contain', lastPost.title)
cy.get('.content').should('contain', lastPost.content)
})
Then("the post was saved successfully", () => {
cy.get(".ds-card-content > .ds-heading").should("contain", lastPost.title);
cy.get(".content").should("contain", lastPost.content);
});
Then(/^I should see only ([0-9]+) posts? on the landing page/, postCount => {
cy.get('.post-card').should('have.length', postCount)
})
cy.get(".post-card").should("have.length", postCount);
});
Then('the first post on the landing page has the title:', title => {
cy.get('.post-card:first').should('contain', title)
})
Then("the first post on the landing page has the title:", title => {
cy.get(".post-card:first").should("contain", title);
});
Then(
'the page {string} returns a 404 error with a message:',
"the page {string} returns a 404 error with a message:",
(route, message) => {
// TODO: how can we check HTTP codes with cypress?
cy.visit(route, { failOnStatusCode: false })
cy.get('.error').should('contain', message)
cy.visit(route, { failOnStatusCode: false });
cy.get(".error").should("contain", message);
}
)
);
Given('my user account has the following login credentials:', table => {
loginCredentials = table.hashes()[0]
cy.debug()
cy.factory().create('User', loginCredentials)
})
Given("my user account has the following login credentials:", table => {
loginCredentials = table.hashes()[0];
cy.debug();
cy.factory().create("User", loginCredentials);
});
When('I fill the password form with:', table => {
table = table.rowsHash()
cy.get('input[id=oldPassword]')
.type(table['Your old password'])
.get('input[id=newPassword]')
.type(table['Your new passsword'])
.get('input[id=confirmPassword]')
.type(table['Confirm new password'])
})
When("I fill the password form with:", table => {
table = table.rowsHash();
cy.get("input[id=oldPassword]")
.type(table["Your old password"])
.get("input[id=newPassword]")
.type(table["Your new passsword"])
.get("input[id=confirmPassword]")
.type(table["Confirm new password"]);
});
When('submit the form', () => {
cy.get('form').submit()
})
When("submit the form", () => {
cy.get("form").submit();
});
Then('I cannot login anymore with password {string}', password => {
cy.reload()
const { email } = loginCredentials
cy.visit(`/login`)
cy.get('input[name=email]')
.trigger('focus')
.type(email)
cy.get('input[name=password]')
.trigger('focus')
.type(password)
cy.get('button[name=submit]')
.as('submitButton')
.click()
cy.get('.iziToast-wrapper').should('contain', 'Incorrect email address or password.')
})
Then("I cannot login anymore with password {string}", password => {
cy.reload();
const { email } = loginCredentials;
cy.visit(`/login`);
cy.get("input[name=email]")
.trigger("focus")
.type(email);
cy.get("input[name=password]")
.trigger("focus")
.type(password);
cy.get("button[name=submit]")
.as("submitButton")
.click();
cy.get(".iziToast-wrapper").should(
"contain",
"Incorrect email address or password."
);
});
Then('I can login successfully with password {string}', password => {
cy.reload()
Then("I can login successfully with password {string}", password => {
cy.reload();
cy.login({
...loginCredentials,
...{password}
})
cy.get('.iziToast-wrapper').should('contain', "You are logged in!")
})
...{ password }
});
cy.get(".iziToast-wrapper").should("contain", "You are logged in!");
});
When('I log in with the following credentials:', table => {
const { email, password } = table.hashes()[0]
cy.login({ email, password })
})
When("I log in with the following credentials:", table => {
const { email, password } = table.hashes()[0];
cy.login({ email, password });
});
When('open the notification menu and click on the first item', () => {
cy.get('.notifications-menu').click()
cy.get('.notification-mention-post').first().click()
})
When("open the notification menu and click on the first item", () => {
cy.get(".notifications-menu").click();
cy.get(".notification-mention-post")
.first()
.click();
});
Then('see {int} unread notifications in the top menu', count => {
cy.get('.notifications-menu').should('contain', count)
})
Then("see {int} unread notifications in the top menu", count => {
cy.get(".notifications-menu").should("contain", count);
});
Then('I get to the post page of {string}', path => {
path = path.replace('...', '')
cy.url().should('contain', '/post/')
cy.url().should('contain', path)
})
Then("I get to the post page of {string}", path => {
path = path.replace("...", "");
cy.url().should("contain", "/post/");
cy.url().should("contain", path);
});
When('I start to write a new post with the title {string} beginning with:', (title, intro) => {
cy.get('.post-add-button').click()
cy.get('input[name="title"]').type(title)
cy.get('.ProseMirror').type(intro)
})
When(
"I start to write a new post with the title {string} beginning with:",
(title, intro) => {
cy.get(".post-add-button").click();
cy.get('input[name="title"]').type(title);
cy.get(".ProseMirror").type(intro);
}
);
When('mention {string} in the text', (mention) => {
cy.get('.ProseMirror').type(' @')
cy.get('.suggestion-list__item').contains(mention).click()
cy.debug()
})
When("mention {string} in the text", mention => {
cy.get(".ProseMirror").type(" @");
cy.get(".suggestion-list__item")
.contains(mention)
.click();
cy.debug();
});
Then('the notification gets marked as read', () => {
cy.get('.notification').first().should('have.class', 'read')
})
Then("the notification gets marked as read", () => {
cy.get(".notification")
.first()
.should("have.class", "read");
});
Then('there are no notifications in the top menu', () => {
cy.get('.notifications-menu').should('contain', '0')
})
Then("there are no notifications in the top menu", () => {
cy.get(".notifications-menu").should("contain", "0");
});

View File

@ -12,10 +12,10 @@ Feature: Create a post
When I click on the big plus icon in the bottom right corner to create post
And I choose "My first post" as the title of the post
And I type in the following text:
"""
Human Connection is a free and open-source social network
for active citizenship.
"""
"""
Human Connection is a free and open-source social network
for active citizenship.
"""
And I click on "Save"
Then I get redirected to ".../my-first-post"
And the post was saved successfully

View File

@ -10,6 +10,7 @@ metadata:
spec:
tls:
- hosts:
# - nitro-mailserver.human-connection.org
- nitro-staging.human-connection.org
secretName: tls
rules:
@ -20,3 +21,10 @@ spec:
backend:
serviceName: nitro-web
servicePort: 3000
# - host: nitro-mailserver.human-connection.org
# http:
# paths:
# - path: /
# backend:
# serviceName: mailserver
# servicePort: 80

View File

@ -0,0 +1,18 @@
# Development Mail Server
You can deploy a fake smtp server which captures all send mails and displays
them in a web interface. The [sample configuration](../templates/configmap.template.yml)
is assuming such a dummy server in the `SMTP_HOST` configuration and points to
a cluster-internal SMTP server.
To deploy the SMTP server just uncomment the relevant code in the
[ingress server configuration](../../https/templates/ingress.template.yaml) and
run the following:
```bash
# in folder deployment/human-connection
kubectl apply -f mailserver/
```
You might need to refresh the TLS secret to enable HTTPS on the publicly
available web interface.

View File

@ -0,0 +1,34 @@
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: mailserver
namespace: human-connection
spec:
replicas: 1
minReadySeconds: 15
progressDeadlineSeconds: 60
selector:
matchLabels:
human-connection.org/selector: deployment-human-connection-mailserver
template:
metadata:
labels:
human-connection.org/selector: deployment-human-connection-mailserver
name: "mailserver"
spec:
containers:
- name: mailserver
image: djfarrelly/maildev
imagePullPolicy: Always
ports:
- containerPort: 80
- containerPort: 25
envFrom:
- configMapRef:
name: configmap
- secretRef:
name: human-connection
restartPolicy: Always
terminationGracePeriodSeconds: 30
status: {}

View File

@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
name: mailserver
namespace: human-connection
labels:
human-connection.org/selector: deployment-human-connection-mailserver
spec:
ports:
- name: web
port: 80
targetPort: 80
- name: smtp
port: 25
targetPort: 25
selector:
human-connection.org/selector: deployment-human-connection-mailserver

View File

@ -2,6 +2,10 @@
apiVersion: v1
kind: ConfigMap
data:
SMTP_HOST: "mailserver.human-connection"
SMTP_PORT: "25"
SMTP_USERNAME: ""
SMTP_PASSWORD: ""
GRAPHQL_PORT: "4000"
GRAPHQL_URI: "http://nitro-backend.human-connection:4000"
MOCKS: "false"

View File

@ -8,11 +8,18 @@ set +o allexport
# Export collection function defintion
function export_collection () {
"${EXPORT_MONGOEXPORT_BIN}" --db ${MONGODB_DATABASE} --host localhost -d ${MONGODB_DATABASE} --port 27018 --username ${MONGODB_USERNAME} --password ${MONGODB_PASSWORD} --authenticationDatabase ${MONGODB_AUTH_DB} --collection $1 --collection $1 --out "${EXPORT_PATH}$1.json"
"${EXPORT_MONGOEXPORT_BIN}" --db ${MONGODB_DATABASE} --host localhost -d ${MONGODB_DATABASE} --port 27018 --username ${MONGODB_USERNAME} --password ${MONGODB_PASSWORD} --authenticationDatabase ${MONGODB_AUTH_DB} --collection $1 --out "${EXPORT_PATH}$1.json"
mkdir -p ${EXPORT_PATH}splits/$1/
split -l ${MONGO_EXPORT_SPLIT_SIZE} -a 3 ${EXPORT_PATH}$1.json ${EXPORT_PATH}splits/$1/
}
# Export collection with query function defintion
function export_collection_query () {
"${EXPORT_MONGOEXPORT_BIN}" --db ${MONGODB_DATABASE} --host localhost -d ${MONGODB_DATABASE} --port 27018 --username ${MONGODB_USERNAME} --password ${MONGODB_PASSWORD} --authenticationDatabase ${MONGODB_AUTH_DB} --collection $1 --out "${EXPORT_PATH}$1_$3.json" --query "$2"
mkdir -p ${EXPORT_PATH}splits/$1_$3/
split -l ${MONGO_EXPORT_SPLIT_SIZE} -a 3 ${EXPORT_PATH}$1_$3.json ${EXPORT_PATH}splits/$1_$3/
}
# Delete old export & ensure directory
rm -rf ${EXPORT_PATH}*
mkdir -p ${EXPORT_PATH}
@ -24,9 +31,12 @@ ssh -4 -M -S my-ctrl-socket -fnNT -L 27018:localhost:27017 -l ${SSH_USERNAME} ${
export_collection "badges"
export_collection "categories"
export_collection "comments"
export_collection "contributions"
export_collection_query "contributions" "{'type': 'DELETED'}" "DELETED"
export_collection_query "contributions" "{'type': 'post'}" "post"
export_collection_query "contributions" "{'type': 'cando'}" "cando"
export_collection "emotions"
export_collection "follows"
export_collection_query "follows" "{'foreignService': 'organizations'}" "organizations"
export_collection_query "follows" "{'foreignService': 'users'}" "users"
export_collection "invites"
export_collection "notifications"
export_collection "organizations"

View File

@ -109,8 +109,8 @@
}
}
},
[?] deleted: {
[X] type: Boolean,
[?] deleted: { // THis field is not always present in the alpha-data
[?] type: Boolean,
[ ] default: false, // Default value is missing in Nitro
[-] index: true
},
@ -137,7 +137,7 @@ p.contentExcerpt = post.contentExcerpt,
p.visibility = toLower(post.visibility),
p.createdAt = post.createdAt.`$date`,
p.updatedAt = post.updatedAt.`$date`,
p.deleted = post.deleted,
p.deleted = COALESCE(post.deleted,false),
p.disabled = NOT post.isEnabled
WITH p, post
MATCH (u:User {id: post.userId})

View File

@ -9,10 +9,10 @@ set +o allexport
# Delete collection function defintion
function delete_collection () {
# Delete from Database
echo "Delete $1"
"${IMPORT_CYPHERSHELL_BIN}" < $(dirname "$0")/$1_delete.cql > /dev/null
echo "Delete $2"
"${IMPORT_CYPHERSHELL_BIN}" < $(dirname "$0")/$1/delete.cql > /dev/null
# Delete index file
rm -f "${IMPORT_PATH}splits/$1.index"
rm -f "${IMPORT_PATH}splits/$2.index"
}
# Import collection function defintion
@ -34,7 +34,7 @@ function import_collection () {
# calculate the path of the chunk
export IMPORT_CHUNK_PATH_CQL_FILE="${IMPORT_CHUNK_PATH_CQL}$1/${CHUNK_FILE_NAME}"
# load the neo4j command and replace file variable with actual path
NEO4J_COMMAND="$(envsubst '${IMPORT_CHUNK_PATH_CQL_FILE}' < $(dirname "$0")/$1.cql)"
NEO4J_COMMAND="$(envsubst '${IMPORT_CHUNK_PATH_CQL_FILE}' < $(dirname "$0")/$2)"
# run the import of the chunk
echo "Import $1 ${CHUNK_FILE_NAME} (${chunk})"
echo "${NEO4J_COMMAND}" | "${IMPORT_CYPHERSHELL_BIN}" > /dev/null
@ -52,13 +52,14 @@ SECONDS=0
# Delete all Neo4J Database content
echo "Deleting Database Contents"
delete_collection "badges"
delete_collection "categories"
delete_collection "users"
delete_collection "follows"
delete_collection "contributions"
delete_collection "shouts"
delete_collection "comments"
delete_collection "badges" "badges"
delete_collection "categories" "categories"
delete_collection "users" "users"
delete_collection "follows" "follows_users"
delete_collection "contributions" "contributions_post"
delete_collection "contributions" "contributions_cando"
delete_collection "shouts" "shouts"
delete_collection "comments" "comments"
#delete_collection "emotions"
#delete_collection "invites"
@ -75,26 +76,33 @@ echo "DONE"
# Import Data
echo "Start Importing Data"
import_collection "badges"
import_collection "categories"
import_collection "users"
import_collection "follows"
import_collection "contributions"
import_collection "shouts"
import_collection "comments"
import_collection "badges" "badges/badges.cql"
import_collection "categories" "categories/categories.cql"
import_collection "users" "users/users.cql"
import_collection "follows_users" "follows/follows.cql"
#import_collection "follows_organizations" "follows/follows.cql"
import_collection "contributions_post" "contributions/contributions.cql"
import_collection "contributions_cando" "contributions/contributions.cql"
#import_collection "contributions_DELETED" "contributions/contributions.cql"
import_collection "shouts" "shouts/shouts.cql"
import_collection "comments" "comments/comments.cql"
# import_collection "emotions"
# import_collection "invites"
# import_collection "notifications"
# import_collection "organizations"
# import_collection "pages"
# import_collection "projects"
# import_collection "settings"
# import_collection "status"
# import_collection "systemnotifications"
# import_collection "userscandos"
# import_collection "usersettings"
# does only contain dummy data
# import_collection "projects"
# does only contain alpha specifc data
# import_collection "status
# import_collection "settings""
echo "DONE"
echo "Time elapsed: $SECONDS seconds"

View File

@ -26,9 +26,9 @@
"cypress-file-upload": "^3.1.4",
"cypress-plugin-retries": "^1.2.2",
"dotenv": "^8.0.0",
"faker": "^4.1.0",
"faker": "Marak/faker.js#master",
"graphql-request": "^1.8.2",
"neo4j-driver": "^1.7.5",
"npm-run-all": "^4.1.5"
}
}
}

View File

@ -53,7 +53,7 @@ describe('Avatar.vue', () => {
beforeEach(() => {
propsData = {
user: {
avatar: 'http://lorempixel.com/640/480/animals',
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/sawalazar/128.jpg',
},
}
})
@ -64,7 +64,7 @@ describe('Avatar.vue', () => {
Wrapper()
.find('img')
.attributes('src'),
).toBe('http://lorempixel.com/640/480/animals')
).toBe('https://s3.amazonaws.com/uifaces/faces/twitter/sawalazar/128.jpg')
})
})
})

View File

@ -2,6 +2,7 @@ import { config, mount, createLocalVue } from '@vue/test-utils'
import ContributionForm from './index.vue'
import Styleguide from '@human-connection/styleguide'
import Vuex from 'vuex'
import PostMutations from '~/graphql/PostMutations.js'
const localVue = createLocalVue()
@ -17,9 +18,9 @@ describe('ContributionForm.vue', () => {
let deutschOption
let cancelBtn
let mocks
let propsData
const postTitle = 'this is a title for a post'
const postContent = 'this is a post'
const computed = { locale: () => 'English' }
beforeEach(() => {
mocks = {
@ -52,6 +53,7 @@ describe('ContributionForm.vue', () => {
push: jest.fn(),
},
}
propsData = {}
})
describe('mount', () => {
@ -64,7 +66,7 @@ describe('ContributionForm.vue', () => {
getters,
})
const Wrapper = () => {
return mount(ContributionForm, { mocks, localVue, computed, store })
return mount(ContributionForm, { mocks, localVue, store, propsData })
}
beforeEach(() => {
@ -73,16 +75,22 @@ describe('ContributionForm.vue', () => {
})
describe('CreatePost', () => {
describe('language placeholder', () => {
it("displays the name that corresponds with the user's location code", () => {
expect(wrapper.find('.ds-select-placeholder').text()).toEqual('English')
})
})
describe('invalid form submission', () => {
it('title required for form submission', async () => {
postTitleInput = wrapper.find('.ds-input')
postTitleInput.setValue('this is a title for a post')
postTitleInput.setValue(postTitle)
await wrapper.find('form').trigger('submit')
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
it('content required for form submission', async () => {
wrapper.vm.updateEditorContent('this is a post')
wrapper.vm.updateEditorContent(postContent)
await wrapper.find('form').trigger('submit')
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
@ -95,6 +103,7 @@ describe('ContributionForm.vue', () => {
}
beforeEach(async () => {
expectedParams = {
mutation: PostMutations().CreatePost,
variables: {
title: postTitle,
content: postContent,
@ -104,8 +113,8 @@ describe('ContributionForm.vue', () => {
},
}
postTitleInput = wrapper.find('.ds-input')
postTitleInput.setValue('this is a title for a post')
wrapper.vm.updateEditorContent('this is a post')
postTitleInput.setValue(postTitle)
wrapper.vm.updateEditorContent(postContent)
await wrapper.find('form').trigger('submit')
})
@ -154,8 +163,8 @@ describe('ContributionForm.vue', () => {
jest.useFakeTimers()
wrapper = Wrapper()
postTitleInput = wrapper.find('.ds-input')
postTitleInput.setValue('this is a title for a post')
wrapper.vm.updateEditorContent('this is a post')
postTitleInput.setValue(postTitle)
wrapper.vm.updateEditorContent(postContent)
// second submission causes mutation to reject
await wrapper.find('form').trigger('submit')
})
@ -186,5 +195,57 @@ describe('ContributionForm.vue', () => {
})
})
})
describe('UpdatePost', () => {
beforeEach(() => {
propsData = {
contribution: {
id: 'p1456',
slug: 'dies-ist-ein-post',
title: 'dies ist ein Post',
content: 'auf Deutsch geschrieben',
language: 'de',
},
}
wrapper = Wrapper()
})
it('sets id equal to contribution id', () => {
expect(wrapper.vm.id).toEqual(propsData.contribution.id)
})
it('sets slug equal to contribution slug', () => {
expect(wrapper.vm.slug).toEqual(propsData.contribution.slug)
})
it('sets title equal to contribution title', () => {
expect(wrapper.vm.form.title).toEqual(propsData.contribution.title)
})
it('sets content equal to contribution content', () => {
expect(wrapper.vm.form.content).toEqual(propsData.contribution.content)
})
it('sets language equal to contribution language', () => {
expect(wrapper.vm.form.language).toEqual({ value: propsData.contribution.language })
})
it('calls the UpdatePost apollo mutation', async () => {
expectedParams = {
mutation: PostMutations().UpdatePost,
variables: {
title: postTitle,
content: postContent,
language: propsData.contribution.language,
id: propsData.contribution.id,
},
}
postTitleInput = wrapper.find('.ds-input')
postTitleInput.setValue(postTitle)
wrapper.vm.updateEditorContent(postContent)
await wrapper.find('form').trigger('submit')
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
})
})
})
})

View File

@ -127,8 +127,8 @@ export default {
this.slug = contribution.slug
this.form.content = contribution.content
this.form.title = contribution.title
this.form.language = this.locale
this.form.teaserImage = contribution.imageUpload
this.form.language = { value: contribution.language }
},
},
error() {

View File

@ -25,6 +25,8 @@
<script>
import gql from 'graphql-tag'
import HcEditor from '~/components/Editor'
import PostCommentsQuery from '~/graphql/PostCommentsQuery.js'
import CommentMutations from '~/graphql/CommentMutations.js'
export default {
components: {
@ -62,22 +64,22 @@ export default {
this.disabled = true
this.$apollo
.mutate({
mutation: gql`
mutation($postId: ID, $content: String!) {
CreateComment(postId: $postId, content: $content) {
id
content
}
}
`,
mutation: CommentMutations().CreateComment,
variables: {
postId: this.post.id,
content: this.form.content,
},
update: (store, { data: { CreateComment } }) => {
const data = store.readQuery({
query: PostCommentsQuery(this.$i18n),
variables: { slug: this.post.slug },
})
data.Post[0].comments.push(CreateComment)
store.writeQuery({ query: PostCommentsQuery(this.$i18n), data })
},
})
.then(res => {
this.loading = false
this.$root.$emit('refetchPostComments')
this.clear()
this.$toast.success(this.$t('post.comment.submitted'))
this.disabled = false
@ -94,6 +96,8 @@ export default {
User(orderBy: slug_asc) {
id
slug
name
avatar
}
}`)
},

View File

@ -1,4 +1,4 @@
import { mount, createLocalVue, createWrapper } from '@vue/test-utils'
import { mount, createLocalVue } from '@vue/test-utils'
import CommentForm from './index.vue'
import Styleguide from '@human-connection/styleguide'
import Vuex from 'vuex'
@ -81,11 +81,6 @@ describe('CommentForm.vue', () => {
expect(cancelMethodSpy).toHaveBeenCalledTimes(1)
})
it('emits a method call with the returned comment', () => {
const rootWrapper = createWrapper(wrapper.vm.$root)
expect(rootWrapper.emitted().refetchPostComments.length).toEqual(1)
})
describe('mutation fails', () => {
it('shows the error toaster', async () => {
await wrapper.find('form').trigger('submit')

View File

@ -88,10 +88,5 @@ describe('CommentList.vue', () => {
it('displays comments when there are comments to display', () => {
expect(wrapper.find('div.comments').text()).toEqual('this is a comment')
})
it("refetches a post's comments from the backend", () => {
wrapper.vm.refetchPostComments()
expect(mocks.$apollo.queries.Post.refetch).toHaveBeenCalledTimes(1)
})
})
})

View File

@ -30,6 +30,7 @@
<script>
import Comment from '~/components/Comment.vue'
import HcEmpty from '~/components/Empty.vue'
import PostCommentsQuery from '~/graphql/PostCommentsQuery.js'
export default {
components: {
@ -49,25 +50,10 @@ export default {
this.comments = post[0].comments || []
},
},
mounted() {
this.$root.$on('refetchPostComments', () => {
this.refetchPostComments()
})
},
beforeDestroy() {
this.$root.$off('refetchPostComments')
},
methods: {
refetchPostComments() {
if (this.$apollo.queries.Post) {
this.$apollo.queries.Post.refetch()
}
},
},
apollo: {
Post: {
query() {
return require('~/graphql/PostCommentsQuery.js').default(this)
return PostCommentsQuery(this.$i18n)
},
variables() {
return {

View File

@ -0,0 +1,23 @@
import gql from 'graphql-tag'
export default () => {
return {
CreateComment: gql`
mutation($postId: ID, $content: String!) {
CreateComment(postId: $postId, content: $content) {
id
contentExcerpt
author {
id
slug
name
avatar
}
createdAt
deleted
disabled
}
}
`,
}
}

View File

@ -1,7 +1,7 @@
import gql from 'graphql-tag'
export default app => {
const lang = app.$i18n.locale().toUpperCase()
export default i18n => {
const lang = i18n.locale().toUpperCase()
return gql(`
query Post($slug: String!) {
Post(slug: $slug) {

View File

@ -60,7 +60,7 @@
"apollo-client": "~2.6.3",
"cookie-universal-nuxt": "~2.0.16",
"cross-env": "~5.2.0",
"date-fns": "2.0.0-alpha.35",
"date-fns": "2.0.0-alpha.36",
"express": "~4.17.1",
"graphql": "~14.3.1",
"jsonwebtoken": "~8.5.1",
@ -94,13 +94,13 @@
"eslint-config-prettier": "~5.0.0",
"eslint-config-standard": "~12.0.0",
"eslint-loader": "~2.1.2",
"eslint-plugin-import": "~2.17.3",
"eslint-plugin-jest": "~22.7.0",
"eslint-plugin-import": "~2.18.0",
"eslint-plugin-jest": "~22.7.1",
"eslint-plugin-node": "~9.1.0",
"eslint-plugin-prettier": "~3.1.0",
"eslint-plugin-promise": "~4.1.1",
"eslint-plugin-standard": "~4.0.0",
"eslint-plugin-vue": "~5.2.2",
"eslint-plugin-vue": "~5.2.3",
"fuse.js": "^3.4.5",
"jest": "~24.8.0",
"node-sass": "~4.12.0",

View File

@ -45,7 +45,7 @@ export default {
// Initialize your apollo data
Post: [],
page: 1,
pageSize: 10,
pageSize: 12,
filter: {},
}
},

View File

@ -3754,10 +3754,10 @@ data-urls@^1.0.0:
whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0"
date-fns@2.0.0-alpha.35:
version "2.0.0-alpha.35"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.35.tgz#185813cdc51b05cc1468a95116494bb3f3440934"
integrity sha512-dAY1ujqRtyUsa9mVeupyMzUluWo1d7kAMwyXTQHFImKYSHKvxDw/dipiY6fdswQOs8CwpGoiKysGfaaRP5r3bA==
date-fns@2.0.0-alpha.36:
version "2.0.0-alpha.36"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.36.tgz#e3d106fbc6a7247e951916229b948d47848d6102"
integrity sha512-VcO89v0bmgw1V6wVlg9iYiOfyS9/m8T/TDhLBlERkF9NxVEatVr3LcwronDtUGMwGAK6KxUzs2TrOx7TzANMkw==
date-now@^0.1.4:
version "0.1.4"
@ -4283,10 +4283,10 @@ eslint-plugin-es@^1.4.0:
eslint-utils "^1.3.0"
regexpp "^2.0.1"
eslint-plugin-import@~2.17.3:
version "2.17.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.17.3.tgz#00548b4434c18faebaba04b24ae6198f280de189"
integrity sha512-qeVf/UwXFJbeyLbxuY8RgqDyEKCkqV7YC+E5S5uOjAp4tOc8zj01JP3ucoBM8JcEqd1qRasJSg6LLlisirfy0Q==
eslint-plugin-import@~2.18.0:
version "2.18.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.0.tgz#7a5ba8d32622fb35eb9c8db195c2090bd18a3678"
integrity sha512-PZpAEC4gj/6DEMMoU2Df01C5c50r7zdGIN52Yfi7CvvWaYssG7Jt5R9nFG5gmqodxNOz9vQS87xk6Izdtpdrig==
dependencies:
array-includes "^3.0.3"
contains-path "^0.1.0"
@ -4300,10 +4300,10 @@ eslint-plugin-import@~2.17.3:
read-pkg-up "^2.0.0"
resolve "^1.11.0"
eslint-plugin-jest@~22.7.0:
version "22.7.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.7.0.tgz#a1d325bccb024b04f5354c56fe790baba54a454c"
integrity sha512-0U9nBd9V6+GKpM/KvRDcmMuPsewSsdM7NxCozgJkVAh8IrwHmQ0aw44/eYuVkhT8Fcdhsz0zYiyPtKg147eXMQ==
eslint-plugin-jest@~22.7.1:
version "22.7.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.7.1.tgz#5dcdf8f7a285f98040378220d6beca581f0ab2a1"
integrity sha512-CrT3AzA738neimv8G8iK2HCkrCwHnAJeeo7k5TEHK86VMItKl6zdJT/tHBDImfnVVAYsVs4Y6BUdBZQCCgfiyw==
eslint-plugin-node@~9.1.0:
version "9.1.0"
@ -4334,10 +4334,10 @@ eslint-plugin-standard@~4.0.0:
resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.0.tgz#f845b45109c99cd90e77796940a344546c8f6b5c"
integrity sha512-OwxJkR6TQiYMmt1EsNRMe5qG3GsbjlcOhbGUBY4LtavF9DsLaTcoR+j2Tdjqi23oUwKNUqX7qcn5fPStafMdlA==
eslint-plugin-vue@~5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-5.2.2.tgz#86601823b7721b70bc92d54f1728cfc03b36283c"
integrity sha512-CtGWH7IB0DA6BZOwcV9w9q3Ri6Yuo8qMjx05SmOGJ6X6E0Yo3y9E/gQ5tuNxg2dEt30tRnBoFTbvtmW9iEoyHA==
eslint-plugin-vue@~5.2.3:
version "5.2.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-5.2.3.tgz#3ee7597d823b5478804b2feba9863b1b74273961"
integrity sha512-mGwMqbbJf0+VvpGR5Lllq0PMxvTdrZ/ZPjmhkacrCHbubJeJOt+T6E3HUzAifa2Mxi7RSdJfC9HFpOeSYVMMIw==
dependencies:
vue-eslint-parser "^5.0.0"

View File

@ -2303,10 +2303,9 @@ extsprintf@^1.2.0:
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
faker@^4.1.0:
faker@Marak/faker.js#master:
version "4.1.0"
resolved "https://registry.yarnpkg.com/faker/-/faker-4.1.0.tgz#1e45bbbecc6774b3c195fad2835109c6d748cc3f"
integrity sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=
resolved "https://codeload.github.com/Marak/faker.js/tar.gz/10bfb9f467b0ac2b8912ffc15690b50ef3244f09"
fast-deep-equal@^2.0.1:
version "2.0.1"