commands.ts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /**
  2. * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
  3. * SPDX-License-Identifier: AGPL-3.0-or-later
  4. */
  5. // eslint-disable-next-line n/no-extraneous-import
  6. import axios from 'axios'
  7. import { addCommands, User } from '@nextcloud/cypress'
  8. import { basename } from 'path'
  9. // Add custom commands
  10. import '@testing-library/cypress/add-commands'
  11. import 'cypress-if'
  12. import 'cypress-wait-until'
  13. addCommands()
  14. const url = (Cypress.config('baseUrl') || '').replace(/\/index.php\/?$/g, '')
  15. Cypress.env('baseUrl', url)
  16. /**
  17. * Enable or disable a user
  18. * TODO: standardize in @nextcloud/cypress
  19. *
  20. * @param {User} user the user to dis- / enable
  21. * @param {boolean} enable True if the user should be enable, false to disable
  22. */
  23. Cypress.Commands.add('enableUser', (user: User, enable = true) => {
  24. const url = `${Cypress.config('baseUrl')}/ocs/v2.php/cloud/users/${user.userId}/${enable ? 'enable' : 'disable'}`.replace('index.php/', '')
  25. return cy.request({
  26. method: 'PUT',
  27. url,
  28. form: true,
  29. auth: {
  30. user: 'admin',
  31. password: 'admin',
  32. },
  33. headers: {
  34. 'OCS-ApiRequest': 'true',
  35. 'Content-Type': 'application/x-www-form-urlencoded',
  36. },
  37. }).then((response) => {
  38. cy.log(`Enabled user ${user}`, response.status)
  39. return cy.wrap(response)
  40. })
  41. })
  42. /**
  43. * cy.uploadedFile - uploads a file from the fixtures folder
  44. * TODO: standardize in @nextcloud/cypress
  45. *
  46. * @param {User} user the owner of the file, e.g. admin
  47. * @param {string} fixture the fixture file name, e.g. image1.jpg
  48. * @param {string} mimeType e.g. image/png
  49. * @param {string} [target] the target of the file relative to the user root
  50. */
  51. Cypress.Commands.add('uploadFile', (user, fixture = 'image.jpg', mimeType = 'image/jpeg', target = `/${fixture}`) => {
  52. // get fixture
  53. return cy.fixture(fixture, 'base64').then(async file => {
  54. // convert the base64 string to a blob
  55. const blob = Cypress.Blob.base64StringToBlob(file, mimeType)
  56. cy.uploadContent(user, blob, mimeType, target)
  57. })
  58. })
  59. Cypress.Commands.add('setFileAsFavorite', (user: User, target: string, favorite = true) => {
  60. // eslint-disable-next-line cypress/unsafe-to-chain-command
  61. cy.clearAllCookies()
  62. .then(async () => {
  63. try {
  64. const rootPath = `${Cypress.env('baseUrl')}/remote.php/dav/files/${encodeURIComponent(user.userId)}`
  65. const filePath = target.split('/').map(encodeURIComponent).join('/')
  66. const response = await axios({
  67. url: `${rootPath}${filePath}`,
  68. method: 'PROPPATCH',
  69. auth: {
  70. username: user.userId,
  71. password: user.password,
  72. },
  73. headers: {
  74. 'Content-Type': 'application/xml',
  75. },
  76. data: `<?xml version="1.0"?>
  77. <d:propertyupdate xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
  78. <d:set>
  79. <d:prop>
  80. <oc:favorite>${favorite ? 1 : 0}</oc:favorite>
  81. </d:prop>
  82. </d:set>
  83. </d:propertyupdate>`,
  84. })
  85. cy.log(`Created directory ${target}`, response)
  86. } catch (error) {
  87. cy.log('error', error)
  88. throw new Error('Unable to process fixture')
  89. }
  90. })
  91. })
  92. Cypress.Commands.add('mkdir', (user: User, target: string) => {
  93. // eslint-disable-next-line cypress/unsafe-to-chain-command
  94. cy.clearCookies()
  95. .then(async () => {
  96. try {
  97. const rootPath = `${Cypress.env('baseUrl')}/remote.php/dav/files/${encodeURIComponent(user.userId)}`
  98. const filePath = target.split('/').map(encodeURIComponent).join('/')
  99. const response = await axios({
  100. url: `${rootPath}${filePath}`,
  101. method: 'MKCOL',
  102. auth: {
  103. username: user.userId,
  104. password: user.password,
  105. },
  106. })
  107. cy.log(`Created directory ${target}`, response)
  108. } catch (error) {
  109. cy.log('error', error)
  110. throw new Error('Unable to create directory')
  111. }
  112. })
  113. })
  114. /**
  115. * cy.uploadedContent - uploads a raw content
  116. * TODO: standardize in @nextcloud/cypress
  117. *
  118. * @param {User} user the owner of the file, e.g. admin
  119. * @param {Blob} blob the content to upload
  120. * @param {string} mimeType e.g. image/png
  121. * @param {string} target the target of the file relative to the user root
  122. */
  123. Cypress.Commands.add('uploadContent', (user: User, blob: Blob, mimeType: string, target: string, mtime?: number) => {
  124. cy.clearCookies()
  125. return cy.then(async () => {
  126. const fileName = basename(target)
  127. // Process paths
  128. const rootPath = `${Cypress.env('baseUrl')}/remote.php/dav/files/${encodeURIComponent(user.userId)}`
  129. const filePath = target.split('/').map(encodeURIComponent).join('/')
  130. try {
  131. const file = new File([blob], fileName, { type: mimeType })
  132. const response = await axios({
  133. url: `${rootPath}${filePath}`,
  134. method: 'PUT',
  135. data: file,
  136. headers: {
  137. 'Content-Type': mimeType,
  138. 'X-OC-MTime': mtime ? `${mtime}` : undefined,
  139. },
  140. auth: {
  141. username: user.userId,
  142. password: user.password,
  143. },
  144. })
  145. cy.log(`Uploaded content as ${fileName}`, response)
  146. return response
  147. } catch (error) {
  148. cy.log('error', error)
  149. throw new Error('Unable to process fixture')
  150. }
  151. })
  152. })
  153. /**
  154. * Reset the admin theming entirely
  155. */
  156. Cypress.Commands.add('resetAdminTheming', () => {
  157. const admin = new User('admin', 'admin')
  158. cy.clearCookies()
  159. cy.login(admin)
  160. // Clear all settings
  161. cy.request('/csrftoken').then(({ body }) => {
  162. const requestToken = body.token
  163. axios({
  164. method: 'POST',
  165. url: '/index.php/apps/theming/ajax/undoAllChanges',
  166. headers: {
  167. requesttoken: requestToken,
  168. },
  169. })
  170. })
  171. // Clear admin session
  172. cy.clearCookies()
  173. })
  174. /**
  175. * Reset the current or provided user theming settings
  176. * It does not reset the theme config as it is enforced in the
  177. * server config for cypress testing.
  178. */
  179. Cypress.Commands.add('resetUserTheming', (user?: User) => {
  180. if (user) {
  181. cy.clearCookies()
  182. cy.login(user)
  183. }
  184. // Reset background config
  185. cy.request('/csrftoken').then(({ body }) => {
  186. const requestToken = body.token
  187. cy.request({
  188. method: 'POST',
  189. url: '/apps/theming/background/default',
  190. headers: {
  191. requesttoken: requestToken,
  192. },
  193. })
  194. })
  195. if (user) {
  196. // Clear current session
  197. cy.clearCookies()
  198. }
  199. })
  200. Cypress.Commands.add('runOccCommand', (command: string, options?: Partial<Cypress.ExecOptions>) => {
  201. const env = Object.entries(options?.env ?? {}).map(([name, value]) => `-e '${name}=${value}'`).join(' ')
  202. return cy.exec(`docker exec --user www-data ${env} nextcloud-cypress-tests-server php ./occ ${command}`, options)
  203. })
  204. Cypress.Commands.add('userFileExists', (user: string, path: string) => {
  205. user.replaceAll('"', '\\"')
  206. path.replaceAll('"', '\\"').replaceAll(/^\/+/gm, '')
  207. return cy.exec(`docker exec --user www-data nextcloud-cypress-tests-server stat --printf="%s" "data/${user}/files/${path}"`, { failOnNonZeroExit: true })
  208. .then((exec) => Number.parseInt(exec.stdout || '0'))
  209. })
  210. Cypress.Commands.add('backupDB', (): Cypress.Chainable<string> => {
  211. const randomString = Math.random().toString(36).substring(7)
  212. cy.exec(`docker exec --user www-data nextcloud-cypress-tests-server cp /var/www/html/data/owncloud.db /var/www/html/data/owncloud.db-${randomString}`)
  213. cy.log(`Created snapshot ${randomString}`)
  214. return cy.wrap(randomString)
  215. })
  216. Cypress.Commands.add('restoreDB', (snapshot: string = 'init') => {
  217. cy.exec(`docker exec --user www-data nextcloud-cypress-tests-server cp /var/www/html/data/owncloud.db-${snapshot} /var/www/html/data/owncloud.db`)
  218. cy.log(`Restored snapshot ${snapshot}`)
  219. })
  220. Cypress.Commands.add('backupData', (users: string[] = ['admin']) => {
  221. const snapshot = Math.random().toString(36).substring(7)
  222. const toBackup = users.map((user) => `'${user.replaceAll('\\', '').replaceAll('\'', '\\\'')}'`).join(' ')
  223. cy.exec(`docker exec --user www-data rm /var/www/html/data/data-${snapshot}.tar`, { failOnNonZeroExit: false })
  224. cy.exec(`docker exec --user www-data --workdir /var/www/html/data nextcloud-cypress-tests-server tar cf /var/www/html/data/data-${snapshot}.tar ${toBackup}`)
  225. return cy.wrap(snapshot as string)
  226. })
  227. Cypress.Commands.add('restoreData', (snapshot?: string) => {
  228. snapshot = snapshot ?? 'init'
  229. snapshot.replaceAll('\\', '').replaceAll('"', '\\"')
  230. cy.exec(`docker exec --user www-data --workdir /var/www/html/data nextcloud-cypress-tests-server rm -vfr $(tar --exclude='*/*' -tf '/var/www/html/data/data-${snapshot}.tar')`)
  231. cy.exec(`docker exec --user www-data --workdir /var/www/html/data nextcloud-cypress-tests-server tar -xf '/var/www/html/data/data-${snapshot}.tar'`)
  232. })