diff options
Diffstat (limited to 'cypress/e2e/settings')
-rw-r--r-- | cypress/e2e/settings/access-levels.cy.ts | 29 | ||||
-rw-r--r-- | cypress/e2e/settings/apps.cy.ts | 30 | ||||
-rw-r--r-- | cypress/e2e/settings/personal-info.cy.ts | 121 | ||||
-rw-r--r-- | cypress/e2e/settings/users-group-admin.cy.ts | 186 | ||||
-rw-r--r-- | cypress/e2e/settings/users.cy.ts | 32 | ||||
-rw-r--r-- | cypress/e2e/settings/usersUtils.ts | 21 | ||||
-rw-r--r-- | cypress/e2e/settings/users_columns.cy.ts | 21 | ||||
-rw-r--r-- | cypress/e2e/settings/users_disable.cy.ts | 21 | ||||
-rw-r--r-- | cypress/e2e/settings/users_groups.cy.ts | 91 | ||||
-rw-r--r-- | cypress/e2e/settings/users_manager.cy.ts | 121 | ||||
-rw-r--r-- | cypress/e2e/settings/users_modify.cy.ts | 64 |
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 }) |