Add tests

This commit is contained in:
Angelina Filippova 2019-03-22 20:58:58 +00:00 committed by Maxim Filippov
parent 220ad0553a
commit 2ae27483b4
13 changed files with 2798 additions and 980 deletions

View File

@ -1,17 +1,25 @@
{ {
"presets": [ "presets": [
["env", { [
"modules": false, "@babel/preset-env", {
"targets": { "modules": "auto",
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"] "targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
} }
}], ],
"stage-2" "@vue/babel-preset-jsx"
],
"plugins": [
"@babel/plugin-transform-runtime",
"@babel/plugin-syntax-dynamic-import"
], ],
"plugins": ["transform-vue-jsx", "transform-runtime"],
"env": { "env": {
"development":{ "development":{
"plugins": ["dynamic-import-node"] "plugins": ["dynamic-import-node-babel-7"]
},
"test":{
"plugins": ["dynamic-import-node-babel-7"]
} }
} }
} }

View File

@ -9,7 +9,8 @@
"build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js", "build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js",
"build:sit": "cross-env NODE_ENV=production env_config=sit node build/build.js", "build:sit": "cross-env NODE_ENV=production env_config=sit node build/build.js",
"lint": "eslint --ext .js,.vue src", "lint": "eslint --ext .js,.vue src",
"test": "npm run lint", "test": "jest",
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
"precommit": "lint-staged", "precommit": "lint-staged",
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml" "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
}, },
@ -34,6 +35,7 @@
"url": "https://github.com/PanJiaChen/vue-element-admin/issues" "url": "https://github.com/PanJiaChen/vue-element-admin/issues"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.3.4",
"axios": "0.18.0", "axios": "0.18.0",
"clipboard": "1.7.1", "clipboard": "1.7.1",
"codemirror": "5.39.2", "codemirror": "5.39.2",
@ -46,6 +48,7 @@
"js-cookie": "2.2.0", "js-cookie": "2.2.0",
"jsonlint": "1.6.3", "jsonlint": "1.6.3",
"jszip": "3.1.5", "jszip": "3.1.5",
"lodash": "^4.17.11",
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",
"mockjs": "1.0.1-beta3", "mockjs": "1.0.1-beta3",
"normalize.css": "7.0.0", "normalize.css": "7.0.0",
@ -54,7 +57,7 @@
"showdown": "1.8.6", "showdown": "1.8.6",
"sortablejs": "1.7.0", "sortablejs": "1.7.0",
"tui-editor": "1.2.7", "tui-editor": "1.2.7",
"vue": "2.5.17", "vue": "^2.6.8",
"vue-count-to": "1.0.13", "vue-count-to": "1.0.13",
"vue-i18n": "7.3.2", "vue-i18n": "7.3.2",
"vue-router": "3.0.2", "vue-router": "3.0.2",
@ -64,17 +67,20 @@
"xlsx": "^0.11.16" "xlsx": "^0.11.16"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.3.4",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-transform-runtime": "^7.3.4",
"@babel/preset-env": "^7.3.4",
"@vue/babel-helper-vue-jsx-merge-props": "^1.0.0-beta.2",
"@vue/babel-preset-jsx": "^1.0.0-beta.2",
"@vue/test-utils": "^1.0.0-beta.29",
"autoprefixer": "8.5.0", "autoprefixer": "8.5.0",
"babel-core": "6.26.3",
"babel-eslint": "8.2.6", "babel-eslint": "8.2.6",
"babel-helper-vue-jsx-merge-props": "2.0.3", "babel-helper-vue-jsx-merge-props": "2.0.3",
"babel-loader": "7.1.5", "babel-jest": "^24.1.0",
"babel-plugin-dynamic-import-node": "2.0.0", "babel-loader": "^8.0.5",
"babel-plugin-syntax-jsx": "6.18.0", "babel-plugin-dynamic-import-node-babel-7": "^2.0.7",
"babel-plugin-transform-runtime": "6.23.0", "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"babel-plugin-transform-vue-jsx": "3.7.0",
"babel-preset-env": "1.7.0",
"babel-preset-stage-2": "6.24.1",
"chalk": "2.4.1", "chalk": "2.4.1",
"compression-webpack-plugin": "2.0.0", "compression-webpack-plugin": "2.0.0",
"connect": "3.6.6", "connect": "3.6.6",
@ -88,8 +94,10 @@
"file-loader": "1.1.11", "file-loader": "1.1.11",
"friendly-errors-webpack-plugin": "1.7.0", "friendly-errors-webpack-plugin": "1.7.0",
"hash-sum": "1.0.2", "hash-sum": "1.0.2",
"html-webpack-plugin": "4.0.0-alpha", "html-webpack-plugin": "^3.2.0",
"husky": "0.14.3", "husky": "0.14.3",
"jest": "^24.1.0",
"jest-transform-stub": "^2.0.0",
"lint-staged": "7.2.2", "lint-staged": "7.2.2",
"mini-css-extract-plugin": "0.4.1", "mini-css-extract-plugin": "0.4.1",
"node-notifier": "5.2.1", "node-notifier": "5.2.1",
@ -112,12 +120,13 @@
"svgo": "1.0.5", "svgo": "1.0.5",
"uglifyjs-webpack-plugin": "1.2.7", "uglifyjs-webpack-plugin": "1.2.7",
"url-loader": "1.0.1", "url-loader": "1.0.1",
"vue-jest": "4.0.0-beta.2",
"vue-loader": "15.3.0", "vue-loader": "15.3.0",
"vue-style-loader": "4.1.2", "vue-style-loader": "4.1.2",
"vue-template-compiler": "2.5.17", "vue-template-compiler": "^2.6.8",
"webpack": "4.16.5", "webpack": "^4.29.6",
"webpack-bundle-analyzer": "2.13.1", "webpack-bundle-analyzer": "2.13.1",
"webpack-cli": "3.1.0", "webpack-cli": "^3.2.3",
"webpack-dev-server": "3.1.14", "webpack-dev-server": "3.1.14",
"webpack-merge": "4.1.4" "webpack-merge": "4.1.4"
}, },
@ -129,5 +138,25 @@
"> 1%", "> 1%",
"last 2 versions", "last 2 versions",
"not ie <= 8" "not ie <= 8"
] ],
"jest": {
"moduleFileExtensions": [
"js",
"json",
"vue"
],
"moduleDirectories": [
"node_modules",
"src"
],
"transform": {
"^.+\\.vue$": "vue-jest",
"^.+\\.js$": "babel-jest",
".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$'": "jest-transform-stub"
},
"moduleNameMapper": {
"^.+.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
"^@/(.*)$": "<rootDir>/src/$1"
}
}
} }

