suitest-js-api 3.11.7 → 3.12.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/README.md CHANGED
@@ -45,6 +45,7 @@ Vizio etc.
45
45
  - Locally installed Chrome, Firefox, Safari and Edge
46
46
  - Apple tvOS, iOS and iPadOS devices, including simulators
47
47
  - Roku TVs, boxes and sticks
48
+ - Xfinity, XClass, Xumo and Sky devices
48
49
 
49
50
  Suitest supports automating end-to-end testing of:
50
51
 
@@ -57,8 +58,11 @@ Suitest supports automating end-to-end testing of:
57
58
  - Xbox One and Xbox Series S and X apps
58
59
  - PlayStation 4 / 5 apps
59
60
  - Roku apps
61
+ - Sky apps
60
62
  - VIDAA apps
61
63
  - Vizio SmartCast apps
64
+ - Xfinity / XClass TV / Xumo TV apps
65
+ - Xumo (Entertainment OS) apps
62
66
  - Traditional websites and web apps for desktop browsers.
63
67
 
64
68
  ## Contributing
package/index.d.ts CHANGED
@@ -42,6 +42,11 @@ import {RelativePosition} from './typeDefinition/RelativePositionChain';
42
42
  import {LaunchMode} from './typeDefinition/constants/LaunchMode';
43
43
  import {OpenDeepLinkChain} from './typeDefinition/OpenDeepLink';
44
44
  import {CookieProp} from './typeDefinition/constants/CookieProp';
45
+ import {OcrChain} from './typeDefinition/OcrChain';
46
+ import {OcrReadAs} from './typeDefinition/constants/OcrReadAs';
47
+ import {StringPropComparators} from './typeDefinition/constants/PropComparators';
48
+ import {ValueOf} from './typeDefinition/utils';
49
+ import {OcrColor} from './typeDefinition/constants/OcrColor';
45
50
 
46
51
  // --------------- Suitest Interface ---------------------- //
47
52
 
