@open-xchange/appsuite-codeceptjs 0.6.17 → 0.6.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -89,6 +89,7 @@ module.exports = {
89
89
  I: '@open-xchange/appsuite-codeceptjs/src/actor',
90
90
  users: '@open-xchange/appsuite-codeceptjs/src/users',
91
91
  contexts: '@open-xchange/appsuite-codeceptjs/src/contexts',
92
+ sharedaccounts: '@open-xchange/appsuite-codeceptjs/src/sharedaccounts',
92
93
  ...pageobjects
93
94
  },
94
95
  async bootstrap () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-xchange/appsuite-codeceptjs",
3
- "version": "0.6.17",
3
+ "version": "0.6.19",
4
4
  "description": "OX App Suite CodeceptJS Configuration and Helpers",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -33,8 +33,8 @@
33
33
  "p-retry": "^7.1.1",
34
34
  "playwright-core": "1.58.2",
35
35
  "short-uuid": "^6.0.3",
36
- "@open-xchange/codecept-horizontal-scaler": "0.1.14",
37
- "@open-xchange/soap-client": "0.0.10"
36
+ "@open-xchange/soap-client": "0.0.11",
37
+ "@open-xchange/codecept-horizontal-scaler": "0.1.14"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^24.10.3",
package/src/actor.js CHANGED
@@ -32,9 +32,16 @@ module.exports = function (customSteps) {
32
32
  this.amOnPage(baseURL)
33
33
  },
34
34
 