View File

@ -0,0 +1,29 @@
const users = [
{ deactivated: false, id: '1', nickname: 'john', local: true },
{ deactivated: false, id: '2', nickname: 'bob', local: false },
{ deactivated: true, id: '3', nickname: 'allis', local: true }
]
export async function fetchUsers(showLocalUsersOnly, token, page = 1) {
const filteredUsers = showLocalUsersOnly ? users.filter(user => user.local) : users
return Promise.resolve({ data: {
users: filteredUsers,
count: filteredUsers.length,
page_size: 50
}})
}
export async function toggleUserActivation(nickname, token) {
const response = users.find(user => user.nickname === nickname)
return Promise.resolve({ data: { ...response, deactivated: !response.deactivated }})
}
export async function searchUsers(query, showLocalUsersOnly, token, page = 1) {
const filteredUsers = showLocalUsersOnly ? users.filter(user => user.local) : users
const response = filteredUsers.filter(user => user.nickname === query)
return Promise.resolve({ data: {
users: response,
count: response.length,
page_size: 50
}})
}

View File

@ -26,10 +26,11 @@ export async function loginByUsername(username, password) {
}) })
} }
export function getUserInfo() { export function getUserInfo(token) {
return request({ return request({
url: '/api/account/verify_credentials', url: '/api/account/verify_credentials',
method: 'post' method: 'post',
headers: token ? { 'Authorization': `Bearer ${token}` } : {}
}) })
} }

View File

@ -1,9 +0,0 @@
import request from '@/utils/request'
export function fetchList(query) {
return request({
url: '/transaction/list',
method: 'get',
params: query
})
}

View File

