You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

users_groups.cy.ts 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /**
  2. * @copyright 2023 Christopher Ng <chrng8@gmail.com>
  3. *
  4. * @author Christopher Ng <chrng8@gmail.com>
  5. *
  6. * @license AGPL-3.0-or-later
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Affero General Public License as
  10. * published by the Free Software Foundation, either version 3 of the
  11. * License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Affero General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. import { User } from '@nextcloud/cypress'
  23. import { assertNotExistOrNotVisible, getUserListRow, handlePasswordConfirmation, toggleEditButton } from './usersUtils'
  24. // eslint-disable-next-line n/no-extraneous-import
  25. import randomString from 'crypto-random-string'
  26. const admin = new User('admin', 'admin')
  27. describe('Settings: Create groups', () => {
  28. before(() => {
  29. cy.login(admin)
  30. cy.visit('/settings/users')
  31. })
  32. it('Can create a group', () => {
  33. const groupName = randomString(7)
  34. // open the Create group menu
  35. cy.get('button[aria-label="Create group"]').click()
  36. cy.get('li[data-cy-users-settings-new-group-name]').within(() => {
  37. // see that the group name is ""
  38. cy.get('input').should('exist').and('have.value', '')
  39. // set the group name to foo
  40. cy.get('input').type(groupName)
  41. // see that the group name is foo
  42. cy.get('input').should('have.value', groupName)
  43. // submit the group name
  44. cy.get('input ~ button').click()
  45. })
  46. // Make sure no confirmation modal is shown
  47. handlePasswordConfirmation(admin.password)
  48. // see that the created group is in the list
  49. cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => {
  50. // see that the list of groups contains the group foo
  51. cy.contains(groupName).should('exist')
  52. })
  53. })
  54. })
  55. describe('Settings: Assign user to a group', { testIsolation: false }, () => {
  56. const groupName = randomString(7)
  57. let testUser: User
  58. after(() => cy.deleteUser(testUser))
  59. before(() => {
  60. cy.createRandomUser().then((user) => {
  61. testUser = user
  62. })
  63. cy.runOccCommand(`group:add '${groupName}'`)
  64. cy.login(admin)
  65. cy.visit('/settings/users')
  66. })
  67. it('see that the group is in the list', () => {
  68. cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').contains('li', groupName).should('exist')
  69. cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').contains('li', groupName).within(() => {
  70. cy.get('.counter-bubble__counter')
  71. .should('not.exist') // is hidden when 0
  72. })
  73. })
  74. it('see that the user is in the list', () => {
  75. getUserListRow(testUser.userId)
  76. .contains(testUser.userId)
  77. .should('exist')
  78. .scrollIntoView()
  79. })
  80. it('switch into user edit mode', () => {
  81. toggleEditButton(testUser)
  82. getUserListRow(testUser.userId)
  83. .find('[data-cy-user-list-input-groups]')
  84. .should('exist')
  85. })
  86. it('assign the group', () => {
  87. // focus inside the input
  88. getUserListRow(testUser.userId)
  89. .find('[data-cy-user-list-input-groups] input')
  90. .click({ force: true })
  91. // enter the group name
  92. getUserListRow(testUser.userId)
  93. .find('[data-cy-user-list-input-groups] input')
  94. .type(`${groupName.slice(0, 5)}`) // only type part as otherwise we would create a new one with the same name
  95. cy.contains('li.vs__dropdown-option', groupName)
  96. .click({ force: true })
  97. handlePasswordConfirmation(admin.password)
  98. })
  99. it('leave the user edit mode', () => {
  100. toggleEditButton(testUser, false)
  101. })
  102. it('see the group was successfully assigned', () => {
  103. // see a new memeber
  104. cy.get('ul[data-cy-users-settings-navigation-groups="custom"]')
  105. .contains('li', groupName)
  106. .find('.counter-bubble__counter')
  107. .should('contain', '1')
  108. })
  109. it('validate the user was added on backend', () => {
  110. cy.runOccCommand(`user:info --output=json '${testUser.userId}'`).then((output) => {
  111. cy.wrap(output.code).should('eq', 0)
  112. cy.wrap(JSON.parse(output.stdout)?.groups).should('include', groupName)
  113. })
  114. })
  115. })
  116. describe('Settings: Delete an empty group', { testIsolation: false }, () => {
  117. const groupName = randomString(7)
  118. before(() => {
  119. cy.runOccCommand(`group:add '${groupName}'`)
  120. cy.login(admin)
  121. cy.visit('/settings/users')
  122. })
  123. it('see that the group is in the list', () => {
  124. cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => {
  125. // see that the list of groups contains the group foo
  126. cy.contains(groupName).should('exist').scrollIntoView()
  127. // open the actions menu for the group
  128. cy.contains('li', groupName).within(() => {
  129. cy.get('button.action-item__menutoggle').click({ force: true })
  130. })
  131. })
  132. })
  133. it('can delete the group', () => {
  134. // The "Remove group" action in the actions menu is shown and clicked
  135. cy.get('.action-item__popper button').contains('Remove group').should('exist').click({ force: true })
  136. // And confirmation dialog accepted
  137. cy.get('.modal-container button').contains('Confirm').click({ force: true })
  138. // Make sure no confirmation modal is shown
  139. handlePasswordConfirmation(admin.password)
  140. })
  141. it('deleted group is not shown anymore', () => {
  142. cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => {
  143. // see that the list of groups does not contain the group
  144. cy.contains(groupName).should('not.exist')
  145. })
  146. // and also not in database
  147. cy.runOccCommand('group:list --output=json').then(($response) => {
  148. const groups: string[] = Object.keys(JSON.parse($response.stdout))
  149. expect(groups).to.not.include(groupName)
  150. })
  151. })
  152. })
  153. describe('Settings: Delete a non empty group', () => {
  154. let testUser: User
  155. const groupName = randomString(7)
  156. before(() => {
  157. cy.runOccCommand(`group:add '${groupName}'`)
  158. cy.createRandomUser().then(($user) => {
  159. testUser = $user
  160. cy.runOccCommand(`group:addUser '${groupName}' '${$user.userId}'`)
  161. })
  162. cy.login(admin)
  163. cy.visit('/settings/users')
  164. })
  165. after(() => cy.deleteUser(testUser))
  166. it('see that the group is in the list', () => {
  167. // see that the list of groups contains the group
  168. cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').contains('li', groupName).should('exist').scrollIntoView()
  169. })
  170. it('can delete the group', () => {
  171. // open the menu
  172. cy.get('ul[data-cy-users-settings-navigation-groups="custom"]')
  173. .contains('li', groupName)
  174. .find('button.action-item__menutoggle')
  175. .click({ force: true })
  176. // The "Remove group" action in the actions menu is shown and clicked
  177. cy.get('.action-item__popper button').contains('Remove group').should('exist').click({ force: true })
  178. // And confirmation dialog accepted
  179. cy.get('.modal-container button').contains('Confirm').click({ force: true })
  180. // Make sure no confirmation modal is shown
  181. handlePasswordConfirmation(admin.password)
  182. })
  183. it('deleted group is not shown anymore', () => {
  184. cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => {
  185. // see that the list of groups does not contain the group foo
  186. cy.contains(groupName).should('not.exist')
  187. })
  188. // and also not in database
  189. cy.runOccCommand('group:list --output=json').then(($response) => {
  190. const groups: string[] = Object.keys(JSON.parse($response.stdout))
  191. expect(groups).to.not.include(groupName)
  192. })
  193. })
  194. })
  195. describe.only('Settings: Sort groups in the UI', () => {
  196. before(() => {
  197. // Clear state
  198. cy.runOccCommand('group:list --output json').then((output) => {
  199. const groups = Object.keys(JSON.parse(output.stdout)).filter((group) => group !== 'admin')
  200. groups.forEach((group) => {
  201. cy.runOccCommand(`group:delete "${group}"`)
  202. })
  203. })
  204. // Add two groups and add one user to group B
  205. cy.runOccCommand('group:add A')
  206. cy.runOccCommand('group:add B')
  207. cy.createRandomUser().then((user) => {
  208. cy.runOccCommand(`group:adduser B "${user.userId}"`)
  209. })
  210. // Visit the settings as admin
  211. cy.login(admin)
  212. cy.visit('/settings/users')
  213. })
  214. it('Can set sort by member count', () => {
  215. // open the settings dialog
  216. cy.contains('button', 'Account management settings').click()
  217. cy.contains('.modal-container', 'Account management settings').within(() => {
  218. cy.get('[data-test="sortGroupsByMemberCount"] input[type="radio"]').scrollIntoView()
  219. cy.get('[data-test="sortGroupsByMemberCount"] input[type="radio"]').check({ force: true })
  220. // close the settings dialog
  221. cy.get('button.modal-container__close').click()
  222. })
  223. cy.waitUntil(() => cy.get('.modal-container').should(el => assertNotExistOrNotVisible(el)))
  224. })
  225. it('See that the groups are sorted by the member count', () => {
  226. cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => {
  227. cy.get('li').eq(0).should('contain', 'B') // 1 member
  228. cy.get('li').eq(1).should('contain', 'A') // 0 members
  229. })
  230. })
  231. it('See that the order is preserved after a reload', () => {
  232. cy.reload()
  233. cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => {
  234. cy.get('li').eq(0).should('contain', 'B') // 1 member
  235. cy.get('li').eq(1).should('contain', 'A') // 0 members
  236. })
  237. })
  238. it('Can set sort by group name', () => {
  239. // open the settings dialog
  240. cy.contains('button', 'Account management settings').click()
  241. cy.contains('.modal-container', 'Account management settings').within(() => {
  242. cy.get('[data-test="sortGroupsByName"] input[type="radio"]').scrollIntoView()
  243. cy.get('[data-test="sortGroupsByName"] input[type="radio"]').check({ force: true })
  244. // close the settings dialog
  245. cy.get('button.modal-container__close').click()
  246. })
  247. cy.waitUntil(() => cy.get('.modal-container').should(el => assertNotExistOrNotVisible(el)))
  248. })
  249. it('See that the groups are sorted by the user count', () => {
  250. cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => {
  251. cy.get('li').eq(0).should('contain', 'A')
  252. cy.get('li').eq(1).should('contain', 'B')
  253. })
  254. })
  255. it('See that the order is preserved after a reload', () => {
  256. cy.reload()
  257. cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => {
  258. cy.get('li').eq(0).should('contain', 'A')
  259. cy.get('li').eq(1).should('contain', 'B')
  260. })
  261. })
  262. })