35
- openApp (appName) {
35
+ waitForApp () {
36
+ this.waitForInvisible('#background-loader.busy', 30)
37
+ this.waitForVisible({ css: 'html.complete' }, 10)
38
+ },
39
+
40
+ openApp (appName, options) {
36
41
  this.click('~Navigate to:')
37
42
  this.click(appName, '.apps')
43
+ options = { wait: true, ...options }
44
+ if (options.wait) this.waitForApp()
38
45
  },
39
46
 
40
47
  /**
@@ -62,36 +69,33 @@ module.exports = function (customSteps) {
62
69
  * After successful login, the page will be navigated to the specified URL with the URL parameters.
63
70
  * If the 'isDeepLink' option is true, the URL will be constructed with a '#' prefix.
64
71
  * The login process also includes waiting for the page to load and checking for the presence of the app.
65
- * @param {string[]|object} urlParams - The URL parameters as an array or object.
72
+ * @param {string[]|string} urlParams - The URL parameters as an array or string.
66
73
  * @param {object} options - The login options.
67
- * @returns {Promise<void>} - A promise that resolves when the login process is complete.
68
74
  */
69
- async login (urlParams, options) {
75
+ login (urlParams, options) {
70
76
  if (urlParams && !urlParams.length) {
71
77
  // looks like an object, not string or array
72
78
  options = urlParams
73
- urlParams = []
79
+ urlParams = undefined
74
80
  }
81
+ options = { wait: true, ...options }
75
82
  urlParams = [].concat(urlParams || [])
76
- options = Object.assign({ wait: true }, options)
83
+ if (!options.isDeepLink) urlParams.unshift('!!')
77
84
  let user = util.resolveUser(options.user)
78
85
  if (user.toJSON) user = user.toJSON()
79
86
  const loginName = process.env.PROVISIONING_API === 'reseller' ? user.name : `${user.name}${user.context.id ? '@' + user.context.id : ''}`
80
- await this.makeApiRequest('POST', `${baseURL}/api/login`, {
87
+ this.makeApiRequest('POST', `${baseURL}/api/login`, {
81
88
  params: { action: 'login', client: 'open-xchange-appsuite' },
82
- form: {
83
- name: loginName,
84
- password: user.password
85
- }
89
+ form: { name: loginName, password: user.password }
86
90
  })
87
91
  this.wait(0.5)
88
- this.amOnPage(`${baseURL}/` + (options.isDeepLink ? '#' : '#!!') + (urlParams.length === 0 ? '' : '&' + urlParams.join('&')))
92
+ this.amOnPage(`${baseURL}/#${urlParams.join('&')}`)
89
93
  this.waitForInvisible('#background-loader.busy', util.getLoginTimeout())
90
- if (options.wait) await this.waitForApp()
94
+ if (options.wait) this.waitForVisible({ css: 'html.complete' }, 10)
91
95
  },
92
96
 
93
- async logout () {
94
- await this.makeApiRequest('POST', `${baseURL}/api/login`, { params: { action: 'logout' } })
97
+ logout () {
98
+ this.makeApiRequest('POST', `${baseURL}/api/login`, { params: { action: 'logout' } })
95
99
  this.amOnPage('about:blank')
96
100
  },
97
101
 
@@ -105,10 +109,9 @@ module.exports = function (customSteps) {
105
109
  *
106
110
  * @param {string} locator - The locator of the element. Only accepts css selectors.
107
111
  */
108
- async waitForFocus (locator) {
109
- this.waitForElement(locator)
112
+ waitForFocus (locator) {
110
113
  this.waitForVisible(locator)
111
- return this.usePlaywrightTo('wait for focus', async ({ page }) => {
114
+ this.usePlaywrightTo('wait for focus', async ({ page }) => {
112
115
  await expect(page.locator(locator)).toBeFocused()
113
116
  this.wait(0.5)
114
117
  })
@@ -131,7 +134,7 @@ module.exports = function (customSteps) {
131
134
  clickDropdown (text) {
132
135
  this.waitForVisible('.dropdown.open .dropdown-menu, .primary-action > .open .dropdown-menu')
133
136
  this.waitForText(text, 10, '.dropdown.open .dropdown-menu, .primary-action > .open .dropdown-menu')
134
- return this.click(text, '.dropdown.open .dropdown-menu, .primary-action > .open .dropdown-menu')
137
+ this.click(text, '.dropdown.open .dropdown-menu, .primary-action > .open .dropdown-menu')
135
138
  },
136
139
 
137
140
  clickToolbar (selector, timeout = 5) {
@@ -61,8 +61,10 @@ async function fetchWithRetry (url, options) {
61
61
  function createHttpClient (options) {
62
62
  let user = util.resolveUser(options?.user)
63
63
  if (user.toJSON) user = user.toJSON()
64
+ const params = options?.params
64
65
 
65
66
  async function request (url, options = {}) {
67
+ if (params) options.params = { ...params, ...options.params }
66
68
  if (user) {
67
69
  if (!cache[user.name]) await login(user)
68
70
  options.headers = { Cookie: cache[user.name].cookies, ...options.headers }
package/src/helper.js CHANGED
@@ -919,13 +919,6 @@ class AppSuiteHelper extends Helper {
919
919
  )
920
920
  }
921
921
 
922
- async waitForApp () {
923
- const { Playwright } = this.helpers
924
-
925
- await Playwright.waitForInvisible('#background-loader.busy', 30)
926
- await Playwright.waitForVisible({ css: 'html.complete' }, 10)
927
- }
928
-
929
922
  async copyToClipboard () {
930
923
  const helper = this.helpers.Playwright
931
924
  const { page } = helper
@@ -0,0 +1,180 @@
1
+ /**
2
+ * @copyright Copyright (c) Open-Xchange GmbH, Germany <info@open-xchange.com>
3
+ * @license AGPL-3.0
4
+ *
5
+ * This code is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Affero General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU Affero General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License
16
+ * along with OX App Suite. If not, see <https://www.gnu.org/licenses/agpl-3.0.txt>.
17
+ *
18
+ * Any use of the work other than as authorized under this license or copyright law is prohibited.
19
+ */
20
+
21
+ const short = require('short-uuid')
22
+ const { sharedAccountService } = require('@open-xchange/soap-client/common')
23
+ const { util } = require('../util')
24
+
25
+ const sharedAccounts = []
26
+
27
+ class SharedAccount {
28
+ constructor (opt) {
29
+ this.sharedAccountData = opt.sharedAccountData
30
+ this.context = opt.context
31
+ return new Proxy(this, {
32
+ get (target, prop) {
33
+ return prop in target ? target[prop] : target.sharedAccountData[prop]
34
+ }
35
+ })
36
+ }
37
+
38
+ static getRandom () {
39
+ const id = `e2e-${short.generate().slice(0, 9).toLowerCase()}`
40
+ const domain = util.mxDomain()
41
+ const name = `${id}-Anmeldung`
42
+
43
+ return {
44
+ imapServer: util.imapServer(),
45
+ smtpServer: util.smtpServer(),
46
+ language: 'de_DE',
47
+ mailenabled: 1,
48
+ name,
49
+ display_name: name,
50
+ imapLogin: name,
51
+ primaryEmail: `${id}-anmeldung@${domain}`,
52
+ email1: `${id}-anmeldung@${domain}`,
53
+ timezone: 'Europe/Berlin',
54
+ password: 'secret'
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Create a shared account with the specified account data:
60
+ *
61
+ * @param {object} sharedAccountData
62
+ * Properties:
63
+ * - display_name
64
+ * - imapServer
65
+ * - smtpServer
66
+ * - language
67
+ * - mailenabled
68
+ * - name
69
+ * - primaryEmail
70
+ * - email1
71
+ * - timezone
72
+ * - password
73
+ *
74
+ * @param {*} ctx
75
+ *
76
+ * @returns {Promise<Object>} A promise resolving with the created shared account object.
77
+ */
78
+ static async create (sharedAccountData, ctx = {}) {
79
+ sharedAccountData = { ...this.getRandom(), ...sharedAccountData }
80
+
81
+ const { contexts } = inject()
82
+ let sharedAccountContext
83
+ try {
84
+ // @ts-ignore
85
+ sharedAccountContext = await contexts.reuse(ctx, ctx.admin, ctx.auth)
86
+ } catch (e) {
87
+ // @ts-ignore
88
+ sharedAccountContext = await contexts.create(Object.assign({ maxQuota: 1000 }, ctx))
89
+ }
90
+
91
+ const data = await sharedAccountService.create(sharedAccountContext, sharedAccountData)
92
+ const sharedAccount = new SharedAccount({ sharedAccountData: data, context: sharedAccountContext })
93
+ sharedAccounts.push(sharedAccount)
94
+ return sharedAccount
95
+ }
96
+
97
+ /**
98
+ * Get the default configuration for creating shared account permissions for mail and calendar.
99
+ *
100
+ * @returns {Object} The default configuration object.
101
+ */
102
+ static getDefaultConfig () {
103
+ return {
104
+ permissionLevel: 'author',
105
+ // grantedCapability: ['sendAs', 'sendOnBehalf', 'snippets', 'writeSnippets'],
106
+ grantedCapability: [],
107
+ deniedCapability: []
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Add users and groups with specified permissions and capabilities to the shared account using the OX Shared Account Service.
113
+ *
114
+ * @returns {Promise<void>}
115
+ */
116
+ async createPermissions ({ users = [], groups = [], mail = SharedAccount.getDefaultConfig(), calendar = SharedAccount.getDefaultConfig() }) {
117
+ // use context of the first user or the first group
118
+ // -> in every call to 'createSharedAccountPermissions' all users and groups must be in the same context
119
+ const context = users[0].context || groups[0].context
120
+
121
+ return sharedAccountService.createSharedAccountPermissions(context, this.context, this.sharedAccountData, mail, calendar, users, groups)
122
+ }
123
+
124
+ /**
125
+ * List all shared accounts in the specified context.
126
+ *
127
+ * @returns {Promise<Object[]>} A promise resolving with the list of shared account objects.
128
+ */
129
+ static async list (context) {
130
+ return sharedAccountService.list(context)
131
+ }
132
+
133
+ /**
134
+ * Returns the fully qualified identifier of the shared account.
135
+ *
136
+ * @returns {string}
137
+ * The fully qualified identifier of the shared account.
138
+ */
139
+ getFullId () {
140
+ return this.sharedAccountData.id + '@' + this.context.id
141
+ }
142
+
143
+ /**
144
+ * Get all data of the shared account.
145
+ *
146
+ * @returns {Promise<Object>} A promise resolving with all data of the shared account.
147
+ */
148
+ async getData () {
149
+ return sharedAccountService.getData(this.context, this.sharedAccountData)
150
+ }
151
+
152
+ /**
153
+ * Remove the shared account.
154
+ *
155
+ * @returns {Promise<void>}
156
+ */
157
+ async remove () {
158
+ const index = sharedAccounts.indexOf(this)
159
+ if (index < 0) return
160
+ sharedAccounts.splice(index, 1)
161
+ return sharedAccountService.remove(this.context, this.sharedAccountData)
162
+ }
163
+
164
+ toJSON () {
165
+ return {
166
+ context: { ...this.context },
167
+ ...this.sharedAccountData
168
+ }
169
+ }
170
+ }
171
+
172
+ module.exports = () => {
173
+ return new Proxy(sharedAccounts, {
174
+ // act like an array if an index is being used
175
+ get (target, prop) {
176
+ if (prop in target) { return target[prop] }
177
+ return SharedAccount[prop]
178
+ }
179
+ })
180
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @copyright Copyright (c) Open-Xchange GmbH, Germany <info@open-xchange.com>
3
+ * @license AGPL-3.0
4
+ *
5
+ * This code is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Affero General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU Affero General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License
16
+ * along with OX App Suite. If not, see <https://www.gnu.org/licenses/agpl-3.0.txt>.
17
+ *
18
+ * Any use of the work other than as authorized under this license or copyright law is prohibited.
19
+ */
20
+
21
+ const moduleToLoad = './sharedaccounts/sharedaccounts'
22
+
23
+ module.exports = require(moduleToLoad)
package/steps.d.ts CHANGED
@@ -2,12 +2,11 @@
2
2
  type steps_file = typeof import('@codeceptjs/configure/test/integration/steps_file.js')
3
3
  type users = typeof import('./src/users.js')
4
4
  type contexts = typeof import('./src/contexts.js')
5
- type ResellerContext = typeof import('./src/reseller/context.js')
6
- type ResellerUser = typeof import('./src/reseller/user.js')
5
+ type sharedaccounts = typeof import('./src/sharedaccounts.js')
7
6
  type AppSuite = import('./src/helper.js')
8
7
 
9
8
  declare namespace CodeceptJS {
10
- interface SupportObject { I: I, current: any, users: users, contexts: contexts, ResellerContext: ResellerContext, ResellerUser: ResellerUser }
9
+ interface SupportObject { I: I, current: any, users: users, contexts: contexts, sharedaccounts: sharedaccounts }
11
10
  interface Methods extends Playwright, AppSuite {}
12
11
  interface I extends ReturnType<steps_file>, WithTranslation<AppSuite> {}
13
12
  namespace Translation {