Merge branch 'master' into i18n-pluralization

This commit is contained in:
Grzegorz Leoniec 2019-01-02 13:50:20 +01:00 committed by GitHub
commit 9de15879f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 220 additions and 81 deletions

View File

@ -44,6 +44,19 @@ const logout = () => {
cy.location('pathname').should('contain', '/login') // we're out
}
const lastColumnIsSortedInDescendingOrder = () => {
cy.get('tbody')
.find('tr td:last-child')
.then(last_column => {
cy.wrap(last_column)
const values = last_column
.map((i, td) => parseInt(td.textContent))
.toArray()
const ordered_descending = values.slice(0).sort((a, b) => b - a)
return cy.wrap(values).should('deep.eq', ordered_descending)
})
}
Given('I am logged in', () => {
login('admin@example.org', 1234)
})
@ -52,11 +65,11 @@ Given('we have a selection of tags and categories as well as posts', () => {
// TODO: use db factories instead of seed data
})
Given('my account has the following details:', (table) => {
Given('my account has the following details:', table => {
// TODO: use db factories instead of seed data
})
Given('my user account has the role {string}', (role) => {
Given('my user account has the role {string}', role => {
// TODO: use db factories instead of seed data
})
@ -127,28 +140,22 @@ When('I navigate to the administration dashboard', () => {
.click()
})
When('I click on {string}', linkOrButton => {
When(`I click on {string}`, linkOrButton => {
cy.contains(linkOrButton).click()
})
Then('I can see a list of categories ordered by post count:', table => {
// TODO: match the table in the feature with the html table
cy.get('thead').find('tr th').should('have.length', 3)
const last_column = cy.get('tbody').find('tr td:last-child').then((last_column) => {
cy.wrap(last_column)
const values = last_column.map((i, td) => parseInt(td.textContent)).toArray()
const ordered_descending = values.slice(0).sort((a,b) => b - a)
return cy.wrap(values).should('deep.eq', ordered_descending)
})
cy.get('thead')
.find('tr th')
.should('have.length', 3)
lastColumnIsSortedInDescendingOrder()
})
Then('I can see a list of tags ordered by user and post count:', table => {
// TODO: match the table in the feature with the html table
cy.get('thead').find('tr th').should('have.length', 4)
const last_column = cy.get('tbody').find('tr td:last-child').then((last_column) => {
cy.wrap(last_column)
const values = last_column.map((i, td) => parseInt(td.textContent)).toArray()
const ordered_descending = values.slice(0).sort((a,b) => b - a)
return cy.wrap(values).should('deep.eq', ordered_descending)
})
cy.get('thead')
.find('tr th')
.should('have.length', 4)
lastColumnIsSortedInDescendingOrder()
})

View File

@ -54,20 +54,20 @@
"v-tooltip": "^2.0.0-rc.33",
"vue-count-to": "^1.0.13",
"vue-izitoast": "1.1.2",
"vuex-i18n": "^1.10.5"
"vuex-i18n": "^1.10.9"
},
"devDependencies": {
"@vue/eslint-config-prettier": "^4.0.1",
"@vue/server-test-utils": "^1.0.0-beta.27",
"@vue/test-utils": "^1.0.0-beta.27",
"@vue/server-test-utils": "^1.0.0-beta.28",
"@vue/test-utils": "^1.0.0-beta.28",
"babel-eslint": "^10.0.1",
"babel-jest": "^23.6.0",
"babel-preset-env": "^1.7.0",
"cypress-cucumber-preprocessor": "^1.9.1",
"eslint": "^5.10.0",
"eslint": "^5.11.1",
"eslint-config-prettier": "^3.1.0",
"eslint-loader": "^2.0.0",
"eslint-plugin-prettier": "3.0.0",
"eslint-plugin-prettier": "3.0.1",
"eslint-plugin-vue": "^5.0.0",
"jest": "^23.6.0",
"node-sass": "^4.11.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

After

Width:  |  Height:  |  Size: 130 KiB

View File

@ -13,11 +13,6 @@ export const mutations = {
SET_USER(state, user) {
state.user = user || null
},
SET_USER_SETTINGS(state, userSettings) {
// state.user = Object.assign(state.user, {
// userSettings: Object.assign(this.getters['auth/userSettings'], userSettings)
// })
},
SET_TOKEN(state, token) {
state.token = token || null
},
@ -27,15 +22,15 @@ export const mutations = {
}
export const getters = {
isAuthenticated(state) {
return !!state.token
},
isLoggedIn(state) {
return !!(state.user && state.token)
},
pending(state) {
return !!state.pending
},
isVerified(state) {
return !!state.user && state.user.isVerified && !!state.user.name
},
isAdmin(state) {
return !!state.user && state.user.role === 'admin'
},
@ -51,20 +46,6 @@ export const getters = {
token(state) {
return state.token
}
// userSettings(state, getters, rootState, rootGetters) {
// const userSettings = (state.user && state.user.userSettings) ? state.user.userSettings : {}
//
// const defaultLanguage = (state.user && state.user.language) ? state.user.language : rootGetters['i18n/locale']
// let contentLanguages = !isEmpty(userSettings.contentLanguages) ? userSettings.contentLanguages : []
// if (isEmpty(contentLanguages)) {
// contentLanguages = userSettings.uiLanguage ? [userSettings.uiLanguage] : [defaultLanguage]
// }
//
// return Object.assign({
// uiLanguage: defaultLanguage,
// contentLanguages: contentLanguages
// }, userSettings)
// }
}
export const actions = {
@ -97,10 +78,8 @@ export const actions = {
return getters.isLoggedIn
},
async login({ commit }, { email, password }) {
commit('SET_PENDING', true)
try {
commit('SET_PENDING', true)
commit('SET_USER', null)
commit('SET_TOKEN', null)
const res = await this.app.apolloProvider.defaultClient
.mutate({
mutation: gql(`
@ -120,22 +99,15 @@ export const actions = {
})
.then(({ data }) => data && data.login)
if (res && res.token) {
await this.app.$apolloHelpers.onLogin(res.token)
commit('SET_TOKEN', res.token)
delete res.token
commit('SET_USER', res)
commit('SET_PENDING', false)
return true
} else {
commit('SET_PENDING', false)
throw new Error('THERE IS AN ERROR')
}
await this.app.$apolloHelpers.onLogin(res.token)
commit('SET_TOKEN', res.token)
const userData = Object.assign({}, res)
delete userData.token
commit('SET_USER', userData)
} catch (err) {
commit('SET_USER', null)
commit('SET_TOKEN', null)
commit('SET_PENDING', false)
throw new Error(err)
} finally {
commit('SET_PENDING', false)
}
},
async logout({ commit }) {

160
store/auth.test.js Normal file
View File

@ -0,0 +1,160 @@
import { getters, mutations, actions } from './auth.js'
let state
let commit
const token =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InUzIiwic2x1ZyI6Implbm55LXJvc3RvY2siLCJuYW1lIjoiSmVubnkgUm9zdG9jayIsImF2YXRhciI6Imh0dHBzOi8vczMuYW1hem9uYXdzLmNvbS91aWZhY2VzL2ZhY2VzL3R3aXR0ZXIvbXV0dV9rcmlzaC8xMjguanBnIiwiZW1haWwiOiJ1c2VyQGV4YW1wbGUub3JnIiwicm9sZSI6InVzZXIiLCJpYXQiOjE1NDUxNDQ2ODgsImV4cCI6MTYzMTU0NDY4OCwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo0MDAwIiwic3ViIjoidTMifQ.s5_JeQN9TaUPfymAXPOpbMAwhmTIg9cnOvNEcj4z75k'
const successfulLoginResponse = {
data: {
login: {
id: 'u3',
name: 'Jenny Rostock',
slug: 'jenny-rostock',
email: 'user@example.org',
avatar:
'https://s3.amazonaws.com/uifaces/faces/twitter/mutu_krish/128.jpg',
role: 'user',
token
}
}
}
const incorrectPasswordResponse = {
data: {
login: null
},
errors: [
{
message: 'Incorrect password.',
locations: [
{
line: 2,
column: 3
}
],
path: ['login']
}
]
}
beforeEach(() => {
commit = jest.fn()
})
describe('getters', () => {
describe('isAuthenticated', () => {
describe('given JWT Bearer token', () => {
test('true', () => {
state = { token }
expect(getters.isAuthenticated(state)).toBe(true)
})
})
})
})
describe('actions', () => {
let action
describe('login', () => {
describe('given valid credentials and a successful response', () => {
beforeEach(async () => {
const response = Object.assign({}, successfulLoginResponse)
const mutate = jest.fn(() => Promise.resolve(response))
const onLogin = jest.fn(() => Promise.resolve())
const module = {
app: {
apolloProvider: { defaultClient: { mutate } },
$apolloHelpers: { onLogin }
}
}
action = actions.login.bind(module)
await action(
{ commit },
{ email: 'user@example.org', password: '1234' }
)
})
afterEach(() => {
action = null
})
it('saves the JWT Bearer token', () => {
expect(commit.mock.calls).toEqual(
expect.arrayContaining([['SET_TOKEN', token]])
)
})
it('saves user data without token', () => {
expect(commit.mock.calls).toEqual(
expect.arrayContaining([
[
'SET_USER',
{
id: 'u3',
name: 'Jenny Rostock',
slug: 'jenny-rostock',
email: 'user@example.org',
avatar:
'https://s3.amazonaws.com/uifaces/faces/twitter/mutu_krish/128.jpg',
role: 'user'
}
]
])
)
})
it('saves pending flags in order', () => {
expect(commit.mock.calls).toEqual(
expect.arrayContaining([
['SET_PENDING', true],
['SET_PENDING', false]
])
)
})
})
describe('given invalid credentials and incorrect password response', () => {
let onLogin
let mutate
beforeEach(() => {
mutate = jest.fn(() => Promise.reject('This error is expected.'))
onLogin = jest.fn(() => Promise.resolve())
const module = {
app: {
apolloProvider: { defaultClient: { mutate } },
$apolloHelpers: { onLogin }
}
}
action = actions.login.bind(module)
})
afterEach(() => {
action = null
})
it('populates error messages', async () => {
expect(
action({ commit }, { email: 'user@example.org', password: 'wrong' })
).rejects.toThrowError('This error is expected.')
expect(mutate).toHaveBeenCalled()
expect(onLogin).not.toHaveBeenCalled()
})
it('saves pending flags in order', async () => {
try {
await action(
{ commit },
{ email: 'user@example.org', password: 'wrong' }
)
} catch (err) {} // ignore
expect(commit.mock.calls).toEqual(
expect.arrayContaining([
['SET_PENDING', true],
['SET_PENDING', false]
])
)
})
})
})
})

View File

@ -1344,17 +1344,17 @@
eslint-plugin-prettier "^3.0.0"
prettier "^1.15.2"
"@vue/server-test-utils@^1.0.0-beta.27":
version "1.0.0-beta.27"
resolved "https://registry.yarnpkg.com/@vue/server-test-utils/-/server-test-utils-1.0.0-beta.27.tgz#0c61cb724d04bf44c70938b4a6054e73e914492a"
integrity sha512-HKwm1Nw180qNgknPyok/XWJF4ojBZC5vSANPf+f1WR2jYsSc0vvAxUyX2Qeq4F7KLXrfsFwdkfH3a5BxdrA9tg==
"@vue/server-test-utils@^1.0.0-beta.28":
version "1.0.0-beta.28"
resolved "https://registry.yarnpkg.com/@vue/server-test-utils/-/server-test-utils-1.0.0-beta.28.tgz#40adb6a6c05e8970cc5e5f0869eb490fa00d9cf3"
integrity sha512-CrXQ2ISmLvye4HC4sIHqqR3Gyg02i8XcTwMAyIh+UY7fh1Nz4f9XE4LDsxg0B9uSGsSiR0lwphjlGtbUJjGl4A==
dependencies:
cheerio "^1.0.0-rc.2"
"@vue/test-utils@^1.0.0-beta.27":
version "1.0.0-beta.27"
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.0.0-beta.27.tgz#7e5f7b7180c00e28a4ca55c0ff0a7e754377fdb2"
integrity sha512-Lzrd4ZBkS70Tl8JbXbDrN/NcSaH9aZT6+7emU3QhTJ+CrorJpyFDA1dkvSIhH+rDTs8sHFbGeXjXV/qorXxtRw==
"@vue/test-utils@^1.0.0-beta.28":
version "1.0.0-beta.28"
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.0.0-beta.28.tgz#767c43413df8cde86128735e58923803e444b9a5"
integrity sha512-uVbFJG0g/H9hf2pgWUdhvQYItRGzQ44cMFf00wp0YEo85pxuvM9e3mx8QLQfx6R2CogxbK4CvV7qvkLblehXeQ==
dependencies:
dom-event-types "^1.0.0"
lodash "^4.17.4"
@ -4875,10 +4875,10 @@ eslint-loader@^2.0.0:
object-hash "^1.1.4"
rimraf "^2.6.1"
eslint-plugin-prettier@3.0.0, eslint-plugin-prettier@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.0.0.tgz#f6b823e065f8c36529918cdb766d7a0e975ec30c"
integrity sha512-4g11opzhqq/8+AMmo5Vc2Gn7z9alZ4JqrbZ+D4i8KlSyxeQhZHlmIrY8U9Akf514MoEhogPa87Jgkq87aZ2Ohw==
eslint-plugin-prettier@3.0.1, eslint-plugin-prettier@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.0.1.tgz#19d521e3981f69dd6d14f64aec8c6a6ac6eb0b0d"
integrity sha512-/PMttrarPAY78PLvV3xfWibMOdMDl57hmlQ2XqFeA37wd+CJ7WSxV7txqjVPHi/AAFKd2lX0ZqfsOc/i5yFCSQ==
dependencies:
prettier-linter-helpers "^1.0.0"
@ -4915,10 +4915,10 @@ eslint-visitor-keys@^1.0.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==
eslint@^5.10.0:
version "5.10.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.10.0.tgz#24adcbe92bf5eb1fc2d2f2b1eebe0c5e0713903a"
integrity sha512-HpqzC+BHULKlnPwWae9MaVZ5AXJKpkxCVXQHrFaRw3hbDj26V/9ArYM4Rr/SQ8pi6qUPLXSSXC4RBJlyq2Z2OQ==
eslint@^5.11.1:
version "5.11.1"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.11.1.tgz#8deda83db9f354bf9d3f53f9677af7e0e13eadda"
integrity sha512-gOKhM8JwlFOc2acbOrkYR05NW8M6DCMSvfcJiBB5NDxRE1gv8kbvxKaC9u69e6ZGEMWXcswA/7eKR229cEIpvg==
dependencies:
"@babel/code-frame" "^7.0.0"
ajv "^6.5.3"
@ -11682,10 +11682,10 @@ vue@^2.5.17:
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.17.tgz#0f8789ad718be68ca1872629832ed533589c6ada"
integrity sha512-mFbcWoDIJi0w0Za4emyLiW72Jae0yjANHbCVquMKijcavBGypqlF7zHRgMa5k4sesdv7hv2rB4JPdZfR+TPfhQ==
vuex-i18n@^1.10.5:
version "1.10.5"
resolved "https://registry.yarnpkg.com/vuex-i18n/-/vuex-i18n-1.10.5.tgz#635ea2204e0aa3f8fd512f0fab7f6b994d3f666c"
integrity sha1-Y16iIE4Ko/j9US8Pq39rmU0/Zmw=
vuex-i18n@^1.10.9:
version "1.10.9"
resolved "https://registry.yarnpkg.com/vuex-i18n/-/vuex-i18n-1.10.9.tgz#5d5dc551f33b1d229b9bbff45deec29d9be93143"
integrity sha512-9JECE4I9VG0ZDCTV7kAldLdZN7FXJi/wjGi7DP/gosFyiye9jKBuIWO1djxkenl1ablTLfu/O6oTWBZuOIUsbQ==
vuex@^3.0.1:
version "3.0.1"