@ -1,23 +1,27 @@
import request from '@/utils/request' import request from '@/utils/request'
import { getToken } from '@/utils/auth'
export async function fetchUsers(page = 1, showLocalUsers) { export async function fetchUsers(showLocalUsersOnly, token, page = 1) {
return await request({ return await request({
url: `/api/pleroma/admin/users?page=${page}&local_only=${showLocalUsers}`, url: `/api/pleroma/admin/users?page=${page}&local_only=${showLocalUsersOnly}`,
method: 'get' method: 'get',
headers: token ? { 'Authorization': `Bearer ${getToken()}` } : {}
}) })
} }
export async function toggleUserActivation(nickname) { export async function toggleUserActivation(nickname, token) {
return await request({ return await request({
url: `/api/pleroma/admin/users/${nickname}/toggle_activation`, url: `/api/pleroma/admin/users/${nickname}/toggle_activation`,
method: 'patch' method: 'patch',
headers: token ? { 'Authorization': `Bearer ${getToken()}` } : {}
}) })
} }
export async function searchUsers(query, page = 1, showLocalUsers) { export async function searchUsers(query, showLocalUsersOnly, token, page = 1) {
return await request({ return await request({
url: `/api/pleroma/admin/users?query=${query}&page=${page}&local_only=${showLocalUsers}`, url: `/api/pleroma/admin/users?query=${query}&page=${page}&local_only=${showLocalUsersOnly}`,
method: 'get' method: 'get',
headers: token ? { 'Authorization': `Bearer ${getToken()}` } : {}
}) })
} }

View File

@ -54,7 +54,7 @@ const user = {
loginByUsername(username, userInfo.password).then(response => { loginByUsername(username, userInfo.password).then(response => {
const data = response.data const data = response.data
commit('SET_TOKEN', data.access_token) commit('SET_TOKEN', data.access_token)
setToken(response.data.access_token) setToken(data.access_token)
resolve() resolve()
}).catch(error => { }).catch(error => {
reject(error) reject(error)
@ -116,25 +116,25 @@ const user = {
removeToken() removeToken()
resolve() resolve()
}) })
}, }
// 动态修改权限 // 动态修改权限
ChangeRoles({ commit, dispatch }, role) { // ChangeRoles({ commit, dispatch }, role) {
return new Promise(resolve => { // return new Promise(resolve => {
commit('SET_TOKEN', role) // commit('SET_TOKEN', role)
setToken(role) // setToken(role)
getUserInfo(role).then(response => { // getUserInfo(role).then(response => {
const data = response.data // const data = response.data
commit('SET_ROLES', data.roles) // commit('SET_ROLES', data.roles)
commit('SET_NAME', data.name) // commit('SET_NAME', data.name)
commit('SET_ID', data.id) // commit('SET_ID', data.id)
commit('SET_AVATAR', data.avatar) // commit('SET_AVATAR', data.avatar)
commit('SET_INTRODUCTION', data.introduction) // commit('SET_INTRODUCTION', data.introduction)
dispatch('GenerateRoutes', data) // 动态修改权限后 重绘侧边菜单 // dispatch('GenerateRoutes', data) // 动态修改权限后 重绘侧边菜单
resolve() // resolve()
}) // })
}) // })
} // }
} }
} }

View File

