suitest-js-api 3.0.2 → 3.1.2

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
@@ -38,6 +38,7 @@ import {SetScreenOrientationChain} from './typeDefinition/SetScreenOrientationCh
38
38
  import {ScreenOrientation} from './typeDefinition/constants/ScreenOrientation';
39
39
  import {CloseAppChain} from './typeDefinition/CloseAppChain';
40
40
  import {SuspendAppChain} from './typeDefinition/SuspendAppChain';
41
+ import {RelativePosition} from './typeDefinition/RelativePositionChain';
41
42
 
42
43
  // --------------- Suitest Interface ---------------------- //
43
44
 
@@ -53,6 +54,7 @@ declare namespace suitest {
53
54
  pairDevice(deviceId: string): Promise<DeviceData|SuitestError>;
54
55
  releaseDevice(): Promise<void|SuitestError>;
55
56
  startREPL(options?: ReplOptions): Promise<void>;
57
+ getAppConfig(): Promise<AppConfiguration|SuitestError>;
56
58
 
57
59
  // config
58
60
  getConfig(): ConfigureOptions;
@@ -82,6 +84,7 @@ declare namespace suitest {
82
84
  openUrl(absoluteUrl: string): OpenUrlChain;
83
85
  pollUrl(url: string, response: string): PollUrlChain;
84
86
  position(x: number, y: number): PositionChain;
87
+ relativePosition(x: number, y: number): RelativePosition;
85
88
  press(key: string): PressButtonChain;
86
89
  press(keys: string[]): PressButtonChain;
87
90
  sleep(milliseconds: number): SleepChain;
@@ -186,6 +189,7 @@ declare namespace suitest {
186
189
  openUrl(absoluteUrl: string): OpenUrlChain;
187
190
  pollUrl(url: string, response: string): PollUrlChain;
188
191
  position(x: number, y: number): PositionChain;
192
+ relativePosition(x: number, y: number): RelativePosition;
189
193
  press(key: string): PressButtonChain;
190
194
  press(keys: string[]): PressButtonChain;
191
195
  runTest(testId: string): RunTestChain;
@@ -268,6 +272,14 @@ declare namespace suitest {
268
272
  accessToken: string;
269
273
  }
270
274
 
275
+ interface AppConfiguration {
276
+ name: string;
277
+ url: string;
278
+ suitestify: boolean;
279
+ domainList: string[];
280
+ variables: Record<string, string>;
281
+ }
282
+
271
283
  interface ConfigureOptions {
272
284
  logLevel: 'silent'|'normal'|'verbose'|'debug'|'silly';
273
285
  disallowCrashReports: boolean;
@@ -11,6 +11,7 @@ const contentTypes = {
11
11
  eval: 'eval',
12
12
  testLine: 'testLine',
13
13
  takeScreenshot: 'takeScreenshot',
14
+ getConfiguration: 'getConfiguration',
14
15
  };
15
16
 
16
17
  Object.freeze(contentTypes);
@@ -0,0 +1,131 @@
1
+ const {compose} = require('ramda');
2
+ // Import utils
3
+ const makeChain = require('../utils/makeChain');
4
+ const {applyCountAndDelayAndActions, applyUntilCondition} = require('../utils/chainUtils');
5
+ const {validate, validators} = require('../validation');
6
+ const {getRequestType} = require('../utils/socketChainHelper');
7
+ // Import chain composers
8
+ const {
9
+ clickComposer,
10
+ moveToComposer,
11
+ repeatComposer,
12
+ intervalComposer,
13
+ makeToStringComposer,
14
+ makeThenComposer,
15
+ abandonComposer,
16
+ cloneComposer,
17
+ gettersComposer,
18
+ assertComposer,
19
+ makeToJSONComposer,
20
+ untilComposer,
21
+ } = require('../composers');
22
+ const {invalidInputMessage, relativePositionIsMalformed} = require('../texts');
23
+ const SuitestError = require('../utils/SuitestError');
24
+
25
+ /**
26
+ * @param {SUITEST_API} classInstance
27
+ */
28
+ const relativePositionFactory = (classInstance) => {
29
+ const toJSON = (data) => {
30
+ if (!data.isClick && !data.isMoveTo) {
31
+ throw new SuitestError(relativePositionIsMalformed(), SuitestError.INVALID_INPUT);
32
+ }
33
+
34
+ const lineType = data.isClick ? 'click' : 'moveTo';
35
+ const socketMessage = {
36
+ type: getRequestType(data, false),
37
+ request: {
38
+ type: lineType,
39
+ target: {
40
+ type: 'window',
41
+ coordinates: data.coordinates,
42
+ relative: true,
43
+ },
44
+ },
45
+ };
46
+
47
+ if (lineType === 'click') {
48
+ socketMessage.request = compose(
49
+ msg => applyUntilCondition(msg, data),
50
+ msg => applyCountAndDelayAndActions(msg, data),
51
+ )({...socketMessage.request});
52
+ }
53
+
54
+ return socketMessage;
55
+ };
56
+
57
+ // Build Composers
58
+ const toStringComposer = makeToStringComposer(toJSON);
59
+ const thenComposer = makeThenComposer(toJSON);
60
+ const toJSONComposer = makeToJSONComposer(toJSON);
61
+
62
+ /**
63
+ * Function accepts data object of future chain as input
64
+ * and returns a list of composers that should build the chain
65
+ * @param data
66
+ * @returns {*[]}
67
+ */
68
+ const getComposers = (data) => {
69
+ const output = [
70
+ toStringComposer,
71
+ thenComposer,
72
+ cloneComposer,
73
+ gettersComposer,
74
+ toJSONComposer,
75
+ ];
76
+
77
+ if (!data.isAssert) {
78
+ output.push(assertComposer);
79
+ }
80
+
81
+ if (!data.isAbandoned) {
82
+ output.push(abandonComposer);
83
+ }
84
+
85
+ if (!data.isClick && !data.isMoveTo) {
86
+ output.push(clickComposer, moveToComposer);
87
+ }
88
+
89
+ if (data.isClick) {
90
+ if (!data.interval) {
91
+ output.push(intervalComposer);
92
+ }
93
+
94
+ if (!data.repeat) {
95
+ output.push(repeatComposer);
96
+ }
97
+
98
+ if (!data.until) {
99
+ output.push(untilComposer);
100
+ }
101
+ }
102
+
103
+ return output;
104
+ };
105
+
106
+ /**
107
+ * Define relative position
108
+ * @param {number | string} x - horizontal position
109
+ * @param {number | string} y - vertical position
110
+ */
111
+ const relativePositionChain = (x, y) => {
112
+ return makeChain(classInstance, getComposers, {
113
+ type: 'position',
114
+ coordinates: {
115
+ x: validate(validators.ST_VAR_OR_NUMBER, x, invalidInputMessage('relativePosition', 'Position x')),
116
+ y: validate(validators.ST_VAR_OR_NUMBER, y, invalidInputMessage('relativePosition', 'Position y')),
117
+ },
118
+ });
119
+ };
120
+
121
+ return {
122
+ // relativePosition functions
123
+ relativePosition: relativePositionChain,
124
+ relativePositionAssert: (x, y) => relativePositionChain(x, y).toAssert(),
125
+
126
+ // For testing
127
+ toJSON,
128
+ };
129
+ };
130
+
131
+ module.exports = relativePositionFactory;
@@ -0,0 +1,23 @@
1
+ const wsContentTypes = require('../api/wsContentTypes');
2
+ const chainPromise = require('../utils/chainPromise');
3
+ const {getAppConfigError} = require('../texts');
4
+ const SuitestError = require('../utils/SuitestError');
5
+
6
+ async function getAppConfig({appContext, authContext, webSockets}) {
7
+ const authedContent = await authContext.authorizeWs({
8
+ type: wsContentTypes.getConfiguration,
9
+ configId: appContext._context.configId,
10
+ }, getAppConfig.name);
11
+
12
+ const response = await webSockets.send(authedContent);
13
+
14
+ if (response.result === 'error') {
15
+ throw new SuitestError(getAppConfigError());
16
+ }
17
+
18
+ return response.configuration;
19
+ }
20
+
21
+ module.exports = {
22
+ getAppConfig: chainPromise(getAppConfig),
23
+ };
@@ -5,12 +5,18 @@ const {invalidInputMessage} = require('../texts');
5
5
 
6
6
  /**
7
7
  * Defines click method
8
+ * @param {'up' | 'down' | 'left' | 'right'} direction
9
+ * @param {number | string} [distance]
8
10
  */
9
- const scrollComposer = makeModifierComposer(composers.TAP, ['scroll'], (_, meta, direction, distance) => ({
10
- ...meta,
11
- isScroll: true,
12
- direction: validate(validators.DIRECTIONS, direction, invalidInputMessage('scroll', 'direction')),
13
- distance: validate(validators.ST_VAR_OR_POSITIVE_NUMBER, distance, invalidInputMessage('scroll', 'distance')),
14
- }));
11
+ const scrollComposer = makeModifierComposer(composers.TAP, ['scroll'], (_, meta, direction, distance) => {
12
+ return {
13
+ ...meta,
14
+ isScroll: true,
15
+ direction: validate(validators.DIRECTIONS, direction, invalidInputMessage('scroll', 'direction')),
16
+ distance: distance !== undefined
17
+ ? validate(validators.ST_VAR_OR_POSITIVE_NUMBER, distance, invalidInputMessage('scroll', 'distance'))
18
+ : undefined,
19
+ };
20
+ });
15
21
 
16
22
  module.exports = scrollComposer;
@@ -4,6 +4,7 @@ const validationKeys = {
4
4
  NON_EMPTY_STRING: Symbol('nonEmptyString'),
5
5
  POSITIVE_NUMBER: Symbol('positiveNumber'),
6
6
  ST_VAR_OR_POSITIVE_NUMBER: Symbol('stVarOrPositiveNumber'),
7
+ ST_VAR_OR_NUMBER: Symbol('stVarOrNumber'),
7
8
  NON_EMPTY_STRING_OR_UNDEFINED: Symbol('nonEmptyStringOrUndefined'),
8
9
  NON_EMPTY_STRING_OR_NUll: Symbol('nonEmptyStringOrNull'),
9
10
  ARRAY_OF_BUTTONS: Symbol('arrayOfButtons'),
@@ -3,7 +3,7 @@ const parser = require('yargs-parser');
3
3
  // Specify which options should be treated as boolen.
4
4
  // This will enable setting boolean option value to true if not defined explicitly,
5
5
  // eg: '--disallow-crash-reports' will be parsed as '--disallow-crash-reports=true'
6
- const booleanOpts = ['disallow-crash-reports'];
6
+ const booleanOpts = ['disallow-crash-reports', 'include-changelist'];
7
7
  const inspectOpts = ['inspect', 'inspect-brk'];
8
8
 
9
9
  const parseOptions = argv => {
package/lib/texts.js CHANGED
@@ -74,6 +74,7 @@ module.exports = {
74
74
  assertElementMalformed: () => 'Assert element line is malformed',
75
75
 
76
76
  positionIsMalformed: () => 'position command is malformed',
77
+ relativePositionIsMalformed: () => 'relative position is malformed',
77
78
 
78
79
  unusedLeaves: leaves => `Some of your Suitest chains were not executed.
79
80
  Put an "await" in front of those chains or call .abandon() to suppress these warnings.
@@ -171,6 +172,7 @@ ${leaves}`,
171
172
  usingAppConfig: (id) => `Setting app configuration id: ${id}`,
172
173
  usedAppConfig: (id) => `Will use app configuration id: ${id}`,
173
174
  useAppConfigOverrides: () => 'Will apply configuration overrides',
175
+ getAppConfigError: () => 'getAppConfig was called before configId initialization',
174
176
 
175
177
  disconnectedFromDevice: () => 'Disconnected from device',
176
178
  disconnectingFromDevice: () => 'Disconnecting from device',
@@ -37,7 +37,7 @@ const tokenAllowed = {
37
37
  wsContentTypes.pairDevice, wsContentTypes.releaseDevice,
38
38
  wsContentTypes.selectConfiguration, wsContentTypes.query,
39
39
  wsContentTypes.eval, wsContentTypes.testLine,
40
- wsContentTypes.takeScreenshot,
40
+ wsContentTypes.takeScreenshot, wsContentTypes.getConfiguration,
41
41
  ],
42
42
  };
43
43
 
@@ -153,18 +153,29 @@ const validatePositiveNumber = (val, name) => {
153
153
  return val;
154
154
  };
155
155
 
156
- const validateStVarOrPositiveNumber = (val, name) => {
156
+ const validateNumber = (val, name) => {
157
+ if (!Number.isFinite(val)) {
158
+ throwError(name + ' should be number');
159
+ }
160
+
161
+ return val;
162
+ };
163
+
164
+ const createStVarOrNumberValidator = (onlyPositiveNumber = false) => (val, name) => {
157
165
  if (!['number', 'string'].includes(typeof val)) {
158
166
  throwError(name + ' should be suitest configuration variable or number');
159
167
  } else if (typeof val === 'number') {
160
- validatePositiveNumber(val, name);
161
- } else if (typeof val === 'string' && !/(<%.+%>)/.test(val)) {
168
+ (onlyPositiveNumber ? validatePositiveNumber : validateNumber)(val, name);
169
+ } else if (typeof val === 'string' && !/^<%.+%>$/.test(val)) {
162
170
  throwError(name + ' should be suitest configuration variable');
163
171
  }
164
172
 
165
173
  return val;
166
174
  };
167
175
 
176
+ const validateStVarOrNumber = createStVarOrNumberValidator();
177
+ const validateStVarOrPositiveNumber = createStVarOrNumberValidator(true);
178
+
168
179
  const validateNonEmptyStringOrUndefined = (val, name) => {
169
180
  if (typeof val === 'string' && val.length || val === undefined) {
170
181
  return val;
@@ -271,11 +282,13 @@ const validateTapTypeAndDuration = ({tapType, tapDuration}, tapTypeErrorMsg, dur
271
282
  module.exports = {
272
283
  validateJsonSchema,
273
284
  validatePositiveNumber,
285
+ validateNumber,
274
286
  validateNonEmptyStringOrUndefined,
275
287
  validateNonEmptyStringOrNull,
276
288
  validateUntilConditionChain,
277
289
  validateRepoProps,
278
290
  validateNonEmptyArrayOfStrings,
291
+ validateStVarOrNumber,
279
292
  validateStVarOrPositiveNumber,
280
293
  validateTapTypeAndDuration,
281
294
  };
@@ -14,6 +14,9 @@ const validatorsMap = {
14
14
  [validationKeys.ST_VAR_OR_POSITIVE_NUMBER]: (value, text) => {
15
15
  return validators.validateStVarOrPositiveNumber(value, text);
16
16
  },
17
+ [validationKeys.ST_VAR_OR_NUMBER]: (value, text) => {
18
+ return validators.validateStVarOrNumber(value, text);
19
+ },
17
20
  [validationKeys.NON_EMPTY_STRING_OR_UNDEFINED]: (value, text) => {
18
21
  return validators.validateNonEmptyStringOrUndefined(value, text);
19
22
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suitest-js-api",
3
- "version": "3.0.2",
3
+ "version": "3.1.2",
4
4
  "main": "index.js",
5
5
  "repository": "git@github.com:SuitestAutomation/suitest-js-api.git",
6
6
  "author": "Suitest <hello@suite.st>",
@@ -85,7 +85,7 @@
85
85
  },
86
86
  "dependencies": {
87
87
  "@suitest/smst-to-text": "^4.2.2",
88
- "@suitest/translate": "^4.4.0",
88
+ "@suitest/translate": "^4.4.2",
89
89
  "@types/node": "^14.0.10",
90
90
  "ajv": "^6.12.2",
91
91
  "ansi-regex": "^5.0.0",
package/suitest.js CHANGED
@@ -7,6 +7,7 @@ const {closeSession} = require('./lib/commands/closeSession');
7
7
  const {pairDevice} = require('./lib/commands/pairDevice');
8
8
  const releaseDevice = require('./lib/commands/releaseDevice');
9
9
  const {setAppConfig} = require('./lib/commands/setAppConfig');
10
+ const {getAppConfig} = require('./lib/commands/getAppConfig');
10
11
 
11
12
  // Chains
12
13
  const openAppFactory = require('./lib/chains/openAppChain');
@@ -22,6 +23,7 @@ const cookieFactory = require('./lib/chains/cookieChain');
22
23
  const sleepFactory = require('./lib/chains/sleepChain');
23
24
  const pressButtonFactory = require('./lib/chains/pressButtonChain');
24
25
  const positionFactory = require('./lib/chains/positionChain');
26
+ const relativePositionFactory = require('./lib/chains/relativePositionChain');
25
27
  const windowFactory = require('./lib/chains/windowChain');
26
28
  const executeCommandFactory = require('./lib/chains/executeCommandChain');
27
29
  const runTestFactory = require('./lib/chains/runTestChain');
@@ -98,6 +100,7 @@ class SUITEST_API extends EventEmitter {
98
100
  this.setAppConfig = (...args) => setAppConfig(this, ...args);
99
101
  this.closeSession = (...args) => closeSession(this, ...args);
100
102
  this.releaseDevice = (...args) => releaseDevice(this, ...args);
103
+ this.getAppConfig = (...args) => getAppConfig(this, ...args);
101
104
 
102
105
  const {openApp, openAppAssert} = openAppFactory(this);
103
106
  const {closeApp, closeAppAssert} = closeAppFactory(this);
@@ -110,6 +113,7 @@ class SUITEST_API extends EventEmitter {
110
113
  const {sleep, sleepAssert} = sleepFactory(this);
111
114
  const {pressButton, pressButtonAssert} = pressButtonFactory(this);
112
115
  const {position, positionAssert} = positionFactory(this);
116
+ const {relativePosition, relativePositionAssert} = relativePositionFactory(this);
113
117
  const {window, windowAssert} = windowFactory(this);
114
118
  const {executeCommand, executeCommandAssert} = executeCommandFactory(this);
115
119
  const {jsExpression, jsExpressionAssert} = jsExpressionFactory(this);
@@ -134,6 +138,7 @@ class SUITEST_API extends EventEmitter {
134
138
  this.sleep = sleep;
135
139
  this.press = pressButton;
136
140
  this.position = position;
141
+ this.relativePosition = relativePosition;
137
142
  this.window = window;
138
143
  this.executeCommand = executeCommand;
139
144
  // this.executeBrightScript = executeBrightScriptFactory(this).executeBrightScript;
@@ -180,6 +185,7 @@ class SUITEST_API extends EventEmitter {
180
185
  sleep: sleepAssert,
181
186
  press: pressButtonAssert,
182
187
  position: positionAssert,
188
+ relativePosition: relativePositionAssert,
183
189
  window: windowAssert,
184
190
  executeCommand: executeCommandAssert,
185
191
  // executeBrightScript: executeBrightScriptFactory(this).executeBrightScriptAssert,
@@ -0,0 +1,48 @@
1
+ import {
2
+ AbstractChain,
3
+ Clickable,
4
+ MoveToModifier,
5
+ Repeatable,
6
+ Intervalable,
7
+ BaseChain,
8
+ UntilModifier,
9
+ } from './modifiers';
10
+
11
+ export interface RelativePosition extends
12
+ RelativePositionBaseQueryChain<RelativePosition>,
13
+ Clickable<RelativePositionRepeatIntervalChain>, // click
14
+ MoveToModifier<RelativePositionEmptyChain> // moveTo
15
+ {}
16
+
17
+ interface RelativePositionRepeatIntervalChain extends
18
+ RelativePositionBaseEvalChain<RelativePositionRepeatIntervalChain>,
19
+ Repeatable<RelativePositionIntervalChain>,
20
+ Intervalable<RelativePositionRepeatChain>,
21
+ UntilModifier<Repeatable<RelativePositionIntervalChain> & Intervalable<RelativePositionRepeatChain>>
22
+ {}
23
+
24
+ // -repeat +interval
25
+ interface RelativePositionIntervalChain extends
26
+ RelativePositionBaseEvalChain<RelativePositionIntervalChain>,
27
+ Intervalable<RelativePositionEmptyChain>
28
+ {}
29
+
30
+ // +repeat -interval
31
+ interface RelativePositionRepeatChain extends
32
+ RelativePositionBaseEvalChain<RelativePositionRepeatChain>,
33
+ Repeatable<RelativePositionEmptyChain>
34
+ {}
35
+
36
+ interface RelativePositionBaseQueryChain<TSelf> extends
37
+ BaseChain<TSelf, RelativePositionQueryResult, RelativePositionAbandonedChain>
38
+ {}
39
+
40
+ interface RelativePositionBaseEvalChain<TSelf> extends
41
+ BaseChain<TSelf, RelativePositionEvalResult, RelativePositionAbandonedChain>
42
+ {}
43
+
44
+ interface RelativePositionEmptyChain extends RelativePositionBaseEvalChain<RelativePositionEmptyChain> {}
45
+ interface RelativePositionAbandonedChain extends AbstractChain {}
46
+
47
+ type RelativePositionQueryResult = string;
48
+ type RelativePositionEvalResult = boolean | void;