summaryrefslogtreecommitdiffstats
path: root/cypress
diff options
context:
space:
mode:
authorFerdinand Thiessen <opensource@fthiessen.de>2023-10-18 15:19:11 +0200
committerFerdinand Thiessen <opensource@fthiessen.de>2023-10-25 11:36:24 +0200
commit5b0c27b6dafb6ab6007339214c1d22d79e4b6f5d (patch)
treee292568c4e2311fa0b29f0b95c7432c4418e1a84 /cypress
parent888473f5e2aa47b92c11fd169a3ffdd23b2c5504 (diff)
downloadnextcloud-server-5b0c27b6dafb6ab6007339214c1d22d79e4b6f5d.tar.gz
nextcloud-server-5b0c27b6dafb6ab6007339214c1d22d79e4b6f5d.zip
fix(settings): Stablize user list cypress tests
* Use common `data-testid` to identify elements rather than to depend on internal classes or properties * Force clean the state for the user tests * Move leftover acceptance tests for users from drone to cypress Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Diffstat (limited to 'cypress')
-rw-r--r--cypress/e2e/settings/users.cy.ts75
-rw-r--r--cypress/e2e/settings/usersUtils.ts53
-rw-r--r--cypress/e2e/settings/users_columns.cy.ts22
-rw-r--r--cypress/e2e/settings/users_disable.cy.ts38
-rw-r--r--cypress/e2e/settings/users_groups.cy.ts177
-rw-r--r--cypress/e2e/settings/users_modify.cy.ts284
-rw-r--r--cypress/support/commands.ts7
7 files changed, 505 insertions, 151 deletions
diff --git a/cypress/e2e/settings/users.cy.ts b/cypress/e2e/settings/users.cy.ts
index 5cafd77ea73..7c4b7f9df1e 100644
--- a/cypress/e2e/settings/users.cy.ts
+++ b/cypress/e2e/settings/users.cy.ts
@@ -24,7 +24,6 @@ import { User } from '@nextcloud/cypress'
import { getUserListRow, handlePasswordConfirmation } from './usersUtils'
const admin = new User('admin', 'admin')
-const jdoe = new User('jdoe', 'jdoe')
const john = new User('john', '123456')
describe('Settings: Create and delete users', function() {
@@ -38,7 +37,7 @@ describe('Settings: Create and delete users', function() {
cy.login(admin)
cy.listUsers().then((users) => {
cy.login(admin)
- if ((users as string[]).includes('john')) {
+ if ((users as string[]).includes(john.userId)) {
// ensure created user is deleted
cy.deleteUser(john).login(admin)
// ensure deleted user is not present
@@ -55,15 +54,15 @@ describe('Settings: Create and delete users', function() {
// 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')
+ 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')
+ 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('123456')
+ cy.get('input[type="password"]').type(john.password)
// see that the password is 123456
- cy.get('input[type="password"]').should('have.value', '123456')
+ cy.get('input[type="password"]').should('have.value', john.password)
// submit the new user form
cy.get('button[type="submit"]').click()
})
@@ -72,10 +71,9 @@ describe('Settings: Create and delete users', function() {
handlePasswordConfirmation(admin.password)
// see that the created user is in the list
- cy.get('tbody.user-list__body tr[data-test="john"]').within(() => {
+ getUserListRow(john.userId)
// see that the list of users contains the user john
- cy.contains('john').should('exist')
- })
+ .contains(john.userId).should('exist')
})
it('Can create a user with additional field data', function() {
@@ -85,8 +83,8 @@ describe('Settings: Create and delete users', function() {
cy.get('form[data-test="form"]').within(() => {
// set the username
cy.get('input[data-test="username"]').should('exist').and('have.value', '')
- cy.get('input[data-test="username"]').type('john')
- cy.get('input[data-test="username"]').should('have.value', 'john')
+ cy.get('input[data-test="username"]').type(john.userId)
+ cy.get('input[data-test="username"]').should('have.value', john.userId)
// set the display name
cy.get('input[data-test="displayName"]').should('exist').and('have.value', '')
cy.get('input[data-test="displayName"]').type('John Smith')
@@ -97,8 +95,8 @@ describe('Settings: Create and delete users', function() {
cy.get('input[data-test="email"]').should('have.value', 'john@example.org')
// set the password
cy.get('input[type="password"]').should('exist').and('have.value', '')
- cy.get('input[type="password"]').type('123456')
- cy.get('input[type="password"]').should('have.value', '123456')
+ cy.get('input[type="password"]').type(john.password)
+ cy.get('input[type="password"]').should('have.value', john.password)
// submit the new user form
cy.get('button[type="submit"]').click()
})
@@ -107,35 +105,42 @@ describe('Settings: Create and delete users', function() {
handlePasswordConfirmation(admin.password)
// see that the created user is in the list
- getUserListRow('john')
+ getUserListRow(john.userId)
// see that the list of users contains the user john
- .contains('john')
+ .contains(john.userId)
.should('exist')
})
it('Can delete a user', function() {
+ let testUser
// create user
- cy.createUser(jdoe).login(admin)
+ cy.createRandomUser()
+ .then(($user) => {
+ testUser = $user
+ })
+ cy.login(admin)
// ensure created user is present
- cy.reload().login(admin)
-
- // see that the user is in the list
- cy.get(`tbody.user-list__body tr[data-test="${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('td.row__cell--actions button.action-item__menutoggle').click()
+ cy.reload().then(() => {
+ // see that the user is in the list
+ getUserListRow(testUser.userId).within(() => {
+ // see that the list of users contains the user testUser
+ cy.contains(testUser.userId).should('exist')
+ // open the actions menu for the user
+ cy.get('[data-cy-user-list-cell-actions]')
+ .find('button.action-item__menutoggle')
+ .click({ force: true })
+ })
+
+ // The "Delete user" action in the actions menu is shown and clicked
+ cy.get('.action-item__popper .action').contains('Delete user').should('exist').click({ force: true })
+ // And confirmation dialog accepted
+ cy.get('.oc-dialog button').contains(`Delete ${testUser.userId}`).click({ force: true })
+
+ // Make sure no confirmation modal is shown
+ handlePasswordConfirmation(admin.password)
+
+ // deleted clicked the user is not shown anymore
+ getUserListRow(testUser.userId).should('not.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()
-
- // Make sure no confirmation modal is shown
- handlePasswordConfirmation(admin.password)
-
- // deleted clicked the user is not shown anymore
- cy.get(`tbody.user-list__body tr[data-test="${jdoe.userId}"]`).should('not.exist')
})
})
diff --git a/cypress/e2e/settings/usersUtils.ts b/cypress/e2e/settings/usersUtils.ts
index de0c28adcc4..0537f5b0ecb 100644
--- a/cypress/e2e/settings/usersUtils.ts
+++ b/cypress/e2e/settings/usersUtils.ts
@@ -20,6 +20,8 @@
*
*/
+import type { User } from '@nextcloud/cypress'
+
/**
* Assert that `element` does not exist or is not visible
* Useful in cases such as when NcModal is opened/closed rapidly
@@ -34,11 +36,27 @@ export function assertNotExistOrNotVisible(element: JQuery<HTMLElement>) {
}
/**
+ * Helper function ensure users and groups in this tests have a clean state
+ */
+export function clearState() {
+ // cleanup ignoring any failures
+ cy.runOccCommand('group:list --output=json').then(($result) => {
+ const groups = Object.keys(JSON.parse($result.stdout)).filter((name) => name !== 'admin')
+ groups.forEach((groupID) => cy.runOccCommand(`group:delete '${groupID}'`))
+ })
+
+ cy.runOccCommand('user:list --output=json').then(($result) => {
+ const users = Object.keys(JSON.parse($result.stdout)).filter((name) => name !== 'admin')
+ users.forEach((userID) => cy.runOccCommand(`user:delete '${userID}'`))
+ })
+}
+
+/**
* Get the settings users list
* @return Cypress chainable object
*/
export function getUserList() {
- return cy.get('[data-test-id="userList"]')
+ return cy.get('[data-cy-user-list]')
}
/**
@@ -48,7 +66,37 @@ export function getUserList() {
* @return Cypress chainable object
*/
export function getUserListRow(userId: string) {
- return getUserList().find(`tr[data-test="${userId}"]`)
+ return getUserList().find(`[data-cy-user-row="${userId}"]`)
+}
+
+export function waitLoading(selector: string) {
+ // We need to make sure the element is loading, otherwise the "done loading" will succeed even if we did not start loading.
+ // But Cypress might also be simply too slow to catch the loading phase. Thats why we need to wait in this case.
+ // eslint-disable-next-line cypress/no-unnecessary-waiting
+ cy.get(`${selector}[data-loading]`).if().should('exist').else().wait(1000)
+ // https://github.com/NoriSte/cypress-wait-until/issues/75#issuecomment-572685623
+ cy.waitUntil(() => Cypress.$(selector).length > 0 && !Cypress.$(selector).attr('data-loading')?.length, { timeout: 10000 })
+}
+
+/**
+ * Toggle the edit button of the user row
+ * @param user The user row to edit
+ * @param toEdit True if it should be switch to edit mode, false to switch to read-only
+ */
+export function toggleEditButton(user: User, toEdit = true) {
+ // see that the list of users contains the user
+ getUserListRow(user.userId).should('exist')
+ // toggle the edit mode for the user
+ .find('[data-cy-user-list-cell-actions]')
+ .find(`[data-cy-user-list-action-toggle-edit="${!toEdit}"]`)
+ .if()
+ .click({ force: true })
+ .else()
+ // otherwise ensure the button is already in edit mode
+ .then(() => getUserListRow(user.userId)
+ .find(`[data-cy-user-list-action-toggle-edit="${toEdit}"]`)
+ .should('exist'),
+ )
}
/**
@@ -59,7 +107,6 @@ export function handlePasswordConfirmation(adminPassword = 'admin') {
const handleModal = (context: Cypress.Chainable) => {
return context.contains('.modal-container', 'Confirm your password')
.if()
- .if('visible')
.within(() => {
cy.get('input[type="password"]').type(adminPassword)
cy.get('button').contains('Confirm').click()
diff --git a/cypress/e2e/settings/users_columns.cy.ts b/cypress/e2e/settings/users_columns.cy.ts
index 68313a0b245..6b2fcb8c5e7 100644
--- a/cypress/e2e/settings/users_columns.cy.ts
+++ b/cypress/e2e/settings/users_columns.cy.ts
@@ -49,9 +49,7 @@ describe('Settings: Show and hide columns', function() {
it('Can show a column', function() {
// see that the language column is not in the header
- cy.get('.user-list__header tr').within(() => {
- cy.contains('Language').should('not.exist')
- })
+ cy.get('[data-cy-user-list-header-languages]').should('not.exist')
// see that the language column is not in all user rows
cy.get('tbody.user-list__body tr').each(($row) => {
@@ -72,25 +70,21 @@ describe('Settings: Show and hide columns', function() {
cy.waitUntil(() => cy.get('.modal-container').should(el => assertNotExistOrNotVisible(el)))
// see that the language column is in the header
- cy.get('.user-list__header tr').within(() => {
- cy.contains('Language').should('exist')
- })
+ cy.get('[data-cy-user-list-header-languages]').should('exist')
// see that the language column is in all user rows
getUserList().find('tbody tr').each(($row) => {
- cy.wrap($row).get('[data-test-id="cell-language"]').should('exist')
+ cy.wrap($row).get('[data-cy-user-list-cell-language]').should('exist')
})
})
it('Can hide a column', function() {
// see that the last login column is in the header
- cy.get('.user-list__header tr').within(() => {
- cy.contains('Last login').should('exist')
- })
+ cy.get('[data-cy-user-list-header-last-login]').should('exist')
// see that the last login column is in all user rows
getUserList().find('tbody tr').each(($row) => {
- cy.wrap($row).get('[data-test-id="cell-lastLogin"]').should('exist')
+ cy.wrap($row).get('[data-cy-user-list-cell-last-login]').should('exist')
})
// open the settings dialog
@@ -107,13 +101,11 @@ describe('Settings: Show and hide columns', function() {
cy.waitUntil(() => cy.get('.modal-container').should(el => assertNotExistOrNotVisible(el)))
// see that the last login column is not in the header
- cy.get('.user-list__header tr').within(() => {
- cy.contains('Last login').should('not.exist')
- })
+ cy.get('[data-cy-user-list-header-last-login]').should('not.exist')
// see that the last login column is not in all user rows
getUserList().find('tbody tr').each(($row) => {
- cy.wrap($row).get('[data-test-id="cell-lastLogin"]').should('not.exist')
+ cy.wrap($row).get('[data-cy-user-list-cell-last-login]').should('not.exist')
})
})
})
diff --git a/cypress/e2e/settings/users_disable.cy.ts b/cypress/e2e/settings/users_disable.cy.ts
index a9ea21901fc..e63ba4fc3d6 100644
--- a/cypress/e2e/settings/users_disable.cy.ts
+++ b/cypress/e2e/settings/users_disable.cy.ts
@@ -21,61 +21,67 @@
*/
import { User } from '@nextcloud/cypress'
+import { clearState, getUserListRow } from './usersUtils'
const admin = new User('admin', 'admin')
-const jdoe = new User('jdoe', 'jdoe')
describe('Settings: Disable and enable users', function() {
- before(function() {
- cy.createUser(jdoe)
+ let testUser: User
+
+ beforeEach(function() {
+ clearState()
+ cy.createRandomUser().then(($user) => {
+ testUser = $user
+ })
cy.login(admin)
// open the User settings
cy.visit('/settings/users')
})
+ // Not guranteed to run but would be nice to cleanup
after(() => {
- cy.deleteUser(jdoe)
+ cy.deleteUser(testUser)
})
it('Can disable the user', function() {
// ensure user is enabled
- cy.enableUser(jdoe)
+ cy.enableUser(testUser)
// see that the user is in the list of active users
- cy.get(`tbody.user-list__body tr[data-test="${jdoe.userId}"]`).within(() => {
- // see that the list of users contains the user jdoe
- cy.contains(jdoe.userId).should('exist')
+ getUserListRow(testUser.userId).within(() => {
+ // see that the list of users contains the user testUser
+ cy.contains(testUser.userId).should('exist')
// open the actions menu for the user
- cy.get('td.row__cell--actions button.action-item__menutoggle').click({ scrollBehavior: 'center' })
+ cy.get('[data-cy-user-list-cell-actions] button.action-item__menutoggle').click({ scrollBehavior: 'center' })
})
// 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(`tbody.user-list__body tr[data-test="${jdoe.userId}"]`).should('not.exist')
+ getUserListRow(testUser.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(`tbody.user-list__body tr[data-test="${jdoe.userId}"]`).should('exist')
+ getUserListRow(testUser.userId).should('exist')
})
it('Can enable the user', function() {
// ensure user is disabled
- cy.enableUser(jdoe, false)
+ cy.enableUser(testUser, false).reload()
// Open disabled users section
cy.get('#disabled a').click()
cy.url().should('match', /\/disabled/)
// see that the user is in the list of active users
- cy.get(`tbody.user-list__body tr[data-test="${jdoe.userId}"]`).within(() => {
- // see that the list of disabled users contains the user jdoe
- cy.contains(jdoe.userId).should('exist')
+ getUserListRow(testUser.userId).within(() => {
+ // see that the list of disabled users contains the user testUser
+ cy.contains(testUser.userId).should('exist')
// open the actions menu for the user
- cy.get('td.row__cell--actions button.action-item__menutoggle').click({ scrollBehavior: 'center' })
+ cy.get('[data-cy-user-list-cell-actions] button.action-item__menutoggle').click({ scrollBehavior: 'center' })
})
// The "Enable user" action in the actions menu is shown and clicked
diff --git a/cypress/e2e/settings/users_groups.cy.ts b/cypress/e2e/settings/users_groups.cy.ts
index 0d90722c124..634679b702f 100644
--- a/cypress/e2e/settings/users_groups.cy.ts
+++ b/cypress/e2e/settings/users_groups.cy.ts
@@ -21,18 +21,21 @@
*/
import { User } from '@nextcloud/cypress'
-import { handlePasswordConfirmation } from './usersUtils'
+import { getUserListRow, handlePasswordConfirmation, toggleEditButton } from './usersUtils'
+
+// eslint-disable-next-line n/no-extraneous-import
+import randomString from 'crypto-random-string'
const admin = new User('admin', 'admin')
-describe('Settings: Create and delete groups', () => {
+describe('Settings: Create groups', () => {
before(() => {
cy.login(admin)
- // open the User settings
cy.visit('/settings/users')
})
it('Can create a group', () => {
+ const groupName = randomString(7)
// open the Create group menu
cy.get('button[aria-label="Create group"]').click()
@@ -40,9 +43,9 @@ describe('Settings: Create and delete groups', () => {
// see that the group name is ""
cy.get('input[placeholder="Group name"]').should('exist').and('have.value', '')
// set the group name to foo
- cy.get('input[placeholder="Group name"]').type('foo')
+ cy.get('input[placeholder="Group name"]').type(groupName)
// see that the group name is foo
- cy.get('input[placeholder="Group name"]').should('have.value', 'foo')
+ cy.get('input[placeholder="Group name"]').should('have.value', groupName)
// submit the group name
cy.get('input[placeholder="Group name"] ~ button').click()
})
@@ -53,38 +56,170 @@ describe('Settings: Create and delete groups', () => {
// see that the created group is in the list
cy.get('ul.app-navigation__list').within(() => {
// see that the list of groups contains the group foo
- cy.contains('foo').should('exist')
+ cy.contains(groupName).should('exist')
+ })
+ })
+})
+
+describe('Settings: Assign user to a group', { testIsolation: false }, () => {
+ const groupName = randomString(7)
+ let testUser: User
+
+ after(() => cy.deleteUser(testUser))
+ before(() => {
+ cy.createRandomUser().then((user) => {
+ testUser = user
+ })
+ cy.runOccCommand(`group:add '${groupName}'`)
+ cy.login(admin)
+ cy.visit('/settings/users')
+ })
+
+ it('see that the group is in the list', () => {
+ cy.get('ul.app-navigation__list').contains('li', groupName).should('exist')
+ cy.get('ul.app-navigation__list').contains('li', groupName).within(() => {
+ cy.get('.counter-bubble__counter')
+ .should('not.exist') // is hidden when 0
})
})
- it('Can delete a group', () => {
- // see that the group is in the list
+ it('see that the user is in the list', () => {
+ getUserListRow(testUser.userId)
+ .contains(testUser.userId)
+ .should('exist')
+ .scrollIntoView()
+ })
+
+ it('switch into user edit mode', () => {
+ toggleEditButton(testUser)
+ getUserListRow(testUser.userId)
+ .find('[data-cy-user-list-input-groups]')
+ .should('exist')
+ })
+
+ it('assign the group', () => {
+ // focus inside the input
+ getUserListRow(testUser.userId)
+ .find('[data-cy-user-list-input-groups] input')
+ .click({ force: true })
+ // enter the group name
+ getUserListRow(testUser.userId)
+ .find('[data-cy-user-list-input-groups] input')
+ .type(`${groupName.slice(0, 5)}`) // only type part as otherwise we would create a new one with the same name
+ cy.contains('li.vs__dropdown-option', groupName)
+ .click({ force: true })
+
+ handlePasswordConfirmation(admin.password)
+ })
+
+ it('leave the user edit mode', () => {
+ toggleEditButton(testUser, false)
+ })
+
+ it('see the group was successfully assigned', () => {
+ // see a new memeber
+ cy.get('ul.app-navigation__list')
+ .contains('li', groupName)
+ .find('.counter-bubble__counter')
+ .should('contain', '1')
+ })
+
+ it('validate the user was added on backend', () => {
+ cy.runOccCommand(`user:info --output=json '${testUser.userId}'`).then((output) => {
+ cy.wrap(output.code).should('eq', 0)
+ cy.wrap(JSON.parse(output.stdout)?.groups).should('include', groupName)
+ })
+ })
+})
+
+describe('Settings: Delete an empty group', { testIsolation: false }, () => {
+ const groupName = randomString(7)
+
+ before(() => {
+ cy.runOccCommand(`group:add '${groupName}'`)
+ cy.login(admin)
+ cy.visit('/settings/users')
+ })
+
+ it('see that the group is in the list', () => {
cy.get('ul.app-navigation__list').within(() => {
// see that the list of groups contains the group foo
- cy.contains('foo').should('exist')
+ cy.contains(groupName).should('exist').scrollIntoView()
// open the actions menu for the group
- cy.contains('li', 'foo').within(() => {
- cy.get('button.action-item__menutoggle').click()
+ cy.contains('li', groupName).within(() => {
+ cy.get('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()
+ cy.get('.action-item__popper button').contains('Remove group').should('exist').click({ force: true })
// And confirmation dialog accepted
- cy.get('.modal-container button').contains('Confirm').click()
+ cy.get('.modal-container button').contains('Confirm').click({ force: true })
// Make sure no confirmation modal is shown
- cy.get('body').contains('.modal-container', 'Confirm your password')
- .if('visible')
- .then(($modal) => {
- cy.wrap($modal).find('input[type="password"]').type(admin.password)
- cy.wrap($modal).find('button').contains('Confirm').click()
- })
+ handlePasswordConfirmation(admin.password)
+ })
- // deleted group is not shown anymore
+ it('deleted group is not shown anymore', () => {
+ cy.get('ul.app-navigation__list').within(() => {
+ // see that the list of groups does not contain the group
+ cy.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))
+ expect(groups).to.not.include(groupName)
+ })
+ })
+})
+
+describe('Settings: Delete a non empty group', () => {
+ let testUser: User
+ const groupName = randomString(7)
+
+ before(() => {
+ cy.runOccCommand(`group:add '${groupName}'`)
+ cy.createRandomUser().then(($user) => {
+ testUser = $user
+ cy.runOccCommand(`group:addUser '${groupName}' '${$user.userId}'`)
+ })
+ cy.login(admin)
+ cy.visit('/settings/users')
+ })
+ 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.app-navigation__list').contains('li', groupName).should('exist').scrollIntoView()
+ })
+
+ it('can delete the group', () => {
+ // open the menu
+ cy.get('ul.app-navigation__list')
+ .contains('li', 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 })
+ // And confirmation dialog accepted
+ cy.get('.modal-container button').contains('Confirm').click({ force: true })
+
+ // Make sure no confirmation modal is shown
+ handlePasswordConfirmation(admin.password)
+ })
+
+ it('deleted group is not shown anymore', () => {
cy.get('ul.app-navigation__list').within(() => {
// see that the list of groups does not contain the group foo
- cy.contains('foo').should('not.exist')
+ cy.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))
+ expect(groups).to.not.include(groupName)
})
})
})
diff --git a/cypress/e2e/settings/users_modify.cy.ts b/cypress/e2e/settings/users_modify.cy.ts
index c08e9de2610..ee3d1f37c02 100644
--- a/cypress/e2e/settings/users_modify.cy.ts
+++ b/cypress/e2e/settings/users_modify.cy.ts
@@ -21,92 +21,260 @@
*/
import { User } from '@nextcloud/cypress'
-import { getUserListRow, handlePasswordConfirmation } from './usersUtils'
+import { clearState, getUserListRow, handlePasswordConfirmation, toggleEditButton, waitLoading } from './usersUtils'
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)
- // open the User settings
- cy.visit('/settings/users')
- })
+ let user: User
beforeEach(function() {
- // reset to read-only mode: try to find the edit button and click it if set to editing
- getUserListRow(jdoe.userId)
- .find('[data-test-id="cell-actions"]')
- // replace with following (more error resilent) with nextcloud-vue 8
- // find('[data-test-id="button-toggleEdit"][data-test="true"]')
- .find('button[aria-label="Done"]')
- .if()
- .click({ force: true })
- })
-
- after(() => {
- cy.deleteUser(jdoe)
+ clearState()
+ cy.createRandomUser().then(($user) => { user = $user })
+ cy.login(admin)
})
it('Can change the display name', function() {
- // see that the list of users contains the user jdoe
- getUserListRow(jdoe.userId).should('exist')
- // toggle the edit mode for the user jdoe
- .find('[data-test-id="cell-actions"]')
- .find('button[aria-label="Edit"]')
- // replace with following (more error resilent) with nextcloud-vue 8
- // find('[data-test-id="button-toggleEdit"]')
- .click({ force: true })
-
- getUserListRow(jdoe.userId).within(() => {
+ // open the User settings as admin
+ cy.visit('/settings/users')
+
+ // toggle edit button into edit mode
+ toggleEditButton(user, true)
+
+ getUserListRow(user.userId).within(() => {
// set the display name
- cy.get('input[data-test-id="input-displayName"]').should('exist').and('have.value', 'jdoe')
- cy.get('input[data-test-id="input-displayName"]').clear()
- cy.get('input[data-test-id="input-displayName"]').type('John Doe')
- cy.get('input[data-test-id="input-displayName"]').should('have.value', 'John Doe')
- cy.get('input[data-test-id="input-displayName"] ~ button').click()
+ cy.get('[data-cy-user-list-input-displayname]').should('exist').and('have.value', user.userId)
+ cy.get('[data-cy-user-list-input-displayname]').clear()
+ cy.get('[data-cy-user-list-input-displayname]').type('John Doe')
+ cy.get('[data-cy-user-list-input-displayname]').should('have.value', 'John Doe')
+ cy.get('[data-cy-user-list-input-displayname] ~ button').click()
// Make sure no confirmation modal is shown
handlePasswordConfirmation(admin.password)
// see that the display name cell is done loading
- cy.get('[data-test-id="input-displayName"]').should('have.attr', 'data-test-loading', 'true')
- cy.waitUntil(() => cy.get('[data-test-id="input-displayName"]').should('have.attr', 'data-test-loading', 'false'), { timeout: 10000 })
+ waitLoading('[data-cy-user-list-input-displayname]')
})
+
// Success message is shown
cy.get('.toastify.toast-success').contains(/Display.+name.+was.+successfully.+changed/i).should('exist')
})
it('Can change the password', function() {
- // see that the list of users contains the user jdoe
- getUserListRow(jdoe.userId).should('exist')
- // toggle the edit mode for the user jdoe
- .find('[data-test-id="cell-actions"]')
- .find('button[aria-label="Edit"]')
- // replace with following (more error resilent) with nextcloud-vue 8
- // find('[data-test-id="button-toggleEdit"]')
- .click({ force: true })
-
- getUserListRow(jdoe.userId).within(() => {
- // 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('input[type="password"] ~ button').click()
+ // open the User settings as admin
+ cy.visit('/settings/users')
+
+ // toggle edit button into edit mode
+ toggleEditButton(user, true)
+
+ getUserListRow(user.userId).within(() => {
+ // see that the password of user is ""
+ cy.get('[data-cy-user-list-input-password]').should('exist').and('have.value', '')
+ // set the password for user to 123456
+ cy.get('[data-cy-user-list-input-password]').type('123456')
+ // When I set the password for user to 123456
+ cy.get('[data-cy-user-list-input-password]').should('have.value', '123456')
+ cy.get('[data-cy-user-list-input-password] ~ button').click()
// Make sure no confirmation modal is shown
handlePasswordConfirmation(admin.password)
- // see that the password cell for user user0 is done loading
- cy.get('[data-test-id="input-password"]').should('have.attr', 'data-test-loading', 'true')
- cy.waitUntil(() => cy.get('[data-test-id="input-password"]').should('have.attr', 'data-test-loading', 'false'), { timeout: 10000 })
+ // see that the password cell for user is done loading
+ waitLoading('[data-cy-user-list-input-password]')
// password input is emptied on change
- cy.get('[data-test-id="input-password"]').should('have.value', '')
+ cy.get('[data-cy-user-list-input-password]').should('have.value', '')
})
+
// Success message is shown
cy.get('.toastify.toast-success').contains(/Password.+successfully.+changed/i).should('exist')
})
+
+ it('Can change the email address', function() {
+ // open the User settings as admin
+ cy.visit('/settings/users')
+
+ // toggle edit button into edit mode
+ toggleEditButton(user, true)
+
+ getUserListRow(user.userId).find('[data-cy-user-list-cell-email]').within(() => {
+ // see that the email of user is ""
+ cy.get('input').should('exist').and('have.value', '')
+ // set the email for user to mymail@example.com
+ cy.get('input').type('mymail@example.com')
+ // When I set the password for user to mymail@example.com
+ cy.get('input').should('have.value', 'mymail@example.com')
+ cy.get('input ~ button').click()
+
+ // Make sure no confirmation modal is shown
+ handlePasswordConfirmation(admin.password)
+
+ // see that the password cell for user is done loading
+ waitLoading('[data-cy-user-list-input-email]')
+ })
+
+ // Success message is shown
+ cy.get('.toastify.toast-success').contains(/Email.+successfully.+changed/i).should('exist')
+ })
+
+ it('Can change the user quota to a predefined one', function() {
+ // open the User settings as admin
+ cy.visit('/settings/users')
+
+ // toggle edit button into edit mode
+ toggleEditButton(user, true)
+
+ getUserListRow(user.userId).find('[data-cy-user-list-cell-quota]').scrollIntoView()
+ getUserListRow(user.userId).find('[data-cy-user-list-cell-quota] [data-cy-user-list-input-quota]').within(() => {
+ // see that the quota of user is unlimited
+ cy.get('.vs__selected').should('exist').and('contain.text', 'Unlimited')
+ // Open the quota selector
+ cy.get('[role="combobox"]').click({ force: true })
+ // see that there are default options for the quota
+ cy.get('li').then(($options) => {
+ expect($options).to.have.length(5)
+ cy.wrap($options).contains('Default quota')
+ cy.wrap($options).contains('Unlimited')
+ cy.wrap($options).contains('1 GB')
+ cy.wrap($options).contains('10 GB')
+ // select 5 GB
+ cy.wrap($options).contains('5 GB').click({ force: true })
+
+ // Make sure no confirmation modal is shown
+ handlePasswordConfirmation(admin.password)
+ })
+ // see that the quota of user is 5 GB
+ cy.get('.vs__selected').should('exist').and('contain.text', '5 GB')
+ })
+
+ // see that the changes are loading
+ waitLoading('[data-cy-user-list-input-quota]')
+
+ // finish editing the user
+ toggleEditButton(user, false)
+
+ // I see that the quota was set on the backend
+ cy.runOccCommand(`user:info --output=json '${user.userId}'`).then(($result) => {
+ expect($result.code).to.equal(0)
+ const info = JSON.parse($result.stdout)
+ expect(info?.quota).to.equal('5 GB')
+ })
+ })
+
+ it('Can change the user quota to a custom value', function() {
+ // open the User settings as admin
+ cy.visit('/settings/users')
+
+ // toggle edit button into edit mode
+ toggleEditButton(user, true)
+
+ getUserListRow(user.userId).find('[data-cy-user-list-cell-quota]').scrollIntoView()
+ getUserListRow(user.userId).find('[data-cy-user-list-cell-quota]').within(() => {
+ // see that the quota of user is unlimited
+ cy.get('.vs__selected').should('exist').and('contain.text', 'Unlimited')
+ // set the quota to 4 MB
+ cy.get('[data-cy-user-list-input-quota] input').type('4 MB{enter}')
+
+ // Make sure no confirmation modal is shown
+ handlePasswordConfirmation(admin.password)
+
+ // see that the quota of user is 4 MB
+ // TODO: Enable this after the file size handling is fixed
+ // cy.get('.vs__selected').should('exist').and('contain.text', '4 MB')
+
+ // see that the changes are loading
+ waitLoading('[data-cy-user-list-input-quota]')
+ })
+
+ // finish editing the user
+ toggleEditButton(user, false)
+
+ // I see that the quota was set on the backend
+ cy.runOccCommand(`user:info --output=json '${user.userId}'`).then(($result) => {
+ expect($result.code).to.equal(0)
+ // TODO: Enable this after the file size handling is fixed!!!!!!
+ // const info = JSON.parse($result.stdout)
+ // expect(info?.quota).to.equal('4 MB')
+ })
+ })
+
+ 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'
+ cy.runOccCommand(`group:add '${groupName}'`)
+
+ // open the User settings as admin
+ cy.visit('/settings/users')
+
+ // toggle edit button into edit mode
+ toggleEditButton(user, true)
+
+ getUserListRow(user.userId).find('[data-cy-user-list-cell-subadmins]').scrollIntoView()
+ getUserListRow(user.userId).find('[data-cy-user-list-cell-subadmins]').within(() => {
+ // see that the user is no subadmin
+ cy.get('.vs__selected').should('not.exist')
+ // Open the dropdown menu
+ cy.get('[role="combobox"]').click({ force: true })
+ // select the group
+ cy.contains('li', groupName).click({ force: true })
+
+ // handle password confirmation on time out
+ handlePasswordConfirmation(admin.password)
+
+ // see that the user is subadmin of the group
+ cy.get('.vs__selected').should('exist').and('contain.text', groupName)
+ })
+
+ waitLoading('[data-cy-user-list-input-subadmins]')
+
+ // finish editing the user
+ toggleEditButton(user, false)
+
+ // I see that the quota was set on the backend
+ cy.getUserData(user).then(($response) => {
+ expect($response.status).to.equal(200)
+ const dom = (new DOMParser()).parseFromString($response.body, 'text/xml')
+ expect(dom.querySelector('subadmin element')?.textContent).to.contain(groupName)
+ })
+ })
})
diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts
index 58363112a75..1596bfe81fc 100644
--- a/cypress/support/commands.ts
+++ b/cypress/support/commands.ts
@@ -67,7 +67,7 @@ declare global {
/**
* Run an occ command in the docker container.
*/
- runOccCommand(command: string): Cypress.Chainable<void>,
+ runOccCommand(command: string, options?: Partial<Cypress.ExecOptions>): Cypress.Chainable<Cypress.Exec>,
}
}
}
@@ -131,6 +131,7 @@ Cypress.Commands.add('uploadFile', (user, fixture = 'image.jpg', mimeType = 'ima
* @param {string} target the target of the file relative to the user root
*/
Cypress.Commands.add('uploadContent', (user, blob, mimeType, target) => {
+ // eslint-disable-next-line cypress/unsafe-to-chain-command
cy.clearCookies()
.then(async () => {
const fileName = basename(target)
@@ -216,6 +217,6 @@ Cypress.Commands.add('resetUserTheming', (user?: User) => {
}
})
-Cypress.Commands.add('runOccCommand', (command: string) => {
- cy.exec(`docker exec --user www-data nextcloud-cypress-tests-server php ./occ ${command}`)
+Cypress.Commands.add('runOccCommand', (command: string, options?: Partial<Cypress.ExecOptions>) => {
+ return cy.exec(`docker exec --user www-data nextcloud-cypress-tests-server php ./occ ${command}`, options)
})