@ -1,13 +1,13 @@
import { fetchUsers, toggleUserActivation, searchUsers } from '@/api/users' import { fetchUsers, toggleUserActivation, searchUsers } from '@/api/users'
const user = { const users = {
state: { state: {
fetchedUsers: [], fetchedUsers: [],
loading: true, loading: true,
searchQuery: '', searchQuery: '',
totalUsersCount: 0, totalUsersCount: 0,
currentPage: 1, currentPage: 1,
showLocalUsers: false showLocalUsersOnly: false
}, },
mutations: { mutations: {
SET_USERS: (state, users) => { SET_USERS: (state, users) => {
@ -38,23 +38,23 @@ const user = {
state.searchQuery = query state.searchQuery = query
}, },
SET_LOCAL_USERS_FILTER: (state, value) => { SET_LOCAL_USERS_FILTER: (state, value) => {
state.showLocalUsers = value state.showLocalUsersOnly = value
} }
}, },
actions: { actions: {
async FetchUsers({ commit, state }, { page }) { async FetchUsers({ commit, state, getters }, { page }) {
const response = await fetchUsers(page, state.showLocalUsers) const response = await fetchUsers(state.showLocalUsersOnly, getters.token, page)
commit('SET_LOADING', true) commit('SET_LOADING', true)
loadUsers(commit, page, response.data) loadUsers(commit, page, response.data)
}, },
async ToggleUserActivation({ commit }, nickname) { async ToggleUserActivation({ commit, getters }, nickname) {
const response = await toggleUserActivation(nickname) const response = await toggleUserActivation(nickname, getters.token)
commit('SWAP_USER', response.data) commit('SWAP_USER', response.data)
}, },
async SearchUsers({ commit, dispatch, state }, { query, page }) { async SearchUsers({ commit, dispatch, state, getters }, { query, page }) {
if (query.length === 0) { if (query.length === 0) {
commit('SET_SEARCH_QUERY', query) commit('SET_SEARCH_QUERY', query)
dispatch('FetchUsers', page) dispatch('FetchUsers', page)
@ -62,7 +62,7 @@ const user = {
commit('SET_LOADING', true) commit('SET_LOADING', true)
commit('SET_SEARCH_QUERY', query) commit('SET_SEARCH_QUERY', query)
const response = await searchUsers(query, page, state.showLocalUsers) const response = await searchUsers(query, state.showLocalUsersOnly, getters.token, page)
loadUsers(commit, page, response.data) loadUsers(commit, page, response.data)
} }
@ -82,4 +82,4 @@ const loadUsers = (commit, page, { users, count, page_size }) => {
commit('SET_LOADING', false) commit('SET_LOADING', false)
} }
export default user export default users

View File

@ -1,7 +1,5 @@
import axios from 'axios' import axios from 'axios'
import { Message } from 'element-ui' import { Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// create an axios instance // create an axios instance
const service = axios.create({ const service = axios.create({
@ -9,21 +7,6 @@ const service = axios.create({
timeout: 5000 // request timeout timeout: 5000 // request timeout
}) })
// request interceptor
service.interceptors.request.use(
config => {
if (store.getters.token) {
config.headers['Authorization'] = `Bearer ${getToken()}`
}
return config
},
error => {
// Do something with request error
console.log(error) // for debug
Promise.reject(error)
}
)
// response interceptor // response interceptor
service.interceptors.response.use( service.interceptors.response.use(
response => response, response => response,

View File

@ -2,7 +2,7 @@
<div class="users-container"> <div class="users-container">
<h1>Users</h1> <h1>Users</h1>
<div class="search-container"> <div class="search-container">
<el-checkbox :value="showLocalUsers" @change="handleLocalUsersCheckbox">Local users only</el-checkbox> <el-checkbox :value="showLocalUsersOnly" @change="handleLocalUsersCheckbox">Local users only</el-checkbox>
<el-input placeholder="Search" class="search" @input="handleDebounceSearchInput"/> <el-input placeholder="Search" class="search" @input="handleDebounceSearchInput"/>
</div> </div>
<el-table v-loading="loading" :data="users" style="width: 100%"> <el-table v-loading="loading" :data="users" style="width: 100%">
@ -20,6 +20,7 @@
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button
v-if="showDeactivatedButton(scope.row.id)" v-if="showDeactivatedButton(scope.row.id)"
class="toggle-activation"
type="text" type="text"
size="small" size="small"
@click="handleDeactivate(scope.row)" @click="handleDeactivate(scope.row)"
@ -61,8 +62,8 @@ export default {
currentPage() { currentPage() {
return this.$store.state.users.currentPage return this.$store.state.users.currentPage
}, },
showLocalUsers() { showLocalUsersOnly() {
return this.$store.state.users.showLocalUsers return this.$store.state.users.showLocalUsersOnly
}, },
isDesktop() { isDesktop() {
return this.$store.state.app.device === 'desktop' return this.$store.state.app.device === 'desktop'
@ -72,6 +73,11 @@ export default {
}, },
width() { width() {
return this.isMobile ? 60 : false return this.isMobile ? 60 : false
},
rowStyle(id) {
return {
'data-user-id': id
}
} }
}, },
created() { created() {
@ -144,7 +150,6 @@ only screen and (max-width: 760px),
margin-right: 7px; margin-right: 7px;
float: right; float: right;
} }
.search-container { .search-container {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@ -0,0 +1,136 @@
import Vuex from 'vuex'
import { mount, shallowMount, createLocalVue } from '@vue/test-utils'
import Element from 'element-ui'
import Users from '@/views/users/index'
import storeConfig from './store.conf'
import { cloneDeep } from 'lodash'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Element)
jest.mock('@/api/users')
describe('Users', () => {
let store
beforeEach(() => {
store = new Vuex.Store(cloneDeep(storeConfig))
})
it('fetches initial list of users', async (done) => {
const wrapper = mount(Users, {
store,
localVue
})
await wrapper.vm.$nextTick()
expect(wrapper.vm.usersCount).toEqual(3)
done()
})
it('toggles activation status on button click', async (done) => {
const wrapper = mount(Users, {
store,
localVue
})
await wrapper.vm.$nextTick()
const user = store.state.users.fetchedUsers[1]
expect(user.deactivated).toBe(false)
wrapper.find('.el-table__fixed-body-wrapper table tr:nth-child(2) button').trigger('click')
await wrapper.vm.$nextTick()
const updatedUser = store.state.users.fetchedUsers[1]
expect(updatedUser.deactivated).toBe(true)
done()
})
it('starts a search on input change', async (done) => {
const wrapper = mount(Users, {
store,
localVue
})
wrapper.vm.handleDebounceSearchInput = (query) => {
store.dispatch('SearchUsers', { query, page: 1 })
}
await wrapper.vm.$nextTick()
expect(wrapper.vm.usersCount).toEqual(3)
const input = wrapper.find('input.el-input__inner')
input.element.value = 'bob'
input.trigger('input')
await wrapper.vm.$nextTick()
expect(wrapper.vm.usersCount).toEqual(1)
input.element.value = ''
input.trigger('input')
await wrapper.vm.$nextTick()
expect(wrapper.vm.usersCount).toEqual(3)
done()
})
it('shows local users on checkbox click', async (done) => {
const wrapper = mount(Users, {
store,
localVue
})
await wrapper.vm.$nextTick()
expect(wrapper.vm.usersCount).toEqual(3)
const input = wrapper.find('input.el-checkbox__original')
input.trigger('click')
await wrapper.vm.$nextTick()
expect(wrapper.vm.usersCount).toEqual(2)
input.trigger('click')
await wrapper.vm.$nextTick()
expect(wrapper.vm.usersCount).toEqual(3)
done()
})
it('shows local users with query search', async (done) => {
const wrapper = mount(Users, {
store,
localVue
})
wrapper.vm.handleDebounceSearchInput = (query) => {
store.dispatch('SearchUsers', { query, page: 1 })
}
await wrapper.vm.$nextTick()
expect(wrapper.vm.usersCount).toEqual(3)
const checkboxInput = wrapper.find('input.el-checkbox__original')
checkboxInput.trigger('click')
await wrapper.vm.$nextTick()
const searchInput = wrapper.find('input.el-input__inner')
searchInput.element.value = 'bob'
searchInput.trigger('input')
await wrapper.vm.$nextTick()
expect(wrapper.vm.usersCount).toEqual(0)
searchInput.element.value = 'allis'
searchInput.trigger('input')
await wrapper.vm.$nextTick()
expect(wrapper.vm.usersCount).toEqual(1)
searchInput.element.value = ''
searchInput.trigger('input')
await wrapper.vm.$nextTick()
expect(wrapper.vm.usersCount).toEqual(2)
checkboxInput.trigger('click')
await wrapper.vm.$nextTick()
expect(wrapper.vm.usersCount).toEqual(3)
done()
})
})

View File

@ -0,0 +1,28 @@
import getters from '@/store/getters'
import app from '@/store/modules/app'
import user from '@/store/modules/user'
import users from '@/store/modules/users'
export default {
modules: {
app,
users,
user: {
state: {
user: '',
id: '1',
status: '',
code: '',
token: 'MmwSkMgBW6lAkWCFspIjX9icmLfqSCohSi-GReAZrQw',
name: 'john',
avatar: '',
introduction: '',
roles: ["admin"],
setting: {
articlePlatform: []
}
}, ...user
}
},
getters
}

3382
yarn.lock

File diff suppressed because it is too large Load Diff