aboutsummaryrefslogtreecommitdiffstats
path: root/cypress/e2e/settings
diff options
context:
space:
mode:
Diffstat (limited to 'cypress/e2e/settings')
-rw-r--r--cypress/e2e/settings/access-levels.cy.ts29
-rw-r--r--cypress/e2e/settings/apps.cy.ts30
-rw-r--r--cypress/e2e/settings/personal-info.cy.ts121
-rw-r--r--cypress/e2e/settings/users-group-admin.cy.ts186
-rw-r--r--cypress/e2e/settings/users.cy.ts32
-rw-r--r--cypress/e2e/settings/usersUtils.ts21
-rw-r--r--cypress/e2e/settings/users_columns.cy.ts21
-rw-r--r--cypress/e2e/settings/users_disable.cy.ts21
-rw-r--r--cypress/e2e/settings/users_groups.cy.ts91
-rw-r--r--cypress/e2e/settings/users_manager.cy.ts121
-rw-r--r--cypress/e2e/settings/users_modify.cy.ts64
11 files changed, 474 insertions, 263 deletions
diff --git a/cypress/e2e/settings/access-levels.cy.ts b/cypress/e2e/settings/access-levels.cy.ts
index ac02d607de2..4bf0cbc1832 100644
--- a/cypress/e2e/settings/access-levels.cy.ts
+++ b/cypress/e2e/settings/access-levels.cy.ts
@@ -1,23 +1,6 @@
/**
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { User } from '@nextcloud/cypress'
@@ -40,7 +23,9 @@ describe('Settings: Ensure only administrator can see the administration setting
// I open the settings menu
getNextcloudUserMenuToggle().click()
// I navigate to the settings panel
- getNextcloudUserMenu().find('#settings a').click()
+ getNextcloudUserMenu()
+ .findByRole('link', { name: /settings/i })
+ .click()
cy.url().should('match', /\/settings\/user$/)
cy.get('#app-navigation').should('be.visible').within(() => {
@@ -62,7 +47,9 @@ describe('Settings: Ensure only administrator can see the administration setting
// I open the settings menu
getNextcloudUserMenuToggle().click()
// I navigate to the settings panel
- getNextcloudUserMenu().find('#settings a').click()
+ getNextcloudUserMenu()
+ .findByRole('link', { name: /Personal settings/i })
+ .click()
cy.url().should('match', /\/settings\/user$/)
cy.get('#app-navigation').should('be.visible').within(() => {
diff --git a/cypress/e2e/settings/apps.cy.ts b/cypress/e2e/settings/apps.cy.ts
index c1ef24951a9..0df073271ef 100644
--- a/cypress/e2e/settings/apps.cy.ts
+++ b/cypress/e2e/settings/apps.cy.ts
@@ -1,23 +1,6 @@
/**
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { User } from '@nextcloud/cypress'
@@ -34,8 +17,15 @@ describe('Settings: App management', { testIsolation: true }, () => {
// I am logged in as the admin
cy.login(admin)
+
+ // Intercept the apps list request
+ cy.intercept('GET', '*/settings/apps/list').as('fetchAppsList')
+
// I open the Apps management
cy.visit('/settings/apps/installed')
+
+ // Wait for the apps list to load
+ cy.wait('@fetchAppsList')
})
it('Can enable an installed app', () => {
@@ -133,7 +123,7 @@ describe('Settings: App management', { testIsolation: true }, () => {
cy.get('#app-category-your-bundles').find('.active').should('exist')
// I see the app bundles
cy.get('#apps-list').contains('tr', 'Enterprise bundle')
- cy.get('#apps-list').contains('tr', 'Education Edition')
+ cy.get('#apps-list').contains('tr', 'Education bundle')
// I see that the "Enterprise bundle" is disabled
cy.get('#apps-list').contains('tr', 'Enterprise bundle').contains('button', 'Download and enable all')
})
diff --git a/cypress/e2e/settings/personal-info.cy.ts b/cypress/e2e/settings/personal-info.cy.ts
index a7564d5125e..8d4b4bb606a 100644
--- a/cypress/e2e/settings/personal-info.cy.ts
+++ b/cypress/e2e/settings/personal-info.cy.ts
@@ -1,23 +1,6 @@
/**
- * @copyright Copyright (c) 2024 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/>.
- *
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { User } from '@nextcloud/cypress'
@@ -115,33 +98,55 @@ const checkSettingsVisibility = (property: string, defaultVisibility: Visibility
}) */
}
-const genericProperties = ['Location', 'X (formerly Twitter)', 'Fediverse']
+const genericProperties = [
+ ['Location', 'Berlin'],
+ ['X (formerly Twitter)', 'nextclouders'],
+ ['Fediverse', 'nextcloud@mastodon.xyz'],
+]
const nonfederatedProperties = ['Organisation', 'Role', 'Headline', 'About']
describe('Settings: Change personal information', { testIsolation: true }, () => {
+ let snapshot: string = ''
before(() => {
+ // make sure the fediverse check does not do http requests
+ cy.runOccCommand('config:system:set has_internet_connection --type bool --value false')
// ensure we can set locale and language
cy.runOccCommand('config:system:delete force_language')
cy.runOccCommand('config:system:delete force_locale')
+ cy.createRandomUser().then(($user) => {
+ user = $user
+ cy.modifyUser(user, 'language', 'en')
+ cy.modifyUser(user, 'locale', 'en_US')
+
+ // Make sure the user is logged in at least once
+ // before the snapshot is taken to speed up the tests
+ cy.login(user)
+ cy.visit('/settings/user')
+
+ cy.saveState().then(($snapshot) => {
+ snapshot = $snapshot
+ })
+ })
})
after(() => {
+ cy.runOccCommand('config:system:delete has_internet_connection')
+
cy.runOccCommand('config:system:set force_language --value en')
cy.runOccCommand('config:system:set force_locale --value en_US')
})
beforeEach(() => {
- cy.createRandomUser().then(($user) => {
- user = $user
- cy.modifyUser(user, 'language', 'en')
- cy.modifyUser(user, 'locale', 'en_US')
- cy.login($user)
- cy.visit('/settings/user')
- })
+ cy.login(user)
+ cy.visit('/settings/user')
cy.intercept('PUT', /ocs\/v2.php\/cloud\/users\//).as('submitSetting')
})
+ afterEach(() => {
+ cy.restoreState(snapshot)
+ })
+
it('Can dis- and enable the profile', () => {
cy.visit(`/u/${user.userId}`)
cy.contains('h2', user.userId).should('be.visible')
@@ -149,6 +154,7 @@ describe('Settings: Change personal information', { testIsolation: true }, () =>
cy.visit('/settings/user')
cy.contains('Enable profile').click()
handlePasswordConfirmation(user.password)
+ cy.wait('@submitSetting')
cy.visit(`/u/${user.userId}`, { failOnStatusCode: false })
cy.contains('h2', 'Profile not found').should('be.visible')
@@ -156,6 +162,7 @@ describe('Settings: Change personal information', { testIsolation: true }, () =>
cy.visit('/settings/user')
cy.contains('Enable profile').click()
handlePasswordConfirmation(user.password)
+ cy.wait('@submitSetting')
cy.visit(`/u/${user.userId}`, { failOnStatusCode: false })
cy.contains('h2', user.userId).should('be.visible')
@@ -329,6 +336,57 @@ describe('Settings: Change personal information', { testIsolation: true }, () =>
cy.get('a[href="tel:+498972101099701"]').should('be.visible')
})
+ it('Can set phone number with phone region', () => {
+ cy.contains('label', 'Phone number').scrollIntoView()
+ inputForLabel('Phone number').type('{selectAll}0 40 428990')
+ inputForLabel('Phone number').should('have.attr', 'class').and('contain', '--error')
+
+ cy.runOccCommand('config:system:set default_phone_region --value DE')
+ cy.reload()
+
+ cy.contains('label', 'Phone number').scrollIntoView()
+ inputForLabel('Phone number').type('{selectAll}0 40 428990')
+ handlePasswordConfirmation(user.password)
+
+ cy.wait('@submitSetting')
+ cy.reload()
+ inputForLabel('Phone number').should('have.value', '+4940428990')
+ })
+
+ it('Can reset phone number', () => {
+ cy.contains('label', 'Phone number').scrollIntoView()
+ inputForLabel('Phone number').type('{selectAll}+49 40 428990')
+ handlePasswordConfirmation(user.password)
+
+ cy.wait('@submitSetting')
+ cy.reload()
+ inputForLabel('Phone number').should('have.value', '+4940428990')
+
+ inputForLabel('Phone number').clear()
+ handlePasswordConfirmation(user.password)
+
+ cy.wait('@submitSetting')
+ cy.reload()
+ inputForLabel('Phone number').should('have.value', '')
+ })
+
+ it('Can reset social media property', () => {
+ cy.contains('label', 'Fediverse').scrollIntoView()
+ inputForLabel('Fediverse').type('{selectAll}@nextcloud@mastodon.social')
+ handlePasswordConfirmation(user.password)
+
+ cy.wait('@submitSetting')
+ cy.reload()
+ inputForLabel('Fediverse').should('have.value', 'nextcloud@mastodon.social')
+
+ inputForLabel('Fediverse').clear()
+ handlePasswordConfirmation(user.password)
+
+ cy.wait('@submitSetting')
+ cy.reload()
+ inputForLabel('Fediverse').should('have.value', '')
+ })
+
it('Can set Website and change its visibility', () => {
cy.contains('label', 'Website').scrollIntoView()
// Check invalid input
@@ -350,22 +408,21 @@ describe('Settings: Change personal information', { testIsolation: true }, () =>
})
// Check generic properties that allow any visibility and any value
- genericProperties.forEach((property) => {
+ genericProperties.forEach(([property, value]) => {
it(`Can set ${property} and change its visibility`, () => {
- const uniqueValue = `${property.toUpperCase()} ${property.toLowerCase()}`
cy.contains('label', property).scrollIntoView()
- inputForLabel(property).type(uniqueValue)
+ inputForLabel(property).type(value)
handlePasswordConfirmation(user.password)
cy.wait('@submitSetting')
cy.reload()
- inputForLabel(property).should('have.value', uniqueValue)
+ inputForLabel(property).should('have.value', value)
checkSettingsVisibility(property)
// check it is visible on the profile
cy.visit(`/u/${user.userId}`)
- cy.contains(uniqueValue).should('be.visible')
+ cy.contains(value).should('be.visible')
})
})
diff --git a/cypress/e2e/settings/users-group-admin.cy.ts b/cypress/e2e/settings/users-group-admin.cy.ts
new file mode 100644
index 00000000000..5b5dcfd33a8
--- /dev/null
+++ b/cypress/e2e/settings/users-group-admin.cy.ts
@@ -0,0 +1,186 @@
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+/// <reference types="cypress-if" />
+import { User } from '@nextcloud/cypress'
+import { getUserListRow, handlePasswordConfirmation } from './usersUtils'
+// eslint-disable-next-line n/no-extraneous-import
+import randomString from 'crypto-random-string'
+
+const admin = new User('admin', 'admin')
+const john = new User('john', '123456')
+
+/**
+ * Make a user subadmin of a group.
+ *
+ * @param user - The user to make subadmin
+ * @param group - The group the user should be subadmin of
+ */
+function makeSubAdmin(user: User, group: string): void {
+ cy.request({
+ url: `${Cypress.config('baseUrl')!.replace('/index.php', '')}/ocs/v2.php/cloud/users/${user.userId}/subadmins`,
+ method: 'POST',
+ auth: {
+ user: admin.userId,
+ password: admin.userId,
+ },
+ headers: {
+ 'OCS-ApiRequest': 'true',
+ },
+ body: {
+ groupid: group,
+ },
+ })
+}
+
+describe('Settings: Create accounts as a group admin', function() {
+
+ let subadmin: User
+ let group: string
+
+ beforeEach(() => {
+ group = randomString(7)
+ cy.deleteUser(john)
+ cy.createRandomUser().then((user) => {
+ subadmin = user
+ cy.runOccCommand(`group:add '${group}'`)
+ cy.runOccCommand(`group:adduser '${group}' '${subadmin.userId}'`)
+ makeSubAdmin(subadmin, group)
+ })
+ })
+
+ it('Can create a user with prefilled single group', () => {
+ cy.login(subadmin)
+ // open the User settings
+ cy.visit('/settings/users')
+
+ // open the New user modal
+ cy.get('button#new-user-button').click()
+
+ cy.get('form[data-test="form"]').within(() => {
+ // see that the correct group is preselected
+ cy.contains('[data-test="groups"] .vs__selected', group).should('be.visible')
+ // see that the username is ""
+ cy.get('input[data-test="username"]').should('exist').and('have.value', '')
+ // set the username to john
+ cy.get('input[data-test="username"]').type(john.userId)
+ // see that the username is john
+ cy.get('input[data-test="username"]').should('have.value', john.userId)
+ // see that the password is ""
+ cy.get('input[type="password"]').should('exist').and('have.value', '')
+ // set the password to 123456
+ cy.get('input[type="password"]').type(john.password)
+ // see that the password is 123456
+ cy.get('input[type="password"]').should('have.value', john.password)
+ })
+
+ cy.get('form[data-test="form"]').parents('[role="dialog"]').within(() => {
+ // submit the new user form
+ cy.get('button[type="submit"]').click({ force: true })
+ })
+
+ // Make sure no confirmation modal is shown
+ handlePasswordConfirmation(admin.password)
+
+ // see that the created user is in the list
+ getUserListRow(john.userId)
+ // see that the list of users contains the user john
+ .contains(john.userId).should('exist')
+ })
+
+ it('Can create a new user when member of multiple groups', () => {
+ const group2 = randomString(7)
+ cy.runOccCommand(`group:add '${group2}'`)
+ cy.runOccCommand(`group:adduser '${group2}' '${subadmin.userId}'`)
+ makeSubAdmin(subadmin, group2)
+
+ cy.login(subadmin)
+ // open the User settings
+ cy.visit('/settings/users')
+
+ // open the New user modal
+ cy.get('button#new-user-button').click()
+
+ cy.get('form[data-test="form"]').within(() => {
+ // see that no group is pre-selected
+ cy.get('[data-test="groups"] .vs__selected').should('not.exist')
+ // see both groups are available
+ cy.findByRole('combobox', { name: /member of the following groups/i })
+ .should('be.visible')
+ .click()
+ // can select both groups
+ cy.document().its('body')
+ .findByRole('listbox', { name: 'Options' })
+ .should('be.visible')
+ .as('options')
+ .findAllByRole('option')
+ .should('have.length', 2)
+ .get('@options')
+ .findByRole('option', { name: group })
+ .should('be.visible')
+ .get('@options')
+ .findByRole('option', { name: group2 })
+ .should('be.visible')
+ .click()
+ // see group is selected
+ cy.contains('[data-test="groups"] .vs__selected', group2).should('be.visible')
+
+ // see that the username is ""
+ cy.get('input[data-test="username"]').should('exist').and('have.value', '')
+ // set the username to john
+ cy.get('input[data-test="username"]').type(john.userId)
+ // see that the username is john
+ cy.get('input[data-test="username"]').should('have.value', john.userId)
+ // see that the password is ""
+ cy.get('input[type="password"]').should('exist').and('have.value', '')
+ // set the password to 123456
+ cy.get('input[type="password"]').type(john.password)
+ // see that the password is 123456
+ cy.get('input[type="password"]').should('have.value', john.password)
+ })
+
+ cy.get('form[data-test="form"]').parents('[role="dialog"]').within(() => {
+ // submit the new user form
+ cy.get('button[type="submit"]').click({ force: true })
+ })
+
+ // Make sure no confirmation modal is shown
+ handlePasswordConfirmation(admin.password)
+
+ // see that the created user is in the list
+ getUserListRow(john.userId)
+ // see that the list of users contains the user john
+ .contains(john.userId).should('exist')
+ })
+
+ it('Only sees groups they are subadmin of', () => {
+ const group2 = randomString(7)
+ cy.runOccCommand(`group:add '${group2}'`)
+ cy.runOccCommand(`group:adduser '${group2}' '${subadmin.userId}'`)
+ // not a subadmin!
+
+ cy.login(subadmin)
+ // open the User settings
+ cy.visit('/settings/users')
+
+ // open the New user modal
+ cy.get('button#new-user-button').click()
+
+ cy.get('form[data-test="form"]').within(() => {
+ // see that the subadmin group is pre-selected
+ cy.contains('[data-test="groups"] .vs__selected', group).should('be.visible')
+ // see only the subadmin group is available
+ cy.findByRole('combobox', { name: /member of the following groups/i })
+ .should('be.visible')
+ .click()
+ // can select both groups
+ cy.document().its('body')
+ .findByRole('listbox', { name: 'Options' })
+ .should('be.visible')
+ .as('options')
+ .findAllByRole('option')
+ .should('have.length', 1)
+ })
+ })
+})
diff --git a/cypress/e2e/settings/users.cy.ts b/cypress/e2e/settings/users.cy.ts
index c90afc8866e..5b8726e92ca 100644
--- a/cypress/e2e/settings/users.cy.ts
+++ b/cypress/e2e/settings/users.cy.ts
@@ -1,23 +1,6 @@
/**
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
/// <reference types="cypress-if" />
import { User } from '@nextcloud/cypress'
@@ -56,6 +39,9 @@ describe('Settings: Create and delete accounts', function() {
cy.get('input[type="password"]').type(john.password)
// see that the password is 123456
cy.get('input[type="password"]').should('have.value', john.password)
+ })
+
+ cy.get('form[data-test="form"]').parents('[role="dialog"]').within(() => {
// submit the new user form
cy.get('button[type="submit"]').click({ force: true })
})
@@ -90,6 +76,9 @@ describe('Settings: Create and delete accounts', function() {
cy.get('input[type="password"]').should('exist').and('have.value', '')
cy.get('input[type="password"]').type(john.password)
cy.get('input[type="password"]').should('have.value', john.password)
+ })
+
+ cy.get('form[data-test="form"]').parents('[role="dialog"]').within(() => {
// submit the new user form
cy.get('button[type="submit"]').click({ force: true })
})
@@ -126,12 +115,13 @@ describe('Settings: Create and delete accounts', function() {
// The "Delete account" action in the actions menu is shown and clicked
cy.get('.action-item__popper .action').contains('Delete account').should('exist').click({ force: true })
- // And confirmation dialog accepted
- cy.get('.nc-generic-dialog button').contains(`Delete ${testUser.userId}`).click({ force: true })
// Make sure no confirmation modal is shown
handlePasswordConfirmation(admin.password)
+ // And confirmation dialog accepted
+ cy.get('.nc-generic-dialog button').contains(`Delete ${testUser.userId}`).click({ force: true })
+
// deleted clicked the user is not shown anymore
getUserListRow(testUser.userId).should('not.exist')
})
diff --git a/cypress/e2e/settings/usersUtils.ts b/cypress/e2e/settings/usersUtils.ts
index 56eff5e7d7d..7d8ea55d35b 100644
--- a/cypress/e2e/settings/usersUtils.ts
+++ b/cypress/e2e/settings/usersUtils.ts
@@ -1,23 +1,6 @@
/**
- * @copyright 2023 Christopher Ng <chrng8@gmail.com>
- *
- * @author Christopher Ng <chrng8@gmail.com>
- *
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { User } from '@nextcloud/cypress'
diff --git a/cypress/e2e/settings/users_columns.cy.ts b/cypress/e2e/settings/users_columns.cy.ts
index 5f2a293b824..0afbf14e773 100644
--- a/cypress/e2e/settings/users_columns.cy.ts
+++ b/cypress/e2e/settings/users_columns.cy.ts
@@ -1,23 +1,6 @@
/**
- * @copyright 2023 Christopher Ng <chrng8@gmail.com>
- *
- * @author Christopher Ng <chrng8@gmail.com>
- *
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { User } from '@nextcloud/cypress'
diff --git a/cypress/e2e/settings/users_disable.cy.ts b/cypress/e2e/settings/users_disable.cy.ts
index dd555c64f91..6195d43e211 100644
--- a/cypress/e2e/settings/users_disable.cy.ts
+++ b/cypress/e2e/settings/users_disable.cy.ts
@@ -1,23 +1,6 @@
/**
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { User } from '@nextcloud/cypress'
diff --git a/cypress/e2e/settings/users_groups.cy.ts b/cypress/e2e/settings/users_groups.cy.ts
index fd56c558b4f..8d84ddc6bb4 100644
--- a/cypress/e2e/settings/users_groups.cy.ts
+++ b/cypress/e2e/settings/users_groups.cy.ts
@@ -1,23 +1,6 @@
/**
- * @copyright 2023 Christopher Ng <chrng8@gmail.com>
- *
- * @author Christopher Ng <chrng8@gmail.com>
- *
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { User } from '@nextcloud/cypress'
@@ -72,15 +55,17 @@ describe('Settings: Assign user to a group', { testIsolation: false }, () => {
})
cy.runOccCommand(`group:add '${groupName}'`)
cy.login(admin)
+ cy.intercept('GET', '**/ocs/v2.php/cloud/groups/details?search=&offset=*&limit=*').as('loadGroups')
cy.visit('/settings/users')
+ cy.wait('@loadGroups')
})
it('see that the group is in the list', () => {
- cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').contains('li', groupName).should('exist')
- cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').contains('li', groupName).within(() => {
- cy.get('.counter-bubble__counter')
- .should('not.exist') // is hidden when 0
- })
+ cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').find('li').contains(groupName)
+ .should('exist')
+ cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').find('li').contains(groupName)
+ .find('.counter-bubble__counter')
+ .should('not.exist') // is hidden when 0
})
it('see that the user is in the list', () => {
@@ -118,8 +103,7 @@ describe('Settings: Assign user to a group', { testIsolation: false }, () => {
it('see the group was successfully assigned', () => {
// see a new memeber
- cy.get('ul[data-cy-users-settings-navigation-groups="custom"]')
- .contains('li', groupName)
+ cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').find('li').contains(groupName)
.find('.counter-bubble__counter')
.should('contain', '1')
})
@@ -138,23 +122,25 @@ describe('Settings: Delete an empty group', { testIsolation: false }, () => {
before(() => {
cy.runOccCommand(`group:add '${groupName}'`)
cy.login(admin)
+ cy.intercept('GET', '**/ocs/v2.php/cloud/groups/details?search=&offset=*&limit=*').as('loadGroups')
cy.visit('/settings/users')
+ cy.wait('@loadGroups')
})
it('see that the group is in the list', () => {
- cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => {
- // see that the list of groups contains the group foo
- cy.contains(groupName).should('exist').scrollIntoView()
- // open the actions menu for the group
- cy.contains('li', groupName).within(() => {
- cy.get('button.action-item__menutoggle').click({ force: true })
- })
- })
+ // see that the list of groups contains the group foo
+ cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').find('li').contains(groupName)
+ .should('exist')
+ .scrollIntoView()
+ // open the actions menu for the group
+ cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').find('li').contains(groupName)
+ .find('button.action-item__menutoggle')
+ .click({ force: true })
})
it('can delete the group', () => {
- // The "Remove group" action in the actions menu is shown and clicked
- cy.get('.action-item__popper button').contains('Remove group').should('exist').click({ force: true })
+ // The "Delete group" action in the actions menu is shown and clicked
+ cy.get('.action-item__popper button').contains('Delete group').should('exist').click({ force: true })
// And confirmation dialog accepted
cy.get('.modal-container button').contains('Confirm').click({ force: true })
@@ -163,10 +149,9 @@ describe('Settings: Delete an empty group', { testIsolation: false }, () => {
})
it('deleted group is not shown anymore', () => {
- cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => {
- // see that the list of groups does not contain the group
- cy.contains(groupName).should('not.exist')
- })
+ // see that the list of groups does not contain the group
+ cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').find('li').contains(groupName)
+ .should('not.exist')
// and also not in database
cy.runOccCommand('group:list --output=json').then(($response) => {
const groups: string[] = Object.keys(JSON.parse($response.stdout))
@@ -186,24 +171,27 @@ describe('Settings: Delete a non empty group', () => {
cy.runOccCommand(`group:addUser '${groupName}' '${$user.userId}'`)
})
cy.login(admin)
+ cy.intercept('GET', '**/ocs/v2.php/cloud/groups/details?search=&offset=*&limit=*').as('loadGroups')
cy.visit('/settings/users')
+ cy.wait('@loadGroups')
})
after(() => cy.deleteUser(testUser))
it('see that the group is in the list', () => {
// see that the list of groups contains the group
- cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').contains('li', groupName).should('exist').scrollIntoView()
+ cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').find('li').contains(groupName)
+ .should('exist')
+ .scrollIntoView()
})
it('can delete the group', () => {
// open the menu
- cy.get('ul[data-cy-users-settings-navigation-groups="custom"]')
- .contains('li', groupName)
+ cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').find('li').contains(groupName)
.find('button.action-item__menutoggle')
.click({ force: true })
- // The "Remove group" action in the actions menu is shown and clicked
- cy.get('.action-item__popper button').contains('Remove group').should('exist').click({ force: true })
+ // The "Delete group" action in the actions menu is shown and clicked
+ cy.get('.action-item__popper button').contains('Delete group').should('exist').click({ force: true })
// And confirmation dialog accepted
cy.get('.modal-container button').contains('Confirm').click({ force: true })
@@ -212,10 +200,9 @@ describe('Settings: Delete a non empty group', () => {
})
it('deleted group is not shown anymore', () => {
- cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => {
- // see that the list of groups does not contain the group foo
- cy.contains(groupName).should('not.exist')
- })
+ // see that the list of groups does not contain the group foo
+ cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').find('li').contains(groupName)
+ .should('not.exist')
// and also not in database
cy.runOccCommand('group:list --output=json').then(($response) => {
const groups: string[] = Object.keys(JSON.parse($response.stdout))
@@ -224,13 +211,13 @@ describe('Settings: Delete a non empty group', () => {
})
})
-describe.only('Settings: Sort groups in the UI', () => {
+describe('Settings: Sort groups in the UI', () => {
before(() => {
// Clear state
cy.runOccCommand('group:list --output json').then((output) => {
const groups = Object.keys(JSON.parse(output.stdout)).filter((group) => group !== 'admin')
groups.forEach((group) => {
- cy.runOccCommand(`group:delete "${group}"`)
+ cy.runOccCommand(`group:delete '${group}'`)
})
})
@@ -238,7 +225,7 @@ describe.only('Settings: Sort groups in the UI', () => {
cy.runOccCommand('group:add A')
cy.runOccCommand('group:add B')
cy.createRandomUser().then((user) => {
- cy.runOccCommand(`group:adduser B "${user.userId}"`)
+ cy.runOccCommand(`group:adduser B '${user.userId}'`)
})
// Visit the settings as admin
diff --git a/cypress/e2e/settings/users_manager.cy.ts b/cypress/e2e/settings/users_manager.cy.ts
new file mode 100644
index 00000000000..b7596ddf0ce
--- /dev/null
+++ b/cypress/e2e/settings/users_manager.cy.ts
@@ -0,0 +1,121 @@
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import { User } from '@nextcloud/cypress'
+import { getUserListRow, handlePasswordConfirmation, toggleEditButton, waitLoading } from './usersUtils'
+import { clearState } from '../../support/commonUtils'
+
+const admin = new User('admin', 'admin')
+
+describe('Settings: User Manager Management', function() {
+ let user: User
+ let manager: User
+
+ beforeEach(function() {
+ clearState()
+ cy.createRandomUser().then(($user) => {
+ manager = $user
+ return cy.createRandomUser()
+ }).then(($user) => {
+ user = $user
+ cy.login(admin)
+ cy.intercept('PUT', `/ocs/v2.php/cloud/users/${user.userId}*`).as('updateUser')
+ })
+ })
+
+ it('Can assign and remove a manager through the UI', function() {
+ cy.visit('/settings/users')
+
+ toggleEditButton(user, true)
+
+ // Scroll to manager cell and wait for it to be visible
+ getUserListRow(user.userId)
+ .find('[data-cy-user-list-cell-manager]')
+ .scrollIntoView()
+ .should('be.visible')
+
+ // Assign a manager
+ getUserListRow(user.userId).find('[data-cy-user-list-cell-manager]').within(() => {
+ // Verify no manager is set initially
+ cy.get('.vs__selected').should('not.exist')
+
+ // Open the dropdown menu
+ cy.get('[role="combobox"]').click({ force: true })
+
+ // Wait for the dropdown to be visible and initialized
+ waitLoading('[data-cy-user-list-input-manager]')
+
+ // Type the manager's username to search
+ cy.get('input[type="search"]').type(manager.userId, { force: true })
+
+ // Wait for the search results to load
+ waitLoading('[data-cy-user-list-input-manager]')
+ })
+
+ // Now select the manager from the filtered results
+ // Since the dropdown is floating, we need to search globally
+ cy.get('.vs__dropdown-menu').find('li').contains('span', manager.userId).should('be.visible').click({ force: true })
+
+ // Handle password confirmation if needed
+ handlePasswordConfirmation(admin.password)
+
+ // Verify the manager is selected in the UI
+ cy.get('.vs__selected').should('exist').and('contain.text', manager.userId)
+
+ // Verify the PUT request was made to set the manager
+ cy.wait('@updateUser').then((interception) => {
+ // Verify the request URL and body
+ expect(interception.request.url).to.match(/\/cloud\/users\/.+/)
+ expect(interception.request.body).to.deep.equal({
+ key: 'manager',
+ value: manager.userId
+ })
+ expect(interception.response?.statusCode).to.equal(200)
+ })
+
+ // Wait for the save to complete
+ waitLoading('[data-cy-user-list-input-manager]')
+
+ // Verify the manager is set in the backend
+ cy.getUserData(user).then(($result) => {
+ expect($result.body).to.contain(`<manager>${manager.userId}</manager>`)
+ })
+
+ // Now remove the manager
+ getUserListRow(user.userId).find('[data-cy-user-list-cell-manager]').within(() => {
+ // Clear the manager selection
+ cy.get('.vs__clear').click({ force: true })
+
+ // Verify the manager is cleared in the UI
+ cy.get('.vs__selected').should('not.exist')
+
+ // Handle password confirmation if needed
+ handlePasswordConfirmation(admin.password)
+ })
+
+ // Verify the PUT request was made to clear the manager
+ cy.wait('@updateUser').then((interception) => {
+ // Verify the request URL and body
+ expect(interception.request.url).to.match(/\/cloud\/users\/.+/)
+ expect(interception.request.body).to.deep.equal({
+ key: 'manager',
+ value: '',
+ })
+ expect(interception.response?.statusCode).to.equal(200)
+ })
+
+ // Wait for the save to complete
+ waitLoading('[data-cy-user-list-input-manager]')
+
+ // Verify the manager is cleared in the backend
+ cy.getUserData(user).then(($result) => {
+ expect($result.body).to.not.contain(`<manager>${manager.userId}</manager>`)
+ expect($result.body).to.contain('<manager></manager>')
+ })
+
+ // Finish editing the user
+ toggleEditButton(user, false)
+ })
+})
diff --git a/cypress/e2e/settings/users_modify.cy.ts b/cypress/e2e/settings/users_modify.cy.ts
index b230fb998a5..749bded2e94 100644
--- a/cypress/e2e/settings/users_modify.cy.ts
+++ b/cypress/e2e/settings/users_modify.cy.ts
@@ -1,23 +1,6 @@
/**
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { User } from '@nextcloud/cypress'
@@ -198,47 +181,6 @@ describe('Settings: Change user properties', function() {
})
})
- it('Can set manager of a user', function() {
- // create the manager
- let manager: User
- cy.createRandomUser().then(($user) => { manager = $user })
-
- // open the User settings as admin
- cy.login(admin)
- cy.visit('/settings/users')
-
- // toggle edit button into edit mode
- toggleEditButton(user, true)
-
- getUserListRow(user.userId)
- .find('[data-cy-user-list-cell-manager]')
- .scrollIntoView()
-
- getUserListRow(user.userId).find('[data-cy-user-list-cell-manager]').within(() => {
- // see that the user has no manager
- cy.get('.vs__selected').should('not.exist')
- // Open the dropdown menu
- cy.get('[role="combobox"]').click({ force: true })
- // select the manager
- cy.contains('li', manager.userId).click({ force: true })
-
- // Handle password confirmation on time out
- handlePasswordConfirmation(admin.password)
-
- // see that the user has a manager set
- cy.get('.vs__selected').should('exist').and('contain.text', manager.userId)
- })
-
- // see that the changes are loading
- waitLoading('[data-cy-user-list-input-manager]')
-
- // finish editing the user
- toggleEditButton(user, false)
-
- // validate the manager is set
- cy.getUserData(user).then(($result) => expect($result.body).to.contain(`<manager>${manager.userId}</manager>`))
- })
-
it('Can make user a subadmin of a group', function() {
// create a group
const groupName = 'userstestgroup'
@@ -256,6 +198,8 @@ describe('Settings: Change user properties', function() {
cy.get('.vs__selected').should('not.exist')
// Open the dropdown menu
cy.get('[role="combobox"]').click({ force: true })
+ // Search for the group
+ cy.get('[role="combobox"]').type('userstestgroup')
// select the group
cy.contains('li', groupName).click({ force: true })