suitest-js-api 4.2.3 → 4.3.0

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.d.ts CHANGED
@@ -165,36 +165,7 @@ declare namespace suitest {
165
165
  image(imageData: ImageData): ImageChain;
166
166
  image(apiId: string): ImageChain;
167
167
 
168
- getPairedDevice(): null | {
169
- deviceId: string,
170
- manufacturer: string,
171
- model: string,
172
- owner: string,
173
- firmware: string,
174
- osVersion?: string,
175
- modelId: string,
176
- platforms: string[],
177
- customName: string,
178
- ipAddress: string,
179
- controlUnitIds: string[],
180
- inactivityTimeout?: number,
181
- status: string,
182
- displayName?: string,
183
- shortDisplayName?: string,
184
- customUserInfo?: {
185
- location?: string,
186
- team?: string,
187
- responsibleUser?: string,
188
- osInfo?: string,
189
- otherInfo?: string,
190
- },
191
- inUseBy?: {
192
- description?: string,
193
- email?: string,
194
- orgName?: string,
195
- tokenName?: string,
196
- },
197
- }
168
+ getPairedDevice(): PairedDevice | null;
198
169
 
199
170
  // constants
200
171
  PROP: elementTypes.ElementPropTypes;
@@ -223,8 +194,8 @@ declare namespace suitest {
223
194
  LANG: Lang;
224
195
 
225
196
  authContext: AuthContext;
226
- appContext: Context;
227
- pairedDeviceContext: Context;
197
+ appContext: Context<AppContextData>;
198
+ pairedDeviceContext: Context<PairedDevice>;
228
199
 
229
200
  on(eventName: 'consoleLog', listener: (consoleLog: ConsoleLogEvent) => void): this;
230
201
  on(eventName: 'networkLog', listener: (networkLog: NetworkLogEvent) => void): this;
@@ -310,6 +281,58 @@ declare namespace suitest {
310
281
  recordingUrl?: string;
311
282
  }
312
283
 
284
+ interface PairedDevice {
285
+ deviceId: string;
286
+ manufacturer: string;
287
+ model: string;
288
+ owner: string;
289
+ firmware: string;
290
+ osVersion?: string;
291
+ modelId: string;
292
+ platforms: string[];
293
+ customName: string;
294
+ ipAddress: string;
295
+ controlUnitIds: string[];
296
+ inactivityTimeout?: number;
297
+ status: string;
298
+ displayName?: string;
299
+ shortDisplayName?: string;
300
+ customUserInfo?: {
301
+ location?: string;
302
+ team?: string;
303
+ responsibleUser?: string;
304
+ osInfo?: string;
305
+ otherInfo?: string;
306
+ };
307
+ inUseBy?: {
308
+ description?: string;
309
+ email?: string;
310
+ orgName?: string;
311
+ tokenName?: string;
312
+ };
313
+ }
314
+
315
+ interface ConfigVariable {
316
+ key: string;
317
+ value: string;
318
+ }
319
+
320
+ interface EffectiveAppConfiguration {
321
+ configVariables: ConfigVariable[];
322
+ }
323
+
324
+ interface AppContextData {
325
+ appId: string;
326
+ versionId: string;
327
+ configId: string;
328
+ configOverride: ConfigOverride;
329
+ /**
330
+ * The locally available part of the application configuration used by the current test execution.
331
+ * It combines values from the selected cloud configuration with local overrides, with override values taking precedence.
332
+ */
333
+ effectiveAppConfig: EffectiveAppConfiguration;
334
+ }
335
+
313
336
  interface ConfigOverride {
314
337
  url?: string;
315
338
  suitestify?: boolean;
@@ -321,10 +344,7 @@ declare namespace suitest {
321
344
  toUrl: string;
322
345
  }>;
323
346
  codeOverrides?: object;
324
- configVariables?: Array<{
325
- key: string;
326
- value: string;
327
- }>;
347
+ configVariables?: ConfigVariable[];
328
348
  openAppOverrideTest?: string;
329
349
  [key: string]: any; // user should have ability to pass any property to config object
330
350
  }
@@ -396,9 +416,9 @@ declare namespace suitest {
396
416
  defaultTimeout: number;
397
417
  }
398
418
 
399
- interface Context {
400
- context: unknown;
401
- setContext(context: unknown): void;
419
+ interface Context<T = unknown> {
420
+ context: T | null;
421
+ setContext(context: T): void;
402
422
  clear(): void;
403
423
  }
404
424
 
@@ -3,10 +3,10 @@ const chainPromise = require('../utils/chainPromise');
3
3
  const {getAppConfigError} = require('../texts');
4
4
  const SuitestError = require('../utils/SuitestError');
5
5
 
6
- async function getAppConfig({appContext, authContext, webSockets}) {
6
+ async function getAppConfig({appContext, authContext, webSockets}, configId = appContext._context.configId) {
7
7
  const authedContent = await authContext.authorizeWs({
8
8
  type: wsContentTypes.getConfiguration,
9
- configId: appContext._context.configId,
9
+ configId,
10
10
  }, getAppConfig.name);
11
11
 
12
12
  const response = await webSockets.send(authedContent);
@@ -20,4 +20,5 @@ async function getAppConfig({appContext, authContext, webSockets}) {
20
20
 
21
21
  module.exports = {
22
22
  getAppConfig: chainPromise(getAppConfig),
23
+ getAppConfigUnchained: getAppConfig,
23
24
  };
@@ -6,6 +6,15 @@ const {validate, validators} = require('../validation');
6
6
  const wsContentTypes = require('../api/wsContentTypes');
7
7
  const chainPromise = require('../utils/chainPromise');
8
8
  const {invalidInputMessage, usingAppConfig, usedAppConfig, useAppConfigOverrides} = require('../texts');
9
+ const {getAppConfigUnchained: getAppConfig} = require('./getAppConfig');
10
+
11
+ const mergeConfigVariables = (variables = {}, overrideVariables = []) => {
12
+ const effectiveVariables = new Map(Object.entries(variables));
13
+
14
+ overrideVariables.forEach(({key, value}) => effectiveVariables.set(key, value));
15
+
16
+ return Array.from(effectiveVariables, ([key, value]) => ({key, value}));
17
+ };
9
18
 
10
19
  /**
11
20
  * Set application config
@@ -30,7 +39,8 @@ async function setAppConfig({appContext, authContext, webSockets, logger, config
30
39
  }, setAppConfig.name);
31
40
 
32
41
  // make ws request
33
- const {appId, versionId} = await webSockets.send(authedContent);
42
+ const {versionId, appId} = await webSockets.send(authedContent);
43
+ const cloudConfig = await getAppConfig({appContext, authContext, webSockets}, configId);
34
44
 
35
45
  // set app context
36
46
  appContext.setContext({
@@ -38,6 +48,12 @@ async function setAppConfig({appContext, authContext, webSockets, logger, config
38
48
  versionId,
39
49
  configId,
40
50
  configOverride,
51
+ effectiveAppConfig: {
52
+ configVariables: mergeConfigVariables(
53
+ cloudConfig && cloudConfig.variables || {},
54
+ configOverride.configVariables,
55
+ ),
56
+ },
41
57
  });
42
58
 
43
59
  logger.log(usedAppConfig(configId));
@@ -43,7 +43,11 @@ const makeThenComposer = (getSocketMessage, callback, beforeSend) => makeMethodC
43
43
  if (beforeSend) {
44
44
  beforeSend(data);
45
45
  } else {
46
- const translation = translateLine(dataToTranslate, config.logLevel);
46
+ const translation = translateLine(
47
+ dataToTranslate,
48
+ config.logLevel,
49
+ appContext.context ? appContext.context.effectiveAppConfig : null,
50
+ );
47
51
 
48
52
  if (translation) {
49
53
  logger.log(translation);
@@ -52,7 +56,11 @@ const makeThenComposer = (getSocketMessage, callback, beforeSend) => makeMethodC
52
56
 
53
57
  promise = authContext.authorizeWs(jsonSocketMessage, data.type)
54
58
  .then(() => {
55
- const defaultResponseHandler = processServerResponse(logger, config.logLevel);
59
+ const defaultResponseHandler = processServerResponse(
60
+ logger,
61
+ config.logLevel,
62
+ appContext.context ? appContext.context.effectiveAppConfig : null,
63
+ );
56
64
 
57
65
  return webSockets.send(message).then(result => {
58
66
  return callback
@@ -38,6 +38,7 @@ const ELEMENT_PROP = {
38
38
  ID: 'id',
39
39
  IMAGE: 'image',
40
40
  IMAGE_HASH: 'imageHash',
41
+ IS_ACCESSIBLE: 'isAccessible',
41
42
  IS_CHECKED: 'isChecked',
42
43
  IS_CHECKED_ANDROID_OTTIUM: 'checked',
43
44
  IS_CLICKABLE: 'isClickable',
@@ -54,6 +55,7 @@ const ELEMENT_PROP = {
54
55
  IS_SELECTED_ANDROID_OTTIUM: 'selected',
55
56
  IS_TOUCHABLE: 'isTouchable',
56
57
  ITEM_FOCUSED: 'itemFocused',
58
+ LABEL: 'label',
57
59
  LEFT: 'left',
58
60
  LEFT_ABSOLUTE: 'leftAbsolute',
59
61
  MARGIN: 'margin',
@@ -102,6 +104,8 @@ const ELEMENT_PROP = {
102
104
  IS_LONG_CLICKABLE_ANDROID_OTTIUM: 'long-clickable',
103
105
  IS_SCROLLABLE_ANDROID_OTTIUM: 'scrollable',
104
106
  BOUNDS: 'bounds',
107
+ CLASS_CHAIN: 'classChain',
108
+ PREDICATE_STRING: 'predicateString',
105
109
  ROLE: 'role',
106
110
  DESCRIPTION: 'description',
107
111
  ORIENTATION_TEXT: 'orientationText',
@@ -246,22 +246,23 @@ const mapLogLevelsToTranslationModule = {
246
246
  * Use translation module to translate strings
247
247
  * @param json
248
248
  * @param logLevel
249
+ * @param appConfig
249
250
  */
250
- const translateLine = (json, logLevel) => {
251
+ const translateLine = (json, logLevel, appConfig) => {
251
252
  if (logLevel === 'silent') {
252
253
  // no progress messages
253
254
  return '';
254
255
  }
255
256
  const testLine = processJsonMessageForToString(json);
256
257
  const line = text.toText(
257
- translate.translateTestLine({testLine}),
258
+ translate.translateTestLine({testLine, appConfig}),
258
259
  {format: true, verbosity: mapLogLevelsToTranslationModule[logLevel]});
259
260
  const prefix = getRequestOpType(json);
260
261
 
261
262
  return `${prefix}${line}`;
262
263
  };
263
264
 
264
- const translateLineResult = (json, logLevel, lineResult) => {
265
+ const translateLineResult = (json, logLevel, lineResult, appConfig) => {
265
266
  const testLine = processJsonMessageForToString(json);
266
267
  let verbosity = mapLogLevelsToTranslationModule[logLevel];
267
268
 
@@ -270,7 +271,7 @@ const translateLineResult = (json, logLevel, lineResult) => {
270
271
  }
271
272
 
272
273
  return text.toText(
273
- translate.translateTestLineResult({testLine, lineResult}),
274
+ translate.translateTestLineResult({testLine, lineResult, appConfig}),
274
275
  {format: true, verbosity},
275
276
  );
276
277
  };
@@ -20,9 +20,10 @@ const assertionErrorTypes = ['queryFailed', 'appRunning', 'appNotRunning', 'quer
20
20
  * Process network api websocket chain response
21
21
  * @param logger
22
22
  * @param {verbosity} verbosity level
23
+ * @param appConfig
23
24
  * @returns {any}
24
25
  */
25
- const processServerResponse = (logger, verbosity) =>
26
+ const processServerResponse = (logger, verbosity, appConfig) =>
26
27
  function processServerResponseHandler(res, data, jsonMessage, snippets) {
27
28
  const isSnippet = data.type === 'runSnippet';
28
29
  const isAssertionError = isSnippet || assertionErrorTypes.includes(normalizeErrorType(res));
@@ -32,6 +33,7 @@ const processServerResponse = (logger, verbosity) =>
32
33
  verbosity,
33
34
  jsonMessage,
34
35
  snippets,
36
+ appConfig,
35
37
  });
36
38
  const throwErr = err => {
37
39
  err.stack = data.stack;
@@ -102,6 +104,7 @@ const processServerResponse = (logger, verbosity) =>
102
104
  results: [],
103
105
  testId: jsonMessage.request.val,
104
106
  level: 1,
107
+ appConfig,
105
108
  });
106
109
 
107
110
  logger.log(snippet);
@@ -40,7 +40,7 @@ const testSnippetError = 'testSnippetError';
40
40
  * @param {string} data.verbosity - string verbosity level
41
41
  * @param {function} translate - added as function argument for testing
42
42
  */
43
- const getSnippetLogs = ({testId, definitions, results, level, verbosity}, translate = chainUtils.translateLineResult) => {
43
+ const getSnippetLogs = ({testId, definitions, results, level, verbosity, appConfig}, translate = chainUtils.translateLineResult) => {
44
44
  const indent = ' '.repeat(level);
45
45
  // Indent all text by indent constant
46
46
  const applyIndent = (str) => str.split('\n').map(l => indent + l).join('\n');
@@ -69,7 +69,7 @@ const getSnippetLogs = ({testId, definitions, results, level, verbosity}, transl
69
69
  }).filter(Boolean);
70
70
 
71
71
  return definitionsWithResults.reduce((logs, {def, result}) => {
72
- logs.push(applyIndent(translate(def, verbosity, result)));
72
+ logs.push(applyIndent(translate(def, verbosity, result, appConfig)));
73
73
 
74
74
  if (result && (result.results || result.loopResults)) {
75
75
  const getLoopLogs = loopRes => getSnippetLogs({
@@ -78,6 +78,7 @@ const getSnippetLogs = ({testId, definitions, results, level, verbosity}, transl
78
78
  results: loopRes,
79
79
  level: level + 1,
80
80
  verbosity,
81
+ appConfig,
81
82
  }, translate);
82
83
 
83
84
  if (result.loopResults) {
@@ -120,10 +121,10 @@ function isCommandError(response) {
120
121
  * @param {Array} snippets
121
122
  * @returns {string}
122
123
  */
123
- function getErrorMessage({response, jsonMessage, verbosity, snippets}) {
124
+ function getErrorMessage({response, jsonMessage, verbosity, snippets, appConfig}) {
124
125
  if (response.results && snippets) {
125
126
  return getSnippetLogs(
126
- {verbosity, definitions: snippets, results: response.results, testId: jsonMessage.request.val, level: 1},
127
+ {verbosity, definitions: snippets, results: response.results, testId: jsonMessage.request.val, level: 1, appConfig},
127
128
  );
128
129
  }
129
130
 
@@ -131,7 +132,7 @@ function getErrorMessage({response, jsonMessage, verbosity, snippets}) {
131
132
  return commandErrors[response.errorType]();
132
133
  }
133
134
 
134
- return chainUtils.translateLineResult(jsonMessage, verbosity, response);
135
+ return chainUtils.translateLineResult(jsonMessage, verbosity, response, appConfig);
135
136
  }
136
137
 
137
138
  /**
@@ -33,6 +33,7 @@ const ELEMENT_PROP_TYPES = {
33
33
  [ELEMENT_PROP.HINT]: 'string',
34
34
  [ELEMENT_PROP.HREF]: 'string',
35
35
  [ELEMENT_PROP.ID]: 'string',
36
+ [ELEMENT_PROP.IS_ACCESSIBLE]: 'boolean',
36
37
  [ELEMENT_PROP.IS_CHECKED]: 'boolean',
37
38
  [ELEMENT_PROP.IS_CHECKED_ANDROID_OTTIUM]: 'boolean',
38
39
  [ELEMENT_PROP.IS_CLICKABLE]: 'boolean',
@@ -46,6 +47,7 @@ const ELEMENT_PROP_TYPES = {
46
47
  [ELEMENT_PROP.IS_SELECTED_ANDROID_OTTIUM]: 'boolean',
47
48
  [ELEMENT_PROP.IS_TOUCHABLE]: 'boolean',
48
49
  [ELEMENT_PROP.IMAGE]: 'string',
50
+ [ELEMENT_PROP.LABEL]: 'string',
49
51
  [ELEMENT_PROP.LEFT]: 'number',
50
52
  [ELEMENT_PROP.MARGIN]: 'string',
51
53
  [ELEMENT_PROP.NAME]: 'string',
@@ -106,6 +108,8 @@ const ELEMENT_PROP_TYPES = {
106
108
  [ELEMENT_PROP.IS_LONG_CLICKABLE_ANDROID_OTTIUM]: 'boolean',
107
109
  [ELEMENT_PROP.IS_SCROLLABLE_ANDROID_OTTIUM]: 'boolean',
108
110
  [ELEMENT_PROP.BOUNDS]: 'string',
111
+ [ELEMENT_PROP.CLASS_CHAIN]: 'string',
112
+ [ELEMENT_PROP.PREDICATE_STRING]: 'string',
109
113
  [ELEMENT_PROP.ROLE]: 'string',
110
114
  [ELEMENT_PROP.DESCRIPTION]: 'string',
111
115
  [ELEMENT_PROP.ORIENTATION_TEXT]: 'string',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suitest-js-api",
3
- "version": "4.2.3",
3
+ "version": "4.3.0",
4
4
  "main": "index.js",
5
5
  "repository": "github:SuitestAutomation/suitest-js-api",
6
6
  "author": "Suitest <hello@suite.st>",
@@ -88,7 +88,6 @@
88
88
  "eslint": "^7.1.0",
89
89
  "husky": "^4.2.5",
90
90
  "mocha": "^11.7.6",
91
- "mock-fs": "^5.1.0",
92
91
  "mock-require": "^3.0.3",
93
92
  "mock-socket": "^7.1.0",
94
93
  "mock-spawn": "^0.2.6",
@@ -100,7 +99,7 @@
100
99
  "dependencies": {
101
100
  "@sentry/node": "^7.120.3",
102
101
  "@suitest/smst-to-text": "^4.32.0",
103
- "@suitest/translate": "^4.32.0",
102
+ "@suitest/translate": "^4.34.0",
104
103
  "@types/node": "^14.0.10",
105
104
  "ajv": "^6.14.0",
106
105
  "ansi-regex": "^5.0.0",
@@ -28,6 +28,7 @@ export type ElementPropTypes = {
28
28
  HINT: 'hint',
29
29
  HREF: 'href',
30
30
  ID: 'id',
31
+ IS_ACCESSIBLE: 'isAccessible',
31
32
  IS_CHECKED: 'isChecked',
32
33
  IS_CHECKED_ANDROID_OTTIUM: 'checked',
33
34
  IS_CLICKABLE: 'isClickable',
@@ -42,6 +43,7 @@ export type ElementPropTypes = {
42
43
  IS_TOUCHABLE: 'isTouchable',
43
44
  IMAGE: 'image',
44
45
  ITEM_FOCUSED:'itemFocused',
46
+ LABEL: 'label',
45
47
  LEFT: 'left',
46
48
  MARGIN:'margin',
47
49
  NAME: 'name',
@@ -100,6 +102,8 @@ export type ElementPropTypes = {
100
102
  IS_LONG_CLICKABLE_ANDROID_OTTIUM: 'long-clickable',
101
103
  IS_SCROLLABLE_ANDROID_OTTIUM: 'scrollable',
102
104
  BOUNDS: 'bounds',
105
+ CLASS_CHAIN: 'classChain',
106
+ PREDICATE_STRING: 'predicateString',
103
107
  ROLE: 'role',
104
108
  DESCRIPTION: 'description',
105
109
  ORIENTATION_TEXT: 'orientationText',
@@ -81,6 +81,8 @@ export type ElementProps = {
81
81
  extends: string,
82
82
  uiElementId: string,
83
83
  index: number,
84
+ isAccessible: boolean,
85
+ label: string,
84
86
  }
85
87
 
86
88
  export type PlayStationVideoProps = Pick<ElementProps, 'videoLength' | 'videoPos' | 'videoState' | 'videoUrl'>