commands.ts 8.0 KB

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