mirror of
https://github.com/nextcloud/server.git
synced 2026-06-09 00:32:29 -04:00
Merge pull request #39073 from nextcloud/fix/setting_migrate-from-deprecated-ncpopovermenu
This commit is contained in:
commit
cb118d5bd6
16 changed files with 390 additions and 210 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1386,7 +1386,7 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
|
|||
// Scroll if too much groups
|
||||
&:not(.row--editable) {
|
||||
.groups,
|
||||
.subadmins,
|
||||
.subadmins,
|
||||
.subAdminsGroups {
|
||||
overflow: auto;
|
||||
max-height: 100%;
|
||||
|
|
@ -1395,7 +1395,7 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
|
|||
|
||||
.managers,
|
||||
.groups,
|
||||
.subadmins,
|
||||
.subadmins,
|
||||
.subAdminsGroups,
|
||||
.quota {
|
||||
min-width: $grid-col-min-width;
|
||||
|
|
@ -1569,50 +1569,14 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
|
|||
|
||||
&.userActions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
#newsubmit {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.toggleUserActions {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: var(--color-main-background);
|
||||
|
||||
.icon-more {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
opacity: .5;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus,
|
||||
&:hover,
|
||||
&:active {
|
||||
opacity: .7;
|
||||
background-color: var(--color-background-dark)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.feedback {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
transition: opacity 200ms ease-in-out;
|
||||
|
||||
.icon-checkmark {
|
||||
opacity: .5;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill the grid cell */
|
||||
.v-select.select-vue {
|
||||
min-width: 100%;
|
||||
width: 100%;
|
||||
// Make sure to cover whole row
|
||||
height: 100%;
|
||||
width: fit-content;
|
||||
padding-inline: 12px;
|
||||
background-color: var(--color-main-background);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@
|
|||
<!-- User full data -->
|
||||
<UserRowSimple v-else-if="!editing"
|
||||
:editing.sync="editing"
|
||||
:feedback-message="feedbackMessage"
|
||||
:groups="groups"
|
||||
:languages="languages"
|
||||
:loading="loading"
|
||||
|
|
@ -222,59 +221,41 @@
|
|||
</div>
|
||||
|
||||
<div class="userActions">
|
||||
<div v-if="!loading.all"
|
||||
class="toggleUserActions">
|
||||
<NcActions>
|
||||
<NcActionButton icon="icon-checkmark"
|
||||
:title="t('settings', 'Done')"
|
||||
:aria-label="t('settings', 'Done')"
|
||||
@click="handleDoneButton" />
|
||||
</NcActions>
|
||||
<div v-click-outside="hideMenu" class="userPopoverMenuWrapper">
|
||||
<button class="icon-more"
|
||||
:aria-expanded="openedMenu"
|
||||
:aria-label="t('settings', 'Toggle user actions menu')"
|
||||
@click.prevent="toggleMenu" />
|
||||
<div :class="{ 'open': openedMenu }" class="popovermenu">
|
||||
<NcPopoverMenu :menu="userActions" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :style="{opacity: feedbackMessage !== '' ? 1 : 0}"
|
||||
class="feedback">
|
||||
<div class="icon-checkmark" />
|
||||
{{ feedbackMessage }}
|
||||
</div>
|
||||
<UserRowActions v-if="!loading.all"
|
||||
:actions="userActions"
|
||||
:edit="true"
|
||||
@update:edit="toggleEdit" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { showSuccess, showError } from '@nextcloud/dialogs'
|
||||
|
||||
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
|
||||
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
|
||||
import ClickOutside from 'vue-click-outside'
|
||||
|
||||
import NcPopoverMenu from '@nextcloud/vue/dist/Components/NcPopoverMenu.js'
|
||||
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
|
||||
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
|
||||
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
|
||||
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
|
||||
import UserRowActions from './UserRowActions.vue'
|
||||
import UserRowSimple from './UserRowSimple.vue'
|
||||
import UserRowMixin from '../../mixins/UserRowMixin.js'
|
||||
import { showSuccess, showError } from '@nextcloud/dialogs'
|
||||
|
||||
export default {
|
||||
name: 'UserRow',
|
||||
|
||||
components: {
|
||||
UserRowSimple,
|
||||
NcPopoverMenu,
|
||||
NcActions,
|
||||
NcActionButton,
|
||||
NcSelect,
|
||||
NcTextField,
|
||||
UserRowActions,
|
||||
UserRowSimple,
|
||||
},
|
||||
|
||||
directives: {
|
||||
ClickOutside,
|
||||
},
|
||||
|
||||
mixins: [UserRowMixin],
|
||||
|
||||
props: {
|
||||
users: {
|
||||
type: Array,
|
||||
|
|
@ -325,7 +306,6 @@ export default {
|
|||
selectedQuota: false,
|
||||
rand: parseInt(Math.random() * 1000),
|
||||
openedMenu: false,
|
||||
feedbackMessage: '',
|
||||
possibleManagers: [],
|
||||
currentManager: '',
|
||||
editing: false,
|
||||
|
|
@ -348,8 +328,8 @@ export default {
|
|||
editedMail: this.user.email ?? '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
computed: {
|
||||
/* USER POPOVERMENU ACTIONS */
|
||||
userActions() {
|
||||
const actions = [
|
||||
|
|
@ -400,8 +380,10 @@ export default {
|
|||
return this.languages[0].languages.concat(this.languages[1].languages)
|
||||
},
|
||||
},
|
||||
|
||||
async beforeMount() {
|
||||
await this.searchUserManager()
|
||||
|
||||
if (this.user.manager) {
|
||||
await this.initManager(this.user.manager)
|
||||
}
|
||||
|
|
@ -432,13 +414,14 @@ export default {
|
|||
this.loading.wipe = true
|
||||
this.loading.all = true
|
||||
this.$store.dispatch('wipeUserDevices', userid)
|
||||
.then(() => {
|
||||
.then(() => showSuccess(t('settings', 'Wiped {userid}\'s devices', { userid })), { timeout: 2000 })
|
||||
.finally(() => {
|
||||
this.loading.wipe = false
|
||||
this.loading.all = false
|
||||
})
|
||||
}
|
||||
},
|
||||
true
|
||||
true,
|
||||
)
|
||||
},
|
||||
|
||||
|
|
@ -500,7 +483,7 @@ export default {
|
|||
})
|
||||
}
|
||||
},
|
||||
true
|
||||
true,
|
||||
)
|
||||
},
|
||||
|
||||
|
|
@ -778,19 +761,13 @@ export default {
|
|||
sendWelcomeMail() {
|
||||
this.loading.all = true
|
||||
this.$store.dispatch('sendWelcomeMail', this.user.id)
|
||||
.then(success => {
|
||||
if (success) {
|
||||
// Show feedback to indicate the success
|
||||
this.feedbackMessage = t('setting', 'Welcome mail sent!')
|
||||
setTimeout(() => {
|
||||
this.feedbackMessage = ''
|
||||
}, 2000)
|
||||
}
|
||||
.then(() => showSuccess(t('setting', 'Welcome mail sent!'), { timeout: 2000 }))
|
||||
.finally(() => {
|
||||
this.loading.all = false
|
||||
})
|
||||
},
|
||||
|
||||
handleDoneButton() {
|
||||
toggleEdit() {
|
||||
this.editing = false
|
||||
if (this.editedDisplayName !== this.user.displayname) {
|
||||
this.editedDisplayName = this.user.displayname
|
||||
|
|
@ -807,7 +784,12 @@ export default {
|
|||
z-index: 1 !important;
|
||||
}
|
||||
|
||||
.row :deep() {
|
||||
.row :deep() {
|
||||
.v-select.select {
|
||||
// reset min width to 100% instead of X px
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.mailAddress,
|
||||
.password,
|
||||
.displayName {
|
||||
|
|
|
|||
78
apps/settings/src/components/Users/UserRowActions.vue
Normal file
78
apps/settings/src/components/Users/UserRowActions.vue
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<template>
|
||||
<NcActions :aria-label="t('settings', 'Toggle user actions menu')"
|
||||
:inline="1">
|
||||
<NcActionButton @click="toggleEdit">
|
||||
{{ edit ? t('settings', 'Done') : t('settings', 'Edit') }}
|
||||
<template #icon>
|
||||
<NcIconSvgWrapper :svg="editSvg" aria-hidden="true" />
|
||||
</template>
|
||||
</NcActionButton>
|
||||
<NcActionButton v-for="(action, index) in actions"
|
||||
:key="index"
|
||||
:aria-label="action.text"
|
||||
:icon="action.icon"
|
||||
@click="action.action">
|
||||
{{ action.text }}
|
||||
</NcActionButton>
|
||||
</NcActions>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, defineComponent } from 'vue'
|
||||
|
||||
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
|
||||
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
|
||||
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
|
||||
import SvgCheck from '@mdi/svg/svg/check.svg?raw'
|
||||
import SvgPencil from '@mdi/svg/svg/pencil.svg?raw'
|
||||
|
||||
interface UserAction {
|
||||
action: (event: MouseEvent) => void,
|
||||
icon: string,
|
||||
text: string
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NcActionButton,
|
||||
NcActions,
|
||||
NcIconSvgWrapper,
|
||||
},
|
||||
|
||||
props: {
|
||||
/**
|
||||
* Array of user actions
|
||||
*/
|
||||
actions: {
|
||||
type: Array as PropType<readonly UserAction[]>,
|
||||
required: true,
|
||||
},
|
||||
|
||||
/**
|
||||
* The state whether the row is currently edited
|
||||
*/
|
||||
edit: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Current MDI logo to show for edit toggle
|
||||
*/
|
||||
editSvg() {
|
||||
return this.edit ? SvgCheck : SvgPencil
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Toggle edit mode by emitting the update event
|
||||
*/
|
||||
toggleEdit() {
|
||||
this.$emit('update:edit', !this.edit)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
@ -59,45 +59,26 @@
|
|||
{{ user.manager }}
|
||||
</div>
|
||||
<div class="userActions">
|
||||
<div v-if="canEdit && !loading.all" class="toggleUserActions">
|
||||
<NcActions>
|
||||
<NcActionButton icon="icon-rename"
|
||||
:title="t('settings', 'Edit User')"
|
||||
:aria-label="t('settings', 'Edit User')"
|
||||
@click="toggleEdit" />
|
||||
</NcActions>
|
||||
<div class="userPopoverMenuWrapper">
|
||||
<button v-click-outside="hideMenu"
|
||||
class="icon-more"
|
||||
:aria-expanded="openedMenu"
|
||||
:aria-label="t('settings', 'Toggle user actions menu')"
|
||||
@click.prevent="toggleMenu" />
|
||||
<div class="popovermenu" :class="{ 'open': openedMenu }">
|
||||
<NcPopoverMenu :menu="userActions" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feedback" :style="{opacity: feedbackMessage !== '' ? 1 : 0}">
|
||||
<div class="icon-checkmark" />
|
||||
{{ feedbackMessage }}
|
||||
</div>
|
||||
<UserRowActions v-if="canEdit && !loading.all"
|
||||
:actions="userActions"
|
||||
:edit="false"
|
||||
@update:edit="toggleEdit" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NcPopoverMenu from '@nextcloud/vue/dist/Components/NcPopoverMenu.js'
|
||||
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
|
||||
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
|
||||
import ClickOutside from 'vue-click-outside'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
|
||||
import ClickOutside from 'vue-click-outside'
|
||||
|
||||
import UserRowActions from './UserRowActions.vue'
|
||||
import UserRowMixin from '../../mixins/UserRowMixin.js'
|
||||
|
||||
export default {
|
||||
name: 'UserRowSimple',
|
||||
components: {
|
||||
NcPopoverMenu,
|
||||
NcActionButton,
|
||||
NcActions,
|
||||
UserRowActions,
|
||||
},
|
||||
directives: {
|
||||
ClickOutside,
|
||||
|
|
@ -124,10 +105,6 @@ export default {
|
|||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
feedbackMessage: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
subAdminsGroups: {
|
||||
type: Array,
|
||||
required: true,
|
||||
|
|
|
|||
|
|
@ -25,9 +25,8 @@ import { User } from '@nextcloud/cypress'
|
|||
const admin = new User('admin', 'admin')
|
||||
const jdoe = new User('jdoe', 'jdoe')
|
||||
|
||||
describe('Setting: Users list', function() {
|
||||
describe('Settings: Create and delete users', function() {
|
||||
before(function() {
|
||||
cy.createUser(jdoe)
|
||||
cy.login(admin)
|
||||
})
|
||||
|
||||
|
|
@ -35,48 +34,26 @@ describe('Setting: Users list', function() {
|
|||
cy.deleteUser(jdoe)
|
||||
})
|
||||
|
||||
it('Can change the password', function() {
|
||||
it('Can delete a user', function() {
|
||||
// ensure user exists
|
||||
cy.createUser(jdoe).login(admin)
|
||||
|
||||
// open the User settings
|
||||
cy.visit('/settings/users')
|
||||
|
||||
cy.get(`.user-list-grid .row[data-id="${jdoe.userId}"]`).within(($row) => {
|
||||
// see that the user is in the list
|
||||
cy.get(`.user-list-grid .row[data-id="${jdoe.userId}"]`).within(() => {
|
||||
// see that the list of users contains the user jdoe
|
||||
cy.contains(jdoe.userId).should('exist')
|
||||
// toggle the edit mode for the user jdoe
|
||||
cy.get('.userActions button .icon-rename').click()
|
||||
// open the actions menu for the user
|
||||
cy.get('.userActions button.action-item__menutoggle').click()
|
||||
})
|
||||
|
||||
cy.get(`.user-list-grid .row[data-id="${jdoe.userId}"]`).within(($row) => {
|
||||
// see that the edit mode is on
|
||||
cy.wrap($row).should('have.class', 'row--editable')
|
||||
// see that the password of user0 is ""
|
||||
cy.get('input[type="password"]').should('exist').and('have.value', '')
|
||||
// set the password for user0 to 123456
|
||||
cy.get('input[type="password"]').type('123456')
|
||||
// When I set the password for user0 to 123456
|
||||
cy.get('input[type="password"]').should('have.value', '123456')
|
||||
cy.get('.password button').click()
|
||||
|
||||
// Ignore failure if modal is not shown
|
||||
cy.once('fail', (error) => {
|
||||
expect(error.name).to.equal('AssertionError')
|
||||
expect(error).to.have.property('node', '.modal-container')
|
||||
})
|
||||
// Make sure no confirmation modal is shown
|
||||
cy.root().closest('body').find('.modal-container').then(($modal) => {
|
||||
if ($modal.length > 0) {
|
||||
cy.wrap($modal).find('input[type="password"]').type(admin.password)
|
||||
cy.wrap($modal).find('button').contains('Confirm').click()
|
||||
}
|
||||
})
|
||||
|
||||
// see that the password cell for user user0 is done loading
|
||||
cy.get('.user-row-text-field.icon-loading-small').should('exist')
|
||||
cy.waitUntil(() => cy.get('.user-row-text-field.icon-loading-small').should('not.exist'), { timeout: 10000 })
|
||||
// password input is emptied on change
|
||||
cy.get('input[type="password"]').should('have.value', '')
|
||||
})
|
||||
// Success message is shown
|
||||
cy.get('.toastify.toast-success').contains(/Password.+successfully.+changed/i).should('exist')
|
||||
// The "Delete user" action in the actions menu is shown and clicked
|
||||
cy.get('.action-item__popper .action').contains('Delete user').should('exist').click()
|
||||
// And confirmation dialog accepted
|
||||
cy.get('.oc-dialog button').contains(`Delete ${jdoe.userId}`).click()
|
||||
// deleted clicked the user is not shown anymore
|
||||
cy.get(`.user-list-grid .row[data-id="${jdoe.userId}"]`).should('not.exist')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
89
cypress/e2e/settings/users_disable.cy.ts
Normal file
89
cypress/e2e/settings/users_disable.cy.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2023 Ferdinand Thiessen <opensource@fthiessen.de>
|
||||
*
|
||||
* @author Ferdinand Thiessen <opensource@fthiessen.de>
|
||||
*
|
||||
* @license AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
import { User } from '@nextcloud/cypress'
|
||||
|
||||
const admin = new User('admin', 'admin')
|
||||
const jdoe = new User('jdoe', 'jdoe')
|
||||
|
||||
describe('Settings: Disable and enable users', function() {
|
||||
before(function() {
|
||||
cy.createUser(jdoe)
|
||||
cy.login(admin)
|
||||
})
|
||||
|
||||
after(() => {
|
||||
cy.deleteUser(jdoe)
|
||||
})
|
||||
|
||||
it('Can disable the user', function() {
|
||||
// ensure user is enabled
|
||||
cy.enableUser(jdoe)
|
||||
// open the User settings
|
||||
cy.visit('/settings/users')
|
||||
// see that the user is in the list of active users
|
||||
cy.get(`.user-list-grid .row[data-id="${jdoe.userId}"]`).within(() => {
|
||||
// see that the list of users contains the user jdoe
|
||||
cy.contains(jdoe.userId).should('exist')
|
||||
// open the actions menu for the user
|
||||
cy.get('.userActions button.action-item__menutoggle').click()
|
||||
})
|
||||
|
||||
// The "Disable user" action in the actions menu is shown and clicked
|
||||
cy.get('.action-item__popper .action').contains('Disable user').should('exist').click()
|
||||
// When clicked the section is not shown anymore
|
||||
cy.get(`.user-list-grid .row[data-id="${jdoe.userId}"]`).should('not.exist')
|
||||
// But the disabled user section now exists
|
||||
cy.get('#disabled').should('exist')
|
||||
// Open disabled users section
|
||||
cy.get('#disabled a').click()
|
||||
cy.url().should('match', /\/disabled/)
|
||||
// The list of disabled users should now contain the user
|
||||
cy.get(`.user-list-grid .row[data-id="${jdoe.userId}"]`).should('exist')
|
||||
})
|
||||
|
||||
it('Can enable the user', function() {
|
||||
// ensure user is disabled
|
||||
cy.enableUser(jdoe, false)
|
||||
// open the User settings
|
||||
cy.visit('/settings/users')
|
||||
|
||||
// Open disabled users section
|
||||
cy.get('#disabled a').click()
|
||||
cy.url().should('match', /\/disabled/)
|
||||
|
||||
cy.get(`.user-list-grid .row[data-id="${jdoe.userId}"]`).within(() => {
|
||||
// see that the list of disabled users contains the user jdoe
|
||||
cy.contains(jdoe.userId).should('exist')
|
||||
// open the actions menu for the user
|
||||
cy.get('.userActions button.action-item__menutoggle').click()
|
||||
})
|
||||
|
||||
// The "Enable user" action in the actions menu is shown and clicked
|
||||
cy.get('.action-item__popper .action').contains('Enable user').should('exist').click()
|
||||
// When clicked the section is not shown anymore
|
||||
cy.get('#disabled').should('not.exist')
|
||||
// Make sure it is still gone after the reload reload
|
||||
cy.reload().login(admin)
|
||||
cy.get('#disabled').should('not.exist')
|
||||
})
|
||||
})
|
||||
82
cypress/e2e/settings/users_modify.cy.ts
Normal file
82
cypress/e2e/settings/users_modify.cy.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2023 Ferdinand Thiessen <opensource@fthiessen.de>
|
||||
*
|
||||
* @author Ferdinand Thiessen <opensource@fthiessen.de>
|
||||
*
|
||||
* @license AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
import { User } from '@nextcloud/cypress'
|
||||
|
||||
const admin = new User('admin', 'admin')
|
||||
const jdoe = new User('jdoe', 'jdoe')
|
||||
|
||||
describe('Settings: Change user properties', function() {
|
||||
before(function() {
|
||||
cy.createUser(jdoe)
|
||||
cy.login(admin)
|
||||
})
|
||||
|
||||
after(() => {
|
||||
cy.deleteUser(jdoe)
|
||||
})
|
||||
|
||||
it('Can change the password', function() {
|
||||
// open the User settings
|
||||
cy.visit('/settings/users')
|
||||
|
||||
cy.get(`.user-list-grid .row[data-id="${jdoe.userId}"]`).within(($row) => {
|
||||
// see that the list of users contains the user jdoe
|
||||
cy.contains(jdoe.userId).should('exist')
|
||||
// toggle the edit mode for the user jdoe
|
||||
cy.get('.userActions .action-items > button:first-of-type').click()
|
||||
})
|
||||
|
||||
cy.get(`.user-list-grid .row[data-id="${jdoe.userId}"]`).within(($row) => {
|
||||
// see that the edit mode is on
|
||||
cy.wrap($row).should('have.class', 'row--editable')
|
||||
// see that the password of user0 is ""
|
||||
cy.get('input[type="password"]').should('exist').and('have.value', '')
|
||||
// set the password for user0 to 123456
|
||||
cy.get('input[type="password"]').type('123456')
|
||||
// When I set the password for user0 to 123456
|
||||
cy.get('input[type="password"]').should('have.value', '123456')
|
||||
cy.get('.password button').click()
|
||||
|
||||
// Ignore failure if modal is not shown
|
||||
cy.once('fail', (error) => {
|
||||
expect(error.name).to.equal('AssertionError')
|
||||
expect(error).to.have.property('node', '.modal-container')
|
||||
})
|
||||
// Make sure no confirmation modal is shown
|
||||
cy.root().closest('body').find('.modal-container').then(($modal) => {
|
||||
if ($modal.length > 0) {
|
||||
cy.wrap($modal).find('input[type="password"]').type(admin.password)
|
||||
cy.wrap($modal).find('button').contains('Confirm').click()
|
||||
}
|
||||
})
|
||||
|
||||
// see that the password cell for user user0 is done loading
|
||||
cy.get('.user-row-text-field.icon-loading-small').should('exist')
|
||||
cy.waitUntil(() => cy.get('.user-row-text-field.icon-loading-small').should('not.exist'), { timeout: 10000 })
|
||||
// password input is emptied on change
|
||||
cy.get('input[type="password"]').should('have.value', '')
|
||||
})
|
||||
// Success message is shown
|
||||
cy.get('.toastify.toast-success').contains(/Password.+successfully.+changed/i).should('exist')
|
||||
})
|
||||
})
|
||||
|
|
@ -33,6 +33,11 @@ declare global {
|
|||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable<Subject = any> {
|
||||
/**
|
||||
* Enable or disable a given user
|
||||
*/
|
||||
enableUser(user: User, enable?: boolean): Cypress.Chainable<Cypress.Response<any>>,
|
||||
|
||||
/**
|
||||
* Upload a file from the fixtures folder to a given user storage.
|
||||
* **Warning**: Using this function will reset the previous session
|
||||
|
|
@ -69,6 +74,33 @@ declare global {
|
|||
const url = (Cypress.config('baseUrl') || '').replace(/\/index.php\/?$/g, '')
|
||||
Cypress.env('baseUrl', url)
|
||||
|
||||
/**
|
||||
* Enable or disable a user
|
||||
* TODO: standardise in @nextcloud/cypress
|
||||
*
|
||||
* @param {User} user the user to dis- / enable
|
||||
* @param {boolean} enable True if the user should be enable, false to disable
|
||||
*/
|
||||
Cypress.Commands.add('enableUser', (user: User, enable = true) => {
|
||||
const url = `${Cypress.config('baseUrl')}/ocs/v2.php/cloud/users/${user.userId}/${enable ? 'enable' : 'disable'}`.replace('index.php/', '')
|
||||
return cy.request({
|
||||
method: 'PUT',
|
||||
url,
|
||||
form: true,
|
||||
auth: {
|
||||
user: 'admin',
|
||||
password: 'admin',
|
||||
},
|
||||
headers: {
|
||||
'OCS-ApiRequest': 'true',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
}).then((response) => {
|
||||
cy.log(`Enabled user ${user}`, response.status)
|
||||
return cy.wrap(response)
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* cy.uploadedFile - uploads a file from the fixtures folder
|
||||
* TODO: standardise in @nextcloud/cypress
|
||||
|
|
|
|||
4
dist/settings-users-8351.js
vendored
4
dist/settings-users-8351.js
vendored
File diff suppressed because one or more lines are too long
2
dist/settings-users-8351.js.map
vendored
2
dist/settings-users-8351.js.map
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -125,7 +125,7 @@ class UsersSettingsContext implements Context, ActorAwareInterface {
|
|||
* @return Locator
|
||||
*/
|
||||
public static function actionsMenuOf($user) {
|
||||
return Locator::forThe()->css(".icon-more")->
|
||||
return Locator::forThe()->css(".userActions .action-item:not(.action-item--single)")->
|
||||
descendantOf(self::rowForUser($user))->
|
||||
describedAs("Actions menu for user $user in Users Settings");
|
||||
}
|
||||
|
|
@ -134,8 +134,7 @@ class UsersSettingsContext implements Context, ActorAwareInterface {
|
|||
* @return Locator
|
||||
*/
|
||||
public static function theAction($action, $user) {
|
||||
return Locator::forThe()->xpath("//button[normalize-space() = '$action']")->
|
||||
descendantOf(self::rowForUser($user))->
|
||||
return Locator::forThe()->xpath("//button[@aria-label = normalize-space('$action')]")->
|
||||
describedAs("$action action for the user $user row in Users Settings");
|
||||
}
|
||||
|
||||
|
|
@ -160,7 +159,7 @@ class UsersSettingsContext implements Context, ActorAwareInterface {
|
|||
* @return Locator
|
||||
*/
|
||||
public static function editModeToggle($user) {
|
||||
return Locator::forThe()->css(".toggleUserActions button")->
|
||||
return Locator::forThe()->css(".userActions .action-items button:first-of-type")->
|
||||
descendantOf(self::rowForUser($user))->
|
||||
describedAs("The edit toggle button for the user $user in Users Settings");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,42 +22,42 @@ Feature: users
|
|||
Then I see that the list of users contains the user "test"
|
||||
# And I see that the display name for the user "test" is "Test display name"
|
||||
|
||||
Scenario: delete a user
|
||||
Given I act as Jane
|
||||
And I am logged in as the admin
|
||||
And I open the User settings
|
||||
And I see that the list of users contains the user user0
|
||||
And I open the actions menu for the user user0
|
||||
And I see that the "Delete user" action in the user0 actions menu is shown
|
||||
When I click the "Delete user" action in the user0 actions menu
|
||||
And I click the "Delete user0's account" button of the confirmation dialog
|
||||
Then I see that the list of users does not contains the user user0
|
||||
# Scenario: delete a user
|
||||
# Given I act as Jane
|
||||
# And I am logged in as the admin
|
||||
# And I open the User settings
|
||||
# And I see that the list of users contains the user user0
|
||||
# And I open the actions menu for the user user0
|
||||
# And I see that the "Delete user" action in the user0 actions menu is shown
|
||||
# When I click the "Delete user" action in the user0 actions menu
|
||||
# And I click the "Delete user0's account" button of the confirmation dialog
|
||||
# Then I see that the list of users does not contains the user user0
|
||||
|
||||
Scenario: disable a user
|
||||
Given I act as Jane
|
||||
And I am logged in as the admin
|
||||
And I open the User settings
|
||||
And I see that the list of users contains the user user0
|
||||
And I open the actions menu for the user user0
|
||||
And I see that the "Disable user" action in the user0 actions menu is shown
|
||||
When I click the "Disable user" action in the user0 actions menu
|
||||
Then I see that the list of users does not contains the user user0
|
||||
When I open the "Disabled users" section
|
||||
Then I see that the list of users contains the user user0
|
||||
# Scenario: disable a user
|
||||
# Given I act as Jane
|
||||
# And I am logged in as the admin
|
||||
# And I open the User settings
|
||||
# And I see that the list of users contains the user user0
|
||||
# And I open the actions menu for the user user0
|
||||
# And I see that the "Disable user" action in the user0 actions menu is shown
|
||||
# When I click the "Disable user" action in the user0 actions menu
|
||||
# Then I see that the list of users does not contains the user user0
|
||||
# When I open the "Disabled users" section
|
||||
# Then I see that the list of users contains the user user0
|
||||
|
||||
Scenario: users navigation without disabled users
|
||||
Given I act as Jane
|
||||
And I am logged in as the admin
|
||||
And I open the User settings
|
||||
And I open the "Disabled users" section
|
||||
And I see that the list of users contains the user disabledUser
|
||||
And I open the actions menu for the user disabledUser
|
||||
And I see that the "Enable user" action in the disabledUser actions menu is shown
|
||||
When I click the "Enable user" action in the disabledUser actions menu
|
||||
Then I see that the section "Disabled users" is not shown
|
||||
# check again after reloading the settings
|
||||
When I open the User settings
|
||||
Then I see that the section "Disabled users" is not shown
|
||||
# Scenario: users navigation without disabled users
|
||||
# Given I act as Jane
|
||||
# And I am logged in as the admin
|
||||
# And I open the User settings
|
||||
# And I open the "Disabled users" section
|
||||
# And I see that the list of users contains the user disabledUser
|
||||
# And I open the actions menu for the user disabledUser
|
||||
# And I see that the "Enable user" action in the disabledUser actions menu is shown
|
||||
# When I click the "Enable user" action in the disabledUser actions menu
|
||||
# Then I see that the section "Disabled users" is not shown
|
||||
# # check again after reloading the settings
|
||||
# When I open the User settings
|
||||
# Then I see that the section "Disabled users" is not shown
|
||||
|
||||
Scenario: assign user to a group
|
||||
Given I act as Jane
|
||||
|
|
|
|||
Loading…
Reference in a new issue