diff options
author | Ferdinand Thiessen <opensource@fthiessen.de> | 2023-10-18 15:19:11 +0200 |
---|---|---|
committer | Ferdinand Thiessen <opensource@fthiessen.de> | 2023-10-25 11:36:24 +0200 |
commit | 5b0c27b6dafb6ab6007339214c1d22d79e4b6f5d (patch) | |
tree | e292568c4e2311fa0b29f0b95c7432c4418e1a84 /cypress | |
parent | 888473f5e2aa47b92c11fd169a3ffdd23b2c5504 (diff) | |
download | nextcloud-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.ts | 75 | ||||
-rw-r--r-- | cypress/e2e/settings/usersUtils.ts | 53 | ||||
-rw-r--r-- | cypress/e2e/settings/users_columns.cy.ts | 22 | ||||
-rw-r--r-- | cypress/e2e/settings/users_disable.cy.ts | 38 | ||||
-rw-r--r-- | cypress/e2e/settings/users_groups.cy.ts | 177 | ||||
-rw-r--r-- | cypress/e2e/settings/users_modify.cy.ts | 284 | ||||
-rw-r--r-- | cypress/support/commands.ts | 7 |
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) }) |