@@ -120,6 +125,7 @@ declare namespace suitest {
120
125
  */
121
126
  saveScreenshot(fileName?: string): TakeScreenshotChain<void>;
122
127
  openDeepLink(deepLink?: string): OpenDeepLinkChain;
128
+ ocr(comparators: OcrCommonItem[]): OcrChain;
123
129
 
124
130
  getPairedDevice(): null | {
125
131
  deviceId: string,
@@ -157,6 +163,8 @@ declare namespace suitest {
157
163
  SCREEN_ORIENTATION: ScreenOrientation;
158
164
  LAUNCH_MODE: LaunchMode;
159
165
  COOKIE_PROP: CookieProp;
166
+ OCR_READ_AS: OcrReadAs;
167
+ OCR_COLOR: OcrColor;
160
168
 
161
169
  authContext: AuthContext;
162
170
  appContext: Context;
@@ -203,6 +211,7 @@ declare namespace suitest {
203
211
  window(): WindowChain;
204
212
  setScreenOrientation(orientation: ScreenOrientationValues): SetScreenOrientationChain;
205
213
  openDeepLink(deepLink?: string): OpenDeepLinkChain;
214
+ ocr(comparators: OcrCommonItem[]): OcrChain;
206
215
  }
207
216
 
208
217
  type NetworkLogEvent = {
@@ -336,4 +345,15 @@ declare namespace suitest {
336
345
  | 'portraitReversed'
337
346
  | 'landscape'
338
347
  | 'landscapeReversed';
348
+
349
+ type OcrCommonItem = {
350
+ val: string,
351
+ type?: StringPropComparators,
352
+ readAs?: ValueOf<OcrReadAs>,
353
+ align?: boolean,
354
+ color?: ValueOf<OcrColor>,
355
+ whitelist?: string,
356
+ blacklist?: string,
357
+ region?: [x: number, y: number, width: number, height: number],
358
+ }
339
359
  }
@@ -0,0 +1,144 @@
1
+ const {
2
+ makeToStringComposer,
3
+ makeThenComposer,
4
+ makeToJSONComposer,
5
+ cloneComposer,
6
+ assertComposer,
7
+ abandonComposer,
8
+ timeoutComposer,
9
+ } = require('../composers');
10
+ const makeChain = require('../utils/makeChain');
11
+ const {getRequestType} = require('../utils/socketChainHelper');
12
+ const {validate, validators} = require('../validation');
13
+ const {invalidInputMessage, assertOcrMalformed} = require('../texts');
14
+ const {applyTimeout} = require('../utils/chainUtils');
15
+ const SuitestError = require('../utils/SuitestError');
16
+ const {PROP_COMPARATOR} = require('../constants/comparator');
17
+
18
+ const validateOcrOptions = (ocrOptions) => {
19
+ return ocrOptions === undefined ? undefined : validate(
20
+ validators.OCR_OPTIONS,
21
+ ocrOptions,
22
+ invalidInputMessage('ocr', 'OCR data'),
23
+ );
24
+ };
25
+
26
+ const validateOcrComparators = (ocrComparators) => {
27
+ if (!Array.isArray(ocrComparators) || ocrComparators.length === 0) {
28
+ throw new SuitestError(assertOcrMalformed(), SuitestError.INVALID_INPUT);
29
+ }
30
+
31
+ return validate(
32
+ validators.OCR_COMPARATORS,
33
+ ocrComparators,
34
+ invalidInputMessage('ocr', 'OCR data'),
35
+ );
36
+ };
37
+
38
+ /**
39
+ * @description return copy of passed ocr comparator with default "type" property
40
+ * @param {import('../../index.d.ts').OcrCommonItem} ocrItem
41
+ * @returns {import('../../index.d.ts').OcrCommonItem}
42
+ */
43
+ const ocrItemWithDefaultType = (ocrItem) => {
44
+ const ocrItemCopy = {...ocrItem};
45
+
46
+ if (ocrItemCopy.val !== undefined) {
47
+ ocrItemCopy.type = ocrItemCopy.type || PROP_COMPARATOR.EQUAL;
48
+ }
49
+
50
+ return ocrItemCopy;
51
+ };
52
+
53
+ /**
54
+ * @param {import('../../index.d.ts').ISuitest} classInstance
55
+ */
56
+ const ocrFactory = (classInstance) => {
57
+ const toJSON = (data) => {
58
+ const subject = {type: 'ocr'};
59
+ const socketMessage = {type: getRequestType(data)};
60
+
61
+ if (socketMessage.type === 'query') {
62
+ socketMessage.subject = subject;
63
+ if (data.ocrItems) {
64
+ socketMessage.subject.options = data.ocrItems;
65
+ }
66
+ } else {
67
+ socketMessage.request = applyTimeout(
68
+ {
69
+ type: 'assert',
70
+ condition: {
71
+ subject,
72
+ type: 'ocrComparators',
73
+ },
74
+ },
75
+ data,
76
+ classInstance.config.defaultTimeout,
77
+ );
78
+ if (data.ocrItems) {
79
+ socketMessage.request.condition.comparators = validateOcrComparators(data.ocrItems);
80
+ }
81
+ }
82
+
83
+ return socketMessage;
84
+ };
85
+
86
+ const toStringComposer = makeToStringComposer(toJSON);
87
+ const thenComposer = makeThenComposer(toJSON);
88
+ const toJSONComposer = makeToJSONComposer(toJSON);
89
+
90
+ const getComposers = (data) => {
91
+ const output = [
92
+ toStringComposer,
93
+ thenComposer,
94
+ cloneComposer,
95
+ toJSONComposer,
96
+ ];
97
+
98
+ if (!data.isAssert) {
99
+ output.push(assertComposer);
100
+ }
101
+
102
+ if (!data.isAbandoned) {
103
+ output.push(abandonComposer);
104
+ }
105
+
106
+ if (!data.timeout) {
107
+ output.push(timeoutComposer);
108
+ }
109
+
110
+ return output;
111
+ };
112
+
113
+ const makeOcrChain = (ocrItems) => {
114
+ return makeChain(
115
+ classInstance,
116
+ getComposers,
117
+ {
118
+ type: 'ocr',
119
+ ocrItems,
120
+ },
121
+ );
122
+ };
123
+
124
+ return {
125
+ ocr: (ocrOptions) => {
126
+ const validatedOcrOptions = validateOcrOptions(ocrOptions);
127
+ const processedOcrOptions = validatedOcrOptions && validatedOcrOptions.map(ocrItemWithDefaultType);
128
+
129
+ return makeOcrChain(processedOcrOptions);
130
+ },
131
+ ocrAssert: (ocrComparators) => {
132
+ const validatedOcrComparators = validateOcrComparators(ocrComparators);
133
+ const processedOcrComparators = validatedOcrComparators.map(ocrItemWithDefaultType);
134
+
135
+ return makeOcrChain(processedOcrComparators).toAssert();
136
+ },
137
+
138
+ // for unit tests
139
+ getComposers,
140
+ toJSON,
141
+ };
142
+ };
143
+
144
+ module.exports = ocrFactory;
@@ -0,0 +1,8 @@
1
+ const OCR_COLOR = {
2
+ DARK: 'dark',
3
+ LIGHT: 'light',
4
+ };
5
+
6
+ Object.freeze(OCR_COLOR);
7
+
8
+ module.exports = OCR_COLOR;
@@ -0,0 +1,9 @@
1
+ const OCR_READ_AS = {
2
+ LINE: 'single-line',
3
+ WORD: 'single-word',
4
+ BLOCK: 'single-block',
5
+ };
6
+
7
+ Object.freeze(OCR_READ_AS);
8
+
9
+ module.exports = OCR_READ_AS;
@@ -33,6 +33,8 @@ const validationKeys = {
33
33
  ELEMENT_HANDLE: Symbol('handle'),
34
34
  ELEMENT_ATTRIBUTES: Symbol('getAttributes'),
35
35
  COOKIE_PROPS: Symbol('cookieProps'),
36
+ OCR_COMPARATORS: Symbol('ocrComparators'),
37
+ OCR_OPTIONS: Symbol('ocrOptions'),
36
38
  };
37
39
 
38
40
  Object.freeze(validationKeys);
package/lib/texts.js CHANGED
@@ -77,6 +77,7 @@ module.exports = {
77
77
 
78
78
  positionIsMalformed: () => 'position command is malformed',
79
79
  relativePositionIsMalformed: () => 'relative position is malformed',
80
+ assertOcrMalformed: () => 'Assert ocr line is malformed - comparators are missing',
80
81
 
81
82
  unusedLeaves: leaves => `Some of your Suitest chains were not executed.
82
83
  Put an "await" in front of those chains or call .abandon() to suppress these warnings.
@@ -59,7 +59,7 @@ const processServerResponse = (logger, verbosity) =>
59
59
  }
60
60
  // query
61
61
  if (isQuery) {
62
- // if one of query fields is on res, return it
62
+ // if one of query fields is on res, return it
63
63
  const queryKeyFound = [
64
64
  'cookieValue',
65
65
  'cookieExists',
@@ -71,6 +71,7 @@ const processServerResponse = (logger, verbosity) =>
71
71
  'attributes',
72
72
  'execute',
73
73
  'location',
74
+ 'ocr',
74
75
  ].find(key => key in res);
75
76
 
76
77
  if (queryKeyFound) {
@@ -170,7 +171,8 @@ const getRequestType = (data, hasQuery = true) => {
170
171
  !data.isMoveTo &&
171
172
  isNil(data.sendText) &&
172
173
  isNil(data.setText) &&
173
- isNil(data.properties)
174
+ isNil(data.properties) &&
175
+ isNil(data.comparators)
174
176
  ) {
175
177
  return 'query';
176
178
  }
@@ -18,8 +18,20 @@ const VRC = require('../constants/vrc');
18
18
  const SCREEN_ORIENTATION = require('../constants/screenOrientation');
19
19
  const LAUNCH_MODE = require('../constants/launchMode');
20
20
  const COOKIE_PROP = require('../constants/cookieProp');
21
+ const OCR_READ_AS = require('../constants/ocrReadAs');
22
+ const OCR_COLOR = require('../constants/ocrColor');
21
23
  const schemas = {};
22
24
 
25
+ const nonNegativeNumber = () => ({
26
+ type: 'number',
27
+ minimum: 0,
28
+ });
29
+
30
+ const positiveNumber = () => ({
31
+ type: 'number',
32
+ exclusiveMinimum: 0,
33
+ });
34
+
23
35
  const CONFIG_OVERRIDE_PROPERTIES = {
24
36
  'url': {'type': 'string'},
25
37
  'suitestify': {'type': 'boolean'},
@@ -498,6 +510,86 @@ schemas[validationKeys.COOKIE_PROPS] = {
498
510
  },
499
511
  };
500
512
 
513
+ // schema for validating OCR assertion (testLine) comparators
514
+ schemas[validationKeys.OCR_COMPARATORS] = {
515
+ 'schemaId': validationKeys.OCR_COMPARATORS,
516
+ 'type': 'array',
517
+ 'items': {
518
+ 'type': 'object',
519
+ 'dependencies': {
520
+ 'type': ['val'],
521
+ },
522
+ 'required': ['val'],
523
+ 'additionalProperties': false,
524
+ 'properties': {
525
+ 'val': {'type': 'string'},
526
+ 'type': stringComparators(),
527
+ 'readAs': {
528
+ 'type': 'string',
529
+ 'enum': Object.values(OCR_READ_AS),
530
+ },
531
+ 'align': {'type': 'boolean'},
532
+ 'color': {
533
+ 'type': 'string',
534
+ 'enum': Object.values(OCR_COLOR),
535
+ },
536
+ 'whitelist': schemas[validationKeys.NON_EMPTY_STRING],
537
+ 'blacklist': schemas[validationKeys.NON_EMPTY_STRING],
538
+ 'region': {
539
+ 'type': 'array',
540
+ 'items': [
541
+ {...nonNegativeNumber(), maximum: 100},
542
+ {...nonNegativeNumber(), maximum: 100},
543
+ {...positiveNumber(), maximum: 100},
544
+ {...positiveNumber(), maximum: 100},
545
+ ],
546
+ 'minItems': 4,
547
+ 'additionalItems': false,
548
+ },
549
+ },
550
+ },
551
+ };
552
+
553
+ // schema for validating OCR query options
554
+ schemas[validationKeys.OCR_OPTIONS] = {
555
+ 'schemaId': validationKeys.OCR_OPTIONS,
556
+ 'type': 'array',
557
+ 'items': {
558
+ 'type': 'object',
559
+ 'required': [],
560
+ 'dependencies': {
561
+ 'type': ['val'],
562
+ },
563
+ 'additionalProperties': false,
564
+ 'properties': {
565
+ 'val': {'type': 'string'},
566
+ 'type': stringComparators(),
567
+ 'readAs': {
568
+ 'type': 'string',
569
+ 'enum': Object.values(OCR_READ_AS),
570
+ },
571
+ 'align': {'type': 'boolean'},
572
+ 'color': {
573
+ 'type': 'string',
574
+ 'enum': Object.values(OCR_COLOR),
575
+ },
576
+ 'whitelist': schemas[validationKeys.NON_EMPTY_STRING],
577
+ 'blacklist': schemas[validationKeys.NON_EMPTY_STRING],
578
+ 'region': {
579
+ 'type': 'array',
580
+ 'items': [
581
+ {...nonNegativeNumber(), maximum: 100},
582
+ {...nonNegativeNumber(), maximum: 100},
583
+ {...positiveNumber(), maximum: 100},
584
+ {...positiveNumber(), maximum: 100},
585
+ ],
586
+ 'minItems': 4,
587
+ 'additionalItems': false,
588
+ },
589
+ },
590
+ },
591
+ };
592
+
501
593
  Object.freeze(schemas);
502
594
 
503
595
  module.exports = schemas;
@@ -109,6 +109,8 @@ function prettifyJsonSchemaErrors(validate) {
109
109
  validationKeys.ELEMENT_ATTRIBUTES,
110
110
  validationKeys.CSS_PROPS,
111
111
  validationKeys.COOKIE_PROPS,
112
+ validationKeys.OCR_COMPARATORS,
113
+ validationKeys.OCR_OPTIONS,
112
114
  ].includes(validate.schema.schemaId)
113
115
  ) {
114
116
  errors = validate.errors.map(err => {
@@ -105,6 +105,12 @@ const validatorsMap = {
105
105
  [validationKeys.COOKIE_PROPS]: (value, text) => {
106
106
  return validators.validateJsonSchema(validationKeys.COOKIE_PROPS, value, text);
107
107
  },
108
+ [validationKeys.OCR_COMPARATORS]: (value, text) => {
109
+ return validators.validateJsonSchema(validationKeys.OCR_COMPARATORS, value, text);
110
+ },
111
+ [validationKeys.OCR_OPTIONS]: (value, text) => {
112
+ return validators.validateJsonSchema(validationKeys.OCR_OPTIONS, value, text);
113
+ },
108
114
  };
109
115
 
110
116
  Object.freeze(validatorsMap);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suitest-js-api",
3
- "version": "3.11.7",
3
+ "version": "3.12.0",
4
4
  "main": "index.js",
5
5
  "repository": "git@github.com:SuitestAutomation/suitest-js-api.git",
6
6
  "author": "Suitest <hello@suite.st>",
@@ -18,8 +18,12 @@
18
18
  "Android TV testing",
19
19
  "Apple TV testing",
20
20
  "Roku testing",
21
+ "Sky testing",
21
22
  "VIDAA testing",
22
23
  "Vizio SmartCast testing",
24
+ "Xfinity testing",
25
+ "XClass TV testing",
26
+ "Xumo testing",
23
27
  "Smart TV test automation",
24
28
  "Fire TV test automation"
25
29
  ],
@@ -86,8 +90,8 @@
86
90
  "typescript": "4.9.4"
87
91
  },
88
92
  "dependencies": {
89
- "@suitest/smst-to-text": "^4.9.0",
90
- "@suitest/translate": "^4.12.0",
93
+ "@suitest/smst-to-text": "^4.13.0",
94
+ "@suitest/translate": "^4.15.0",
91
95
  "@types/node": "^14.0.10",
92
96
  "ajv": "^6.12.2",
93
97
  "ansi-regex": "^5.0.0",
package/suitest.js CHANGED
@@ -39,6 +39,7 @@ const videoFactory = require('./lib/chains/videoChain');
39
39
  const playstationVideoFactory = require('./lib/chains/playstationVideoChain');
40
40
  const setScreenOrientationFactory = require('./lib/chains/setScreenOrientationChain');
41
41
  const openDeepLinkFactory = require('./lib/chains/openDeepLinkChain');
42
+ const ocrFactory = require('./lib/chains/ocrChain');
42
43
 
43
44
  // Constants
44
45
  const {ELEMENT_PROP, VALUE} = require('./lib/constants/element');
@@ -59,6 +60,8 @@ const LAUNCH_MODE = require('./lib/constants/launchMode');
59
60
  const DIRECTIONS = require('./lib/constants/directions');
60
61
  const SCREEN_ORIENTATION = require('./lib/constants/screenOrientation');
61
62
  const COOKIE_PROP = require('./lib/constants/cookieProp');
63
+ const OCR_READ_AS = require('./lib/constants/ocrReadAs');
64
+ const OCR_COLOR = require('./lib/constants/ocrColor');
62
65
 
63
66
  // Network
64
67
  const webSocketsFactory = require('./lib/api/webSockets');
@@ -135,6 +138,7 @@ class SUITEST_API extends EventEmitter {
135
138
  const {saveScreenshot} = saveScreenshotFactory(this);
136
139
  const {setScreenOrientation, setScreenOrientationAssert} = setScreenOrientationFactory(this);
137
140
  const {openDeepLink, openDeepLinkAssert} = openDeepLinkFactory(this);
141
+ const {ocr, ocrAssert} = ocrFactory(this);
138
142
 
139
143
  this.openApp = openApp;
140
144
  this.closeApp = closeApp;
@@ -162,6 +166,7 @@ class SUITEST_API extends EventEmitter {
162
166
  this.saveScreenshot = saveScreenshot;
163
167
  this.setScreenOrientation = setScreenOrientation;
164
168
  this.openDeepLink = openDeepLink;
169
+ this.ocr = ocr;
165
170
 
166
171
  this.PROP = ELEMENT_PROP;
167
172
  this.COMP = PROP_COMPARATOR;
@@ -183,6 +188,8 @@ class SUITEST_API extends EventEmitter {
183
188
  this.DIRECTIONS = DIRECTIONS;
184
189
  this.SCREEN_ORIENTATION = SCREEN_ORIENTATION;
185
190
  this.COOKIE_PROP = COOKIE_PROP;
191
+ this.OCR_READ_AS = OCR_READ_AS;
192
+ this.OCR_COLOR = OCR_COLOR;
186
193
 
187
194
  this.assert = {
188
195
  application: applicationAssert,
@@ -210,6 +217,7 @@ class SUITEST_API extends EventEmitter {
210
217
  psVideo: playstationVideoAssert,
211
218
  setScreenOrientation: setScreenOrientationAssert,
212
219
  openDeepLink: openDeepLinkAssert,
220
+ ocr: ocrAssert,
213
221
  };
214
222
 
215
223
  // Listen to process events to trigger websocket termination and dump warnings, if any
@@ -0,0 +1,24 @@
1
+ import {
2
+ AbstractChain,
3
+ Assertable,
4
+ Timeout,
5
+ BaseEmptyChain,
6
+ } from './modifiers';
7
+
8
+ export interface OcrChain extends
9
+ OcrBaseQueryChain<OcrChain>,
10
+ Timeout<OcrWithoutTimeout>
11
+ {}
12
+
13
+ interface OcrWithoutTimeout extends
14
+ OcrBaseQueryChain<OcrWithoutTimeout>
15
+ {}
16
+
17
+ interface OcrBaseQueryChain<TSelf> extends
18
+ BaseEmptyChain<TSelf, OcrQueryResult, OcrAbandonedChain>,
19
+ Assertable<TSelf>
20
+ {}
21
+
22
+ interface OcrAbandonedChain extends AbstractChain {}
23
+
24
+ type OcrQueryResult = string[];
@@ -0,0 +1,4 @@
1
+ export type OcrColor = {
2
+ DARK: 'dark',
3
+ LIGHT: 'light',
4
+ };
@@ -0,0 +1,5 @@
1
+ export type OcrReadAs = {
2
+ LINE: 'single-line',
3
+ WORD: 'single-word',
4
+ BLOCK: 'single-block',
5
+ }