Merge branch 'feature/select-multiple-users' into 'master'
Apply user actions to multiple users Closes #18 See merge request pleroma/admin-fe!17
This commit is contained in:
commit
366fdbfc1e
9 changed files with 694 additions and 52 deletions
|
@ -52,6 +52,7 @@
|
|||
"lodash.debounce": "^4.0.8",
|
||||
"normalize.css": "7.0.0",
|
||||
"nprogress": "0.2.0",
|
||||
"numeral": "^2.0.6",
|
||||
"screenfull": "4.0.0",
|
||||
"showdown": "1.8.6",
|
||||
"sortablejs": "1.7.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const users = [
|
||||
export const users = [
|
||||
{ active: true, deactivated: false, id: '2', nickname: 'allis', local: true, external: false, roles: { admin: true, moderator: false }, tags: [] },
|
||||
{ active: true, deactivated: false, id: '10', nickname: 'bob', local: false, external: true, roles: { admin: false, moderator: true }, tags: ['sandbox'] },
|
||||
{ active: true, deactivated: false, id: '10', nickname: 'bob', local: false, external: true, roles: { admin: false, moderator: false }, tags: ['sandbox'] },
|
||||
{ active: false, deactivated: true, id: 'abc', nickname: 'john', local: true, external: false, roles: { admin: false, moderator: false }, tags: ['strip_media'] }
|
||||
]
|
||||
|
||||
|
|
|
@ -188,7 +188,9 @@ export default {
|
|||
forceUnlisted: 'Force posts to be unlisted',
|
||||
sandbox: 'Force posts to be followers-only',
|
||||
disableRemoteSubscription: 'Disallow following user from remote instances',
|
||||
disableAnySubscription: 'Disallow following user at all'
|
||||
disableAnySubscription: 'Disallow following user at all',
|
||||
selectUsers: 'Select users to apply actions to multiple users',
|
||||
moderateUsers: 'Moderate multiple users'
|
||||
},
|
||||
usersFilter: {
|
||||
inputPlaceholder: 'Select filter',
|
||||
|
|
|
@ -21,14 +21,9 @@ const users = {
|
|||
SET_LOADING: (state, status) => {
|
||||
state.loading = status
|
||||
},
|
||||
SWAP_USER: (state, user) => {
|
||||
const usersWithoutSwapped = state.fetchedUsers.filter(u => {
|
||||
return u.id !== user.id
|
||||
})
|
||||
|
||||
state.fetchedUsers = [...usersWithoutSwapped, user].sort((a, b) =>
|
||||
a.nickname.localeCompare(b.nickname)
|
||||
)
|
||||
SWAP_USER: (state, updatedUser) => {
|
||||
const updated = state.fetchedUsers.map(user => user.id === updatedUser.id ? updatedUser : user)
|
||||
state.fetchedUsers = updated.sort((a, b) => a.nickname.localeCompare(b.nickname))
|
||||
},
|
||||
SWAP_USERS: (state, users) => {
|
||||
const usersWithoutSwapped = users.reduce((acc, user) => {
|
||||
|
|
259
src/views/users/components/DropdownActionsMenu.vue
Normal file
259
src/views/users/components/DropdownActionsMenu.vue
Normal file
|
@ -0,0 +1,259 @@
|
|||
<template>
|
||||
<el-dropdown size="small" trigger="click" placement="bottom-start">
|
||||
<el-button v-if="isDesktop" class="actions-button">
|
||||
<span class="actions-button-container">
|
||||
<span>
|
||||
<i class="el-icon-edit" />
|
||||
{{ $t('users.moderateUsers') }}
|
||||
</span>
|
||||
<i class="el-icon-arrow-down el-icon--right"/>
|
||||
</span>
|
||||
</el-button>
|
||||
<el-dropdown-menu v-if="showDropdownForMultipleUsers" slot="dropdown">
|
||||
<el-dropdown-item
|
||||
@click.native="grantRightToMultipleUsers('admin')">
|
||||
{{ $t('users.grantAdmin') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
@click.native="revokeRightFromMultipleUsers('admin')">
|
||||
{{ $t('users.revokeAdmin') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
@click.native="grantRightToMultipleUsers('moderator')">
|
||||
{{ $t('users.grantModerator') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
@click.native="revokeRightFromMultipleUsers('moderator')">
|
||||
{{ $t('users.revokeModerator') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
divided
|
||||
@click.native="activateMultipleUsers">
|
||||
{{ $t('users.activateAccount') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
@click.native="deactivateMultipleUsers">
|
||||
{{ $t('users.deactivateAccount') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
@click.native="deleteMultipleUsers">
|
||||
{{ $t('users.deleteAccount') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided class="no-hover">
|
||||
<div class="tag-container">
|
||||
<span class="tag-text">{{ $t('users.forceNsfw') }}</span>
|
||||
<el-button-group class="tag-button-group">
|
||||
<el-button size="mini" @click.native="addTagForMultipleUsers('force_nsfw')">apply</el-button>
|
||||
<el-button size="mini" @click.native="removeTagFromMultipleUsers('force_nsfw')">remove</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item class="no-hover">
|
||||
<div class="tag-container">
|
||||
<span class="tag-text">{{ $t('users.stripMedia') }}</span>
|
||||
<el-button-group class="tag-button-group">
|
||||
<el-button size="mini" @click.native="addTagForMultipleUsers('strip_media')">apply</el-button>
|
||||
<el-button size="mini" @click.native="removeTagFromMultipleUsers('strip_media')">remove</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item class="no-hover">
|
||||
<div class="tag-container">
|
||||
<span class="tag-text">{{ $t('users.forceUnlisted') }}</span>
|
||||
<el-button-group class="tag-button-group">
|
||||
<el-button size="mini" @click.native="addTagForMultipleUsers('force_unlisted')">apply</el-button>
|
||||
<el-button size="mini" @click.native="removeTagFromMultipleUsers('force_unlisted')">remove</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item class="no-hover">
|
||||
<div class="tag-container">
|
||||
<span class="tag-text">{{ $t('users.sandbox') }}</span>
|
||||
<el-button-group class="tag-button-group">
|
||||
<el-button size="mini" @click.native="addTagForMultipleUsers('sandbox')">apply</el-button>
|
||||
<el-button size="mini" @click.native="removeTagFromMultipleUsers('sandbox')">remove</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item class="no-hover">
|
||||
<div class="tag-container">
|
||||
<span class="tag-text">{{ $t('users.disableRemoteSubscription') }}</span>
|
||||
<el-button-group class="tag-button-group">
|
||||
<el-button size="mini" @click.native="addTagForMultipleUsers('disable_remote_subscription')">apply</el-button>
|
||||
<el-button size="mini" @click.native="removeTagFromMultipleUsers('disable_remote_subscription')">remove</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item class="no-hover">
|
||||
<div class="tag-container">
|
||||
<span class="tag-text">{{ $t('users.disableAnySubscription') }}</span>
|
||||
<el-button-group class="tag-button-group">
|
||||
<el-button size="mini" @click.native="addTagForMultipleUsers('disable_any_subscription')">apply</el-button>
|
||||
<el-button size="mini" @click.native="removeTagFromMultipleUsers('disable_any_subscription')">remove</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
<el-dropdown-menu v-else slot="dropdown">
|
||||
<el-dropdown-item>
|
||||
{{ $t('users.selectUsers') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
selectedUsers: {
|
||||
type: Array,
|
||||
default: function() {
|
||||
return []
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showDropdownForMultipleUsers() {
|
||||
return this.$props.selectedUsers.length > 0
|
||||
},
|
||||
isDesktop() {
|
||||
return this.$store.state.app.device === 'desktop'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
mappers() {
|
||||
return {
|
||||
grantRight: (right) => () => this.selectedUsers
|
||||
.filter(user => user.local && !user.roles[right] && this.$store.state.user.id !== user.id)
|
||||
.map(user => this.$store.dispatch('ToggleRight', { user, right })),
|
||||
revokeRight: (right) => () => this.selectedUsers
|
||||
.filter(user => user.local && user.roles[right] && this.$store.state.user.id !== user.id)
|
||||
.map(user => this.$store.dispatch('ToggleRight', { user, right })),
|
||||
activate: () => this.selectedUsers
|
||||
.filter(user => user.deactivated && this.$store.state.user.id !== user.id)
|
||||
.map(user => this.$store.dispatch('ToggleUserActivation', user.nickname)),
|
||||
deactivate: () => this.selectedUsers
|
||||
.filter(user => !user.deactivated && this.$store.state.user.id !== user.id)
|
||||
.map(user => this.$store.dispatch('ToggleUserActivation', user.nickname)),
|
||||
remove: () => this.selectedUsers
|
||||
.filter(user => this.$store.state.user.id !== user.id)
|
||||
.map(user => this.$store.dispatch('DeleteUser', user)),
|
||||
addTag: (tag) => () => {
|
||||
const users = this.selectedUsers
|
||||
.filter(user => tag === 'disable_remote_subscription' || tag === 'disable_any_subscription'
|
||||
? user.local && !user.tags.includes(tag)
|
||||
: !user.tags.includes(tag))
|
||||
this.$store.dispatch('AddTag', { users, tag })
|
||||
},
|
||||
removeTag: (tag) => () => {
|
||||
const users = this.selectedUsers
|
||||
.filter(user => tag === 'disable_remote_subscription' || tag === 'disable_any_subscription'
|
||||
? user.local && user.tags.includes(tag)
|
||||
: user.tags.includes(tag))
|
||||
this.$store.dispatch('RemoveTag', { users, tag })
|
||||
}
|
||||
}
|
||||
},
|
||||
grantRightToMultipleUsers(right) {
|
||||
const { grantRight } = this.mappers()
|
||||
this.confirmMessage(
|
||||
`Are you sure you want to grant ${right} rights to all selected users?`,
|
||||
grantRight(right)
|
||||
)
|
||||
},
|
||||
revokeRightFromMultipleUsers(right) {
|
||||
const { revokeRight } = this.mappers()
|
||||
this.confirmMessage(
|
||||
`Are you sure you want to revoke ${right} rights from all selected users?`,
|
||||
revokeRight(right)
|
||||
)
|
||||
},
|
||||
activateMultipleUsers() {
|
||||
const { activate } = this.mappers()
|
||||
this.confirmMessage(
|
||||
'Are you sure you want to activate accounts of all selected users?',
|
||||
activate
|
||||
)
|
||||
},
|
||||
deactivateMultipleUsers() {
|
||||
const { deactivate } = this.mappers()
|
||||
this.confirmMessage(
|
||||
'Are you sure you want to deactivate accounts of all selected users?',
|
||||
deactivate
|
||||
)
|
||||
},
|
||||
deleteMultipleUsers() {
|
||||
const { remove } = this.mappers()
|
||||
this.confirmMessage(
|
||||
'Are you sure you want to delete accounts of all selected users?',
|
||||
remove
|
||||
)
|
||||
},
|
||||
addTagForMultipleUsers(tag) {
|
||||
const { addTag } = this.mappers()
|
||||
this.confirmMessage(
|
||||
'Are you sure you want to apply tag to all selected users?',
|
||||
addTag(tag)
|
||||
)
|
||||
},
|
||||
removeTagFromMultipleUsers(tag) {
|
||||
const { removeTag } = this.mappers()
|
||||
this.confirmMessage(
|
||||
'Are you sure you want to remove tag from all selected users?',
|
||||
removeTag(tag)
|
||||
)
|
||||
},
|
||||
confirmMessage(message, applyAction) {
|
||||
this.$confirm(message, {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
applyAction()
|
||||
this.$emit('apply-action')
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: 'Completed'
|
||||
})
|
||||
}).catch(() => {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: 'Canceled'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel='stylesheet/scss' lang='scss' scoped>
|
||||
.actions-button {
|
||||
text-align: left;
|
||||
margin: 0 15px 10px 0;
|
||||
width: 350px;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
.actions-button-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.el-dropdown {
|
||||
float: right;
|
||||
}
|
||||
.el-dropdown-menu {
|
||||
margin-right: 15px;
|
||||
}
|
||||
.tag-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.tag-text {
|
||||
padding-right: 20px;
|
||||
}
|
||||
.no-hover:hover {
|
||||
color: #606266;
|
||||
background-color: white;
|
||||
cursor: auto;
|
||||
}
|
||||
</style>
|
|
@ -1,11 +1,29 @@
|
|||
<template>
|
||||
<div class="users-container">
|
||||
<h1>{{ $t('users.users') }}</h1>
|
||||
<h1>
|
||||
{{ $t('users.users') }}
|
||||
<span class="user-count">({{ normalizedUsersCount }})</span>
|
||||
</h1>
|
||||
<div class="search-container">
|
||||
<users-filter/>
|
||||
<el-input :placeholder="$t('users.search')" v-model="search" class="search" @input="handleDebounceSearchInput"/>
|
||||
</div>
|
||||
<el-table v-loading="loading" :data="users" style="width: 100%">
|
||||
<dropdown-actions-menu
|
||||
:selected-users="selectedUsers"
|
||||
@apply-action="clearSelection"/>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
ref="usersTable"
|
||||
:data="users"
|
||||
row-key="id"
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange">
|
||||
<el-table-column
|
||||
v-if="isDesktop"
|
||||
type="selection"
|
||||
reserve-selection
|
||||
width="44"
|
||||
align="center"/>
|
||||
<el-table-column :min-width="width" :label="$t('users.id')" prop="id" />
|
||||
<el-table-column :label="$t('users.name')" prop="nickname">
|
||||
<template slot-scope="scope">
|
||||
|
@ -31,7 +49,7 @@
|
|||
</el-table-column>
|
||||
<el-table-column :label="$t('users.actions')" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-dropdown size="small">
|
||||
<el-dropdown size="small" trigger="click">
|
||||
<span class="el-dropdown-link">
|
||||
{{ $t('users.moderation') }}
|
||||
<i v-if="isDesktop" class="el-icon-arrow-down el-icon--right"/>
|
||||
|
@ -117,12 +135,20 @@
|
|||
|
||||
<script>
|
||||
import debounce from 'lodash.debounce'
|
||||
import numeral from 'numeral'
|
||||
import UsersFilter from './components/UsersFilter'
|
||||
import DropdownActionsMenu from './components/DropdownActionsMenu'
|
||||
|
||||
export default {
|
||||
name: 'Users',
|
||||
components: {
|
||||
UsersFilter
|
||||
UsersFilter,
|
||||
DropdownActionsMenu
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
selectedUsers: []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -133,6 +159,9 @@ export default {
|
|||
loading() {
|
||||
return this.$store.state.users.loading
|
||||
},
|
||||
normalizedUsersCount() {
|
||||
return numeral(this.$store.state.users.totalUsersCount).format('0a')
|
||||
},
|
||||
users() {
|
||||
return this.$store.state.users.fetchedUsers
|
||||
},
|
||||
|
@ -167,6 +196,9 @@ export default {
|
|||
activationIcon(status) {
|
||||
return status ? 'el-icon-error' : 'el-icon-success'
|
||||
},
|
||||
clearSelection() {
|
||||
this.$refs.usersTable.clearSelection()
|
||||
},
|
||||
getFirstLetter(str) {
|
||||
return str.charAt(0).toUpperCase()
|
||||
},
|
||||
|
@ -184,6 +216,9 @@ export default {
|
|||
this.$store.dispatch('SearchUsers', { query: searchQuery, page })
|
||||
}
|
||||
},
|
||||
handleSelectionChange(value) {
|
||||
this.$data.selectedUsers = value
|
||||
},
|
||||
showAdminAction({ local, id }) {
|
||||
return local && this.showDeactivatedButton(id)
|
||||
},
|
||||
|
@ -212,6 +247,10 @@ export default {
|
|||
margin: 7px 0 0 15px;
|
||||
}
|
||||
}
|
||||
.el-dropdown-link:hover {
|
||||
cursor: pointer;
|
||||
color: #409EFF;
|
||||
}
|
||||
.users-container {
|
||||
h1 {
|
||||
margin: 22px 0 0 15px;
|
||||
|
@ -231,7 +270,11 @@ export default {
|
|||
height: 36px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 22px 15px 22px 15px
|
||||
margin: 22px 15px 15px 15px
|
||||
}
|
||||
.user-count {
|
||||
color: gray;
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
@media
|
||||
|
@ -241,10 +284,6 @@ only screen and (max-width: 760px),
|
|||
h1 {
|
||||
margin: 7px 10px 7px 10px;
|
||||
}
|
||||
.el-dropdown-link {
|
||||
cursor: pointer;
|
||||
color: #409EFF;
|
||||
}
|
||||
.el-icon-arrow-down {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
|
331
test/views/users/dropdownMenu.test.js
Normal file
331
test/views/users/dropdownMenu.test.js
Normal file
|
@ -0,0 +1,331 @@
|
|||
import Vuex from 'vuex'
|
||||
import { mount, createLocalVue, config } from '@vue/test-utils'
|
||||
import Element from 'element-ui'
|
||||
import DropdownMenu from '@/views/users/components/DropdownMenu'
|
||||
import storeConfig from './store.conf'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import flushPromises from 'flush-promises'
|
||||
import { users } from '@/api/__mocks__/users.js'
|
||||
|
||||
config.mocks["$t"] = () => {}
|
||||
|
||||
const localVue = createLocalVue()
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Element)
|
||||
|
||||
jest.mock('@/api/users')
|
||||
|
||||
describe('Apply users actions to multiple users', () => {
|
||||
let store
|
||||
|
||||
beforeEach(async() => {
|
||||
store = new Vuex.Store(cloneDeep(storeConfig))
|
||||
store.dispatch('FetchUsers', { page: 1 })
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
it('grants admin rights to multiple users', async (done) => {
|
||||
const wrapper = mount(DropdownMenu, {
|
||||
store,
|
||||
localVue,
|
||||
sync: false,
|
||||
propsData: {
|
||||
selectedUsers: users
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const grantRightToMultipleUsersStub = jest.fn()
|
||||
wrapper.setMethods({ grantRightToMultipleUsers: grantRightToMultipleUsersStub })
|
||||
|
||||
wrapper.find(`.el-dropdown-menu__item:nth-child(1)`).trigger('click')
|
||||
expect(wrapper.vm.grantRightToMultipleUsers).toHaveBeenCalled()
|
||||
expect(wrapper.vm.grantRightToMultipleUsers).toHaveBeenCalledWith('admin')
|
||||
|
||||
const grantRight = wrapper.vm.mappers().grantRight
|
||||
const user1 = store.state.users.fetchedUsers[0]
|
||||
const user2 = store.state.users.fetchedUsers[1]
|
||||
const user3 = store.state.users.fetchedUsers[2]
|
||||
expect(user1.roles.admin).toBe(true)
|
||||
expect(user2.roles.admin).toBe(false)
|
||||
expect(user3.roles.admin).toBe(false)
|
||||
grantRight('admin')()
|
||||
await flushPromises()
|
||||
|
||||
const updatedUser1 = store.state.users.fetchedUsers[0]
|
||||
const updatedUser2 = store.state.users.fetchedUsers[1]
|
||||
const updatedUser3 = store.state.users.fetchedUsers[2]
|
||||
expect(updatedUser1.roles.admin).toBe(true)
|
||||
expect(updatedUser2.roles.admin).toBe(false)
|
||||
expect(updatedUser3.roles.admin).toBe(true)
|
||||
done()
|
||||
})
|
||||
|
||||
it('grants moderator rights to multiple users', async (done) => {
|
||||
const wrapper = mount(DropdownMenu, {
|
||||
store,
|
||||
localVue,
|
||||
sync: false,
|
||||
propsData: {
|
||||
selectedUsers: users
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const grantRightToMultipleUsersStub = jest.fn()
|
||||
wrapper.setMethods({ grantRightToMultipleUsers: grantRightToMultipleUsersStub })
|
||||
|
||||
wrapper.find(`.el-dropdown-menu__item:nth-child(3)`).trigger('click')
|
||||
expect(wrapper.vm.grantRightToMultipleUsers).toHaveBeenCalled()
|
||||
expect(wrapper.vm.grantRightToMultipleUsers).toHaveBeenCalledWith('moderator')
|
||||
|
||||
const grantRight = wrapper.vm.mappers().grantRight
|
||||
const user1 = store.state.users.fetchedUsers[0]
|
||||
const user2 = store.state.users.fetchedUsers[1]
|
||||
const user3 = store.state.users.fetchedUsers[2]
|
||||
expect(user1.roles.moderator).toBe(false)
|
||||
expect(user2.roles.moderator).toBe(false)
|
||||
expect(user3.roles.moderator).toBe(false)
|
||||
grantRight('moderator')()
|
||||
await flushPromises()
|
||||
|
||||
const updatedUser1 = store.state.users.fetchedUsers[0]
|
||||
const updatedUser2 = store.state.users.fetchedUsers[1]
|
||||
const updatedUser3 = store.state.users.fetchedUsers[2]
|
||||
expect(updatedUser1.roles.moderator).toBe(true)
|
||||
expect(updatedUser2.roles.moderator).toBe(false)
|
||||
expect(updatedUser3.roles.moderator).toBe(true)
|
||||
done()
|
||||
})
|
||||
|
||||
it('revokes admin rights from multiple users', async (done) => {
|
||||
const wrapper = mount(DropdownMenu, {
|
||||
store,
|
||||
localVue,
|
||||
sync: false,
|
||||
propsData: {
|
||||
selectedUsers: users
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const revokeRightFromMultipleUsersStub = jest.fn()
|
||||
wrapper.setMethods({ revokeRightFromMultipleUsers: revokeRightFromMultipleUsersStub })
|
||||
|
||||
wrapper.find(`.el-dropdown-menu__item:nth-child(2)`).trigger('click')
|
||||
expect(wrapper.vm.revokeRightFromMultipleUsers).toHaveBeenCalled()
|
||||
expect(wrapper.vm.revokeRightFromMultipleUsers).toHaveBeenCalledWith('admin')
|
||||
|
||||
const revokeRight = wrapper.vm.mappers().revokeRight
|
||||
const user1 = store.state.users.fetchedUsers[0]
|
||||
const user2 = store.state.users.fetchedUsers[2]
|
||||
expect(user1.roles.admin).toBe(true)
|
||||
expect(user2.roles.admin).toBe(false)
|
||||
revokeRight('admin')()
|
||||
await flushPromises()
|
||||
|
||||
const updatedUser1 = store.state.users.fetchedUsers[0]
|
||||
const updatedUser2 = store.state.users.fetchedUsers[2]
|
||||
expect(updatedUser1.roles.admin).toBe(false)
|
||||
expect(updatedUser2.roles.admin).toBe(false)
|
||||
done()
|
||||
})
|
||||
|
||||
it('calls a function that revokes moderator rights from multiple users', async (done) => {
|
||||
const wrapper = mount(DropdownMenu, {
|
||||
store,
|
||||
localVue,
|
||||
sync: false,
|
||||
propsData: {
|
||||
selectedUsers: users
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const revokeRightFromMultipleUsersStub = jest.fn()
|
||||
wrapper.setMethods({ revokeRightFromMultipleUsers: revokeRightFromMultipleUsersStub })
|
||||
|
||||
wrapper.find(`.el-dropdown-menu__item:nth-child(4)`).trigger('click')
|
||||
expect(wrapper.vm.revokeRightFromMultipleUsers).toHaveBeenCalled()
|
||||
expect(wrapper.vm.revokeRightFromMultipleUsers).toHaveBeenCalledWith('moderator')
|
||||
done()
|
||||
})
|
||||
|
||||
it('activates multiple accounts', async (done) => {
|
||||
const wrapper = mount(DropdownMenu, {
|
||||
store,
|
||||
localVue,
|
||||
sync: false,
|
||||
propsData: {
|
||||
selectedUsers: users
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const activateMultipleUsersStub = jest.fn()
|
||||
wrapper.setMethods({ activateMultipleUsers: activateMultipleUsersStub })
|
||||
|
||||
wrapper.find(`.el-dropdown-menu__item:nth-child(5)`).trigger('click')
|
||||
expect(wrapper.vm.activateMultipleUsers).toHaveBeenCalled()
|
||||
|
||||
const activate = wrapper.vm.mappers().activate
|
||||
const user = store.state.users.fetchedUsers[2]
|
||||
expect(user.deactivated).toBe(true)
|
||||
activate()
|
||||
await flushPromises()
|
||||
|
||||
const updatedUser = store.state.users.fetchedUsers[2]
|
||||
expect(updatedUser.deactivated).toBe(false)
|
||||
done()
|
||||
})
|
||||
|
||||
it('deactivates multiple accounts', async (done) => {
|
||||
const wrapper = mount(DropdownMenu, {
|
||||
store,
|
||||
localVue,
|
||||
sync: false,
|
||||
propsData: {
|
||||
selectedUsers: users
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const deactivateMultipleUsersStub = jest.fn()
|
||||
wrapper.setMethods({ deactivateMultipleUsers: deactivateMultipleUsersStub })
|
||||
|
||||
wrapper.find(`.el-dropdown-menu__item:nth-child(6)`).trigger('click')
|
||||
expect(wrapper.vm.deactivateMultipleUsers).toHaveBeenCalled()
|
||||
|
||||
const deactivate = wrapper.vm.mappers().deactivate
|
||||
const user1 = store.state.users.fetchedUsers[0]
|
||||
const user2 = store.state.users.fetchedUsers[1]
|
||||
expect(user1.deactivated).toBe(false)
|
||||
expect(user2.deactivated).toBe(false)
|
||||
deactivate()
|
||||
await flushPromises()
|
||||
|
||||
const updatedUser1 = store.state.users.fetchedUsers[0]
|
||||
const updatedUser2 = store.state.users.fetchedUsers[1]
|
||||
expect(updatedUser1.deactivated).toBe(true)
|
||||
expect(updatedUser2.deactivated).toBe(true)
|
||||
done()
|
||||
})
|
||||
|
||||
it('deletes multiple accounts', async (done) => {
|
||||
const wrapper = mount(DropdownMenu, {
|
||||
store,
|
||||
localVue,
|
||||
sync: false,
|
||||
propsData: {
|
||||
selectedUsers: users
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const deleteMultipleUsersStub = jest.fn()
|
||||
wrapper.setMethods({ deleteMultipleUsers: deleteMultipleUsersStub })
|
||||
|
||||
wrapper.find(`.el-dropdown-menu__item:nth-child(7)`).trigger('click')
|
||||
expect(wrapper.vm.deleteMultipleUsers).toHaveBeenCalled()
|
||||
|
||||
const remove = wrapper.vm.mappers().remove
|
||||
const user1 = store.state.users.fetchedUsers[0]
|
||||
const user2 = store.state.users.fetchedUsers[1]
|
||||
const user3 = store.state.users.fetchedUsers[2]
|
||||
expect(user1.deactivated).toBe(false)
|
||||
expect(user2.deactivated).toBe(false)
|
||||
expect(user3.deactivated).toBe(true)
|
||||
remove()
|
||||
await flushPromises()
|
||||
|
||||
const updatedUser1 = store.state.users.fetchedUsers[0]
|
||||
const updatedUser2 = store.state.users.fetchedUsers[1]
|
||||
const updatedUser3 = store.state.users.fetchedUsers[2]
|
||||
expect(updatedUser1.deactivated).toBe(true)
|
||||
expect(updatedUser2.deactivated).toBe(true)
|
||||
expect(updatedUser3.deactivated).toBe(true)
|
||||
done()
|
||||
})
|
||||
|
||||
it('applies tags for multiple accounts', async (done) => {
|
||||
const wrapper = mount(DropdownMenu, {
|
||||
store,
|
||||
localVue,
|
||||
sync: false,
|
||||
propsData: {
|
||||
selectedUsers: users
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const addTagForMultipleUsersStub = jest.fn()
|
||||
wrapper.setMethods({ addTagForMultipleUsers: addTagForMultipleUsersStub })
|
||||
|
||||
wrapper.find(`.el-dropdown-menu__item:nth-child(8) button:nth-child(1)`).trigger('click')
|
||||
expect(wrapper.vm.addTagForMultipleUsers).toHaveBeenCalled()
|
||||
expect(wrapper.vm.addTagForMultipleUsers).toHaveBeenCalledWith('force_nsfw')
|
||||
|
||||
wrapper.find(`.el-dropdown-menu__item:nth-child(10) button:nth-child(1)`).trigger('click')
|
||||
expect(wrapper.vm.addTagForMultipleUsers).toHaveBeenCalled()
|
||||
expect(wrapper.vm.addTagForMultipleUsers).toHaveBeenCalledWith('force_unlisted')
|
||||
|
||||
wrapper.find(`.el-dropdown-menu__item:nth-child(12) button:nth-child(1)`).trigger('click')
|
||||
expect(wrapper.vm.addTagForMultipleUsers).toHaveBeenCalled()
|
||||
expect(wrapper.vm.addTagForMultipleUsers).toHaveBeenCalledWith('disable_remote_subscription')
|
||||
|
||||
const addTag = wrapper.vm.mappers().addTag
|
||||
const user1 = store.state.users.fetchedUsers[0]
|
||||
const user2 = store.state.users.fetchedUsers[1]
|
||||
expect(user1.tags.length).toBe(0)
|
||||
expect(user2.tags.length).toBe(1)
|
||||
addTag('strip_media')()
|
||||
await flushPromises()
|
||||
|
||||
const updatedUser1 = store.state.users.fetchedUsers[0]
|
||||
const updatedUser2 = store.state.users.fetchedUsers[1]
|
||||
expect(updatedUser1.tags.length).toBe(1)
|
||||
expect(updatedUser2.tags.length).toBe(2)
|
||||
done()
|
||||
})
|
||||
|
||||
it('removes tags from multiple accounts', async (done) => {
|
||||
const wrapper = mount(DropdownMenu, {
|
||||
store,
|
||||
localVue,
|
||||
sync: false,
|
||||
propsData: {
|
||||
selectedUsers: users
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const removeTagFromMultipleUsersStub = jest.fn()
|
||||
wrapper.setMethods({ removeTagFromMultipleUsers: removeTagFromMultipleUsersStub })
|
||||
|
||||
wrapper.find(`.el-dropdown-menu__item:nth-child(9) button:nth-child(2)`).trigger('click')
|
||||
expect(wrapper.vm.removeTagFromMultipleUsers).toHaveBeenCalled()
|
||||
expect(wrapper.vm.removeTagFromMultipleUsers).toHaveBeenCalledWith('strip_media')
|
||||
|
||||
wrapper.find(`.el-dropdown-menu__item:nth-child(11) button:nth-child(2)`).trigger('click')
|
||||
expect(wrapper.vm.removeTagFromMultipleUsers).toHaveBeenCalled()
|
||||
expect(wrapper.vm.removeTagFromMultipleUsers).toHaveBeenCalledWith('sandbox')
|
||||
|
||||
wrapper.find(`.el-dropdown-menu__item:nth-child(13) button:nth-child(2)`).trigger('click')
|
||||
expect(wrapper.vm.removeTagFromMultipleUsers).toHaveBeenCalled()
|
||||
expect(wrapper.vm.removeTagFromMultipleUsers).toHaveBeenCalledWith('disable_any_subscription')
|
||||
|
||||
const removeTag = wrapper.vm.mappers().removeTag
|
||||
const user1 = store.state.users.fetchedUsers[1]
|
||||
const user2 = store.state.users.fetchedUsers[2]
|
||||
expect(user1.tags.length).toBe(1)
|
||||
expect(user2.tags.length).toBe(1)
|
||||
removeTag('strip_media')()
|
||||
await flushPromises()
|
||||
|
||||
const updatedUser1 = store.state.users.fetchedUsers[1]
|
||||
const updatedUser2 = store.state.users.fetchedUsers[2]
|
||||
expect(updatedUser1.tags.length).toBe(1)
|
||||
expect(updatedUser2.tags.length).toBe(0)
|
||||
done()
|
||||
})
|
||||
})
|
|
@ -1,5 +1,6 @@
|
|||
import Vuex from 'vuex'
|
||||
import { mount, createLocalVue, config } from '@vue/test-utils'
|
||||
import flushPromises from 'flush-promises'
|
||||
import Element from 'element-ui'
|
||||
import Users from '@/views/users/index'
|
||||
import storeConfig from './store.conf'
|
||||
|
@ -8,7 +9,6 @@ import { cloneDeep } from 'lodash'
|
|||
config.mocks["$t"] = () => {}
|
||||
config.stubs['users-filter'] = '<div />'
|
||||
|
||||
|
||||
const localVue = createLocalVue()
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Element)
|
||||
|
@ -25,10 +25,11 @@ describe('Search and filter users', () => {
|
|||
it('fetches initial list of users', async (done) => {
|
||||
const wrapper = mount(Users, {
|
||||
store,
|
||||
localVue
|
||||
localVue,
|
||||
sync: false
|
||||
})
|
||||
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
expect(wrapper.vm.usersCount).toEqual(3)
|
||||
done()
|
||||
})
|
||||
|
@ -36,24 +37,25 @@ describe('Search and filter users', () => {
|
|||
it('starts a search on input change', async (done) => {
|
||||
const wrapper = mount(Users, {
|
||||
store,
|
||||
localVue
|
||||
localVue,
|
||||
sync: false
|
||||
})
|
||||
|
||||
wrapper.vm.handleDebounceSearchInput = (query) => {
|
||||
store.dispatch('SearchUsers', { query, page: 1 })
|
||||
}
|
||||
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
expect(wrapper.vm.usersCount).toEqual(3)
|
||||
const input = wrapper.find('.search input.el-input__inner')
|
||||
input.element.value = 'bob'
|
||||
input.trigger('input')
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
expect(wrapper.vm.usersCount).toEqual(1)
|
||||
|
||||
input.element.value = ''
|
||||
input.trigger('input')
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
expect(wrapper.vm.usersCount).toEqual(3)
|
||||
|
||||
done()
|
||||
|
@ -72,18 +74,19 @@ describe('Users actions', () => {
|
|||
it('grants admin and moderator rights to a local user', async (done) => {
|
||||
const wrapper = mount(Users, {
|
||||
store,
|
||||
localVue
|
||||
localVue,
|
||||
sync: false
|
||||
})
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
const user = store.state.users.fetchedUsers[2]
|
||||
expect(user.roles.admin).toBe(false)
|
||||
expect(user.roles.moderator).toBe(false)
|
||||
|
||||
wrapper.find(htmlElement(3, 1)).trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
wrapper.find(htmlElement(3, 2)).trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
const updatedUser = store.state.users.fetchedUsers[2]
|
||||
expect(updatedUser.roles.admin).toBe(true)
|
||||
|
@ -94,9 +97,10 @@ describe('Users actions', () => {
|
|||
it('does not show actions that grant admin and moderator rights to external users', async (done) => {
|
||||
const wrapper = mount(Users, {
|
||||
store,
|
||||
localVue
|
||||
localVue,
|
||||
sync: false
|
||||
})
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
const dropdownMenuItems = wrapper.findAll(
|
||||
`.el-table__fixed-body-wrapper table tr:nth-child(2) ul.el-dropdown-menu li`
|
||||
|
@ -108,15 +112,16 @@ describe('Users actions', () => {
|
|||
it('toggles activation status', async (done) => {
|
||||
const wrapper = mount(Users, {
|
||||
store,
|
||||
localVue
|
||||
localVue,
|
||||
sync: false
|
||||
})
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
const user = store.state.users.fetchedUsers[1]
|
||||
expect(user.deactivated).toBe(false)
|
||||
|
||||
wrapper.find(htmlElement(2, 1)).trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
const updatedUser = store.state.users.fetchedUsers[1]
|
||||
expect(updatedUser.deactivated).toBe(true)
|
||||
|
@ -126,15 +131,16 @@ describe('Users actions', () => {
|
|||
it('deactivates user when Delete action is called', async (done) => {
|
||||
const wrapper = mount(Users, {
|
||||
store,
|
||||
localVue
|
||||
localVue,
|
||||
sync: false
|
||||
})
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
const user = store.state.users.fetchedUsers[1]
|
||||
expect(user.deactivated).toBe(false)
|
||||
|
||||
wrapper.find(htmlElement(2, 2)).trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
const updatedUser = store.state.users.fetchedUsers[1]
|
||||
expect(updatedUser.deactivated).toBe(true)
|
||||
|
@ -144,9 +150,10 @@ describe('Users actions', () => {
|
|||
it('adds tags', async (done) => {
|
||||
const wrapper = mount(Users, {
|
||||
store,
|
||||
localVue
|
||||
localVue,
|
||||
sync: false
|
||||
})
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
const user1 = store.state.users.fetchedUsers[0]
|
||||
const user2 = store.state.users.fetchedUsers[1]
|
||||
|
@ -154,9 +161,9 @@ describe('Users actions', () => {
|
|||
expect(user2.tags.length).toBe(1)
|
||||
|
||||
wrapper.find(htmlElement(1, 5)).trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
wrapper.find(htmlElement(2, 5)).trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
const updatedUser1 = store.state.users.fetchedUsers[0]
|
||||
const updatedUser2 = store.state.users.fetchedUsers[1]
|
||||
|
@ -168,15 +175,16 @@ describe('Users actions', () => {
|
|||
it('deletes tags', async (done) => {
|
||||
const wrapper = mount(Users, {
|
||||
store,
|
||||
localVue
|
||||
localVue,
|
||||
sync: false
|
||||
})
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
const user = store.state.users.fetchedUsers[1]
|
||||
expect(user.tags.length).toBe(1)
|
||||
|
||||
wrapper.find(htmlElement(2, 6)).trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
const updatedUser = store.state.users.fetchedUsers[1]
|
||||
expect(updatedUser.tags.length).toBe(0)
|
||||
|
@ -186,14 +194,15 @@ describe('Users actions', () => {
|
|||
it('shows check icon when tag is added', async (done) => {
|
||||
const wrapper = mount(Users, {
|
||||
store,
|
||||
localVue
|
||||
localVue,
|
||||
sync: false
|
||||
})
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.find(`${htmlElement(1, 5)} i`).exists()).toBe(false)
|
||||
|
||||
wrapper.find(htmlElement(1, 5)).trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.find(`${htmlElement(1, 5)} i`).exists()).toBe(true)
|
||||
done()
|
||||
|
@ -202,9 +211,10 @@ describe('Users actions', () => {
|
|||
it('does not change user index in array when tag is added', async (done) => {
|
||||
const wrapper = mount(Users, {
|
||||
store,
|
||||
localVue
|
||||
localVue,
|
||||
sync: false
|
||||
})
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
const firstUserNickname = store.state.users.fetchedUsers[0].nickname
|
||||
const secondUserNickname = store.state.users.fetchedUsers[1].nickname
|
||||
|
@ -212,7 +222,7 @@ describe('Users actions', () => {
|
|||
expect(secondUserNickname).toBe('bob')
|
||||
|
||||
wrapper.find(htmlElement(2, 5)).trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
|
||||
const firstUserNicknameAfterToggle = store.state.users.fetchedUsers[0].nickname
|
||||
const secondUserNicknameAfterToggle = store.state.users.fetchedUsers[1].nickname
|
||||
|
|
|
@ -7024,6 +7024,11 @@ number-is-nan@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
|
||||
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
|
||||
|
||||
numeral@^2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506"
|
||||
integrity sha1-StCAk21EPCVhrtnyGX7//iX05QY=
|
||||
|
||||
nwsapi@^2.0.7:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.1.1.tgz#08d6d75e69fd791bdea31507ffafe8c843b67e9c"
|
||||
|
|
Loading…
Reference in a new issue