askui 0.18.0 → 0.19.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.
Files changed (61) hide show
  1. package/dist/cjs/core/ai-element/ai-element-collection.d.ts +10 -0
  2. package/dist/cjs/core/ai-element/ai-element-collection.js +78 -0
  3. package/dist/cjs/core/ai-element/ai-element-error.d.ts +2 -0
  4. package/dist/cjs/core/ai-element/ai-element-error.js +6 -0
  5. package/dist/cjs/core/ai-element/ai-element.d.ts +27 -0
  6. package/dist/cjs/core/ai-element/ai-element.js +31 -0
  7. package/dist/cjs/core/model/custom-element-json.d.ts +32 -15
  8. package/dist/cjs/core/model/custom-element.d.ts +3 -2
  9. package/dist/cjs/core/model/custom-element.js +4 -2
  10. package/dist/cjs/core/reporting/step-reporter.js +15 -6
  11. package/dist/cjs/execution/dsl.d.ts +488 -113
  12. package/dist/cjs/execution/dsl.js +519 -113
  13. package/dist/cjs/execution/index.d.ts +1 -1
  14. package/dist/cjs/execution/index.js +11 -3
  15. package/dist/cjs/execution/inference-client.js +9 -4
  16. package/dist/cjs/execution/ui-control-client-dependency-builder.d.ts +1 -0
  17. package/dist/cjs/execution/ui-control-client-dependency-builder.js +6 -3
  18. package/dist/cjs/execution/ui-control-client.d.ts +101 -16
  19. package/dist/cjs/execution/ui-control-client.js +142 -52
  20. package/dist/cjs/execution/ui-controller-client-interface.d.ts +2 -0
  21. package/dist/cjs/execution/ui-controller-client.d.ts +1 -0
  22. package/dist/cjs/execution/ui-controller-client.js +11 -1
  23. package/dist/cjs/execution/ui-controller-not-connected-error.d.ts +4 -0
  24. package/dist/cjs/execution/ui-controller-not-connected-error.js +12 -0
  25. package/dist/cjs/main.d.ts +1 -1
  26. package/dist/cjs/main.js +12 -3
  27. package/dist/cjs/utils/analytics/analytics.d.ts +3 -1
  28. package/dist/cjs/utils/analytics/analytics.js +5 -0
  29. package/dist/cjs/utils/http/custom-errors/index.js +1 -1
  30. package/dist/cjs/utils/http/http-client-got.js +46 -20
  31. package/dist/esm/core/ai-element/ai-element-collection.d.ts +10 -0
  32. package/dist/esm/core/ai-element/ai-element-collection.js +71 -0
  33. package/dist/esm/core/ai-element/ai-element-error.d.ts +2 -0
  34. package/dist/esm/core/ai-element/ai-element-error.js +2 -0
  35. package/dist/esm/core/ai-element/ai-element.d.ts +27 -0
  36. package/dist/esm/core/ai-element/ai-element.js +28 -0
  37. package/dist/esm/core/model/custom-element-json.d.ts +32 -15
  38. package/dist/esm/core/model/custom-element.d.ts +3 -2
  39. package/dist/esm/core/model/custom-element.js +4 -2
  40. package/dist/esm/core/reporting/step-reporter.js +15 -6
  41. package/dist/esm/execution/dsl.d.ts +488 -113
  42. package/dist/esm/execution/dsl.js +519 -113
  43. package/dist/esm/execution/index.d.ts +1 -1
  44. package/dist/esm/execution/index.js +1 -1
  45. package/dist/esm/execution/inference-client.js +9 -4
  46. package/dist/esm/execution/ui-control-client-dependency-builder.d.ts +1 -0
  47. package/dist/esm/execution/ui-control-client-dependency-builder.js +6 -3
  48. package/dist/esm/execution/ui-control-client.d.ts +101 -16
  49. package/dist/esm/execution/ui-control-client.js +142 -49
  50. package/dist/esm/execution/ui-controller-client-interface.d.ts +2 -0
  51. package/dist/esm/execution/ui-controller-client.d.ts +1 -0
  52. package/dist/esm/execution/ui-controller-client.js +11 -1
  53. package/dist/esm/execution/ui-controller-not-connected-error.d.ts +4 -0
  54. package/dist/esm/execution/ui-controller-not-connected-error.js +8 -0
  55. package/dist/esm/main.d.ts +1 -1
  56. package/dist/esm/main.js +1 -1
  57. package/dist/esm/utils/analytics/analytics.d.ts +3 -1
  58. package/dist/esm/utils/analytics/analytics.js +5 -0
  59. package/dist/esm/utils/http/custom-errors/index.js +1 -1
  60. package/dist/esm/utils/http/http-client-got.js +46 -20
  61. package/package.json +1 -1
@@ -1 +1 @@
1
- export { UiControlClient, RelationsForConvenienceMethods } from './ui-control-client';
1
+ export * from './ui-control-client';
@@ -1 +1 @@
1
- export { UiControlClient } from './ui-control-client';
1
+ export * from './ui-control-client';
@@ -56,12 +56,17 @@ export class InferenceClient {
56
56
  inference(customElements = [], image, instruction) {
57
57
  return __awaiter(this, void 0, void 0, function* () {
58
58
  const resizedImage = yield this.resizeIfNeeded(customElements, image);
59
- const response = yield this.httpClient.post(this.urls.inference, {
60
- customElements,
59
+ const response = yield this.httpClient.post(this.urls.inference, this.urls.inference.includes('v4-experimental') ? {
61
60
  image: resizedImage.base64Image,
62
61
  instruction,
63
- modelComposition: this.modelComposition,
64
- });
62
+ tasks: ['OCR'],
63
+ }
64
+ : {
65
+ customElements,
66
+ image: resizedImage.base64Image,
67
+ instruction,
68
+ modelComposition: this.modelComposition,
69
+ });
65
70
  InferenceClient.logMetaInformation(response);
66
71
  return InferenceResponse.fromJson(response.body, resizedImage.resizeRatio, image);
67
72
  });
@@ -8,6 +8,7 @@ export declare class UiControlClientDependencyBuilder {
8
8
  static build(clientArgs: ClientArgsWithDefaults): Promise<{
9
9
  executionRuntime: ExecutionRuntime;
10
10
  stepReporter: StepReporter;
11
+ workspaceId: string | undefined;
11
12
  }>;
12
13
  static getClientArgsWithDefaults(clientArgs: ClientArgs): Promise<ClientArgsWithDefaults>;
13
14
  }
@@ -30,29 +30,32 @@ export class UiControlClientDependencyBuilder {
30
30
  var _a;
31
31
  return __awaiter(this, void 0, void 0, function* () {
32
32
  const httpClient = yield UiControlClientDependencyBuilder.buildHttpClient(clientArgs);
33
- return new InferenceClient(clientArgs.inferenceServerUrl, httpClient, clientArgs.resize, (_a = clientArgs.credentials) === null || _a === void 0 ? void 0 : _a.workspaceId, clientArgs.modelComposition);
33
+ return new InferenceClient(clientArgs.inferenceServerUrl, httpClient, clientArgs.resize, (_a = clientArgs.credentials) === null || _a === void 0 ? void 0 : _a.workspaceId, clientArgs.modelComposition, clientArgs.inferenceServerApiVersion);
34
34
  });
35
35
  }
36
36
  static buildUiControllerClient(clientArgs) {
37
37
  return new UiControllerClient(clientArgs.uiControllerUrl);
38
38
  }
39
39
  static build(clientArgs) {
40
+ var _a;
40
41
  return __awaiter(this, void 0, void 0, function* () {
41
42
  const uiControllerClient = UiControlClientDependencyBuilder.buildUiControllerClient(clientArgs);
42
43
  const inferenceClient = yield UiControlClientDependencyBuilder.buildInferenceClient(clientArgs);
43
44
  const stepReporter = new StepReporter(clientArgs.reporter);
45
+ const workspaceId = (_a = clientArgs.credentials) === null || _a === void 0 ? void 0 : _a.workspaceId;
44
46
  return {
45
47
  executionRuntime: new ExecutionRuntime(uiControllerClient, inferenceClient, stepReporter),
46
48
  stepReporter,
49
+ workspaceId,
47
50
  };
48
51
  });
49
52
  }
50
53
  static getClientArgsWithDefaults(clientArgs) {
51
- var _a, _b, _c, _d, _e;
54
+ var _a, _b, _c, _d, _e, _f;
52
55
  return __awaiter(this, void 0, void 0, function* () {
53
56
  return Object.assign(Object.assign({}, clientArgs), { context: {
54
57
  isCi: (_b = (_a = clientArgs.context) === null || _a === void 0 ? void 0 : _a.isCi) !== null && _b !== void 0 ? _b : isCI,
55
- }, credentials: readCredentials(clientArgs), inferenceServerUrl: (_c = clientArgs.inferenceServerUrl) !== null && _c !== void 0 ? _c : 'https://inference.askui.com', proxyAgents: (_d = clientArgs.proxyAgents) !== null && _d !== void 0 ? _d : (yield envProxyAgents()), uiControllerUrl: (_e = clientArgs.uiControllerUrl) !== null && _e !== void 0 ? _e : 'http://127.0.0.1:6769' });
58
+ }, credentials: readCredentials(clientArgs), inferenceServerApiVersion: (_c = clientArgs.inferenceServerApiVersion) !== null && _c !== void 0 ? _c : 'v3', inferenceServerUrl: (_d = clientArgs.inferenceServerUrl) !== null && _d !== void 0 ? _d : 'https://inference.askui.com', proxyAgents: (_e = clientArgs.proxyAgents) !== null && _e !== void 0 ? _e : (yield envProxyAgents()), uiControllerUrl: (_f = clientArgs.uiControllerUrl) !== null && _f !== void 0 ? _f : 'http://127.0.0.1:6769' });
56
59
  });
57
60
  }
58
61
  }
@@ -1,12 +1,34 @@
1
- import { CustomElementJson } from '../core/model/custom-element-json';
2
- import { Exec, Executable, FluentFilters, ApiCommands, PC_AND_MODIFIER_KEY } from './dsl';
1
+ import { Exec, Executable, FluentFilters, ApiCommands, PC_AND_MODIFIER_KEY, CommandExecutorContext } from './dsl';
3
2
  import { UiControllerClientConnectionState } from './ui-controller-client-connection-state';
4
3
  import { Annotation } from '../core/annotation/annotation';
5
4
  import { AnnotationRequest } from '../core/model/annotation-result/annotation-interface';
6
5
  import { DetectedElement } from '../core/model/annotation-result/detected-element';
7
6
  import { ClientArgs } from './ui-controller-client-interface';
8
7
  export declare type RelationsForConvenienceMethods = 'nearestTo' | 'leftOf' | 'above' | 'rightOf' | 'below' | 'contains';
8
+ export declare type TextMatchingOption = 'similar' | 'exact' | 'regex';
9
+ export declare type ElementExistsQueryType = 'otherElement' | 'switch' | 'element' | 'container' | 'checkbox' | 'element' | 'button' | 'table' | 'text' | 'icon' | 'image' | 'textfield';
10
+ export interface ElementExistsQueryText {
11
+ value: string;
12
+ matching?: TextMatchingOption;
13
+ }
14
+ export interface ElementExistsQueryRelation {
15
+ type: RelationsForConvenienceMethods;
16
+ text: string;
17
+ }
18
+ export interface ElementExistsQuery {
19
+ type: keyof Pick<FluentFilters, ElementExistsQueryType>;
20
+ text?: ElementExistsQueryText;
21
+ relation?: ElementExistsQueryRelation;
22
+ }
23
+ export interface ExpectExistenceElement extends ElementExistsQuery {
24
+ exists: boolean;
25
+ }
26
+ export interface ExpectAllExistResult {
27
+ allExist: boolean;
28
+ elements: ExpectExistenceElement[];
29
+ }
9
30
  export declare class UiControlClient extends ApiCommands {
31
+ private workspaceId;
10
32
  private executionRuntime;
11
33
  private stepReporter;
12
34
  private constructor();
@@ -34,8 +56,9 @@ export declare class UiControlClient extends ApiCommands {
34
56
  annotateInteractively(): Promise<void>;
35
57
  private escapeSeparatorString;
36
58
  private buildInstruction;
37
- fluentCommandExecutor(instructionString: string, customElementJson?: CustomElementJson[]): Promise<void>;
38
- getterExecutor(instruction: string, customElementJson?: CustomElementJson[]): Promise<DetectedElement[]>;
59
+ private getAIElementsByNames;
60
+ fluentCommandExecutor(instructionString: string, context?: CommandExecutorContext): Promise<void>;
61
+ getterExecutor(instruction: string, context?: CommandExecutorContext): Promise<DetectedElement[]>;
39
62
  private secretText;
40
63
  private getAndResetSecretText;
41
64
  /**
@@ -190,8 +213,8 @@ export declare class UiControlClient extends ApiCommands {
190
213
  * @param {Object} params - Object containing required `label` property and
191
214
  * optional `relation` property.
192
215
  * @property {string} params.label - The label for the checkbox.
193
- * @property {Object} params.relation - Object describing the relationship between
194
- * the clicked checkbox and another element.
216
+ * @property {Object} [params.relation] - Object describing the relationship between
217
+ * the clicked checkbox and another element.
195
218
  * @property {RelationsForConvenienceMethods} params.relation.type - The type of relation.
196
219
  */
197
220
  clickCheckbox(params: {
@@ -213,7 +236,7 @@ export declare class UiControlClient extends ApiCommands {
213
236
  * @param {Object} params - Object containing required `label` property and
214
237
  * optional `relation` property.
215
238
  * @property {string} params.label - The label for the checkbox.
216
- * @property {Object} params.relation - Object describing the relationship between
239
+ * @property {Object} [params.relation] - Object describing the relationship between
217
240
  * the clicked checkbox and another element.
218
241
  * @property {RelationsForConvenienceMethods} params.relation.type - The type of relation.
219
242
  */
@@ -266,41 +289,103 @@ export declare class UiControlClient extends ApiCommands {
266
289
  }): Promise<void>;
267
290
  /**
268
291
  * Click on a specific text.
269
- * You can also use a RegEx or match the text exactly by specifyicing the specific flag.
292
+ * You can also use a RegEx or match the text exactly by specifying the specific flag.
270
293
  * Use a relation to find the text in relation to a specific text.
271
294
  *
272
295
  * **Examples:**
273
296
  * ```typescript
274
297
  * // Click text that matches exactly
275
- * await aui.clickText({text: 'askui', type: 'similar'})
298
+ * await aui.clickText({text: 'askui', matching: 'similar'})
276
299
  *
277
300
  * // Click text that contains 'pie' or 'cake' or 'Pie' or 'Cake'
278
- * await aui.clickText({text: '.*([Pp]ie|[Cc]ake).*', type: 'regex'})
301
+ * await aui.clickText({text: '.*([Pp]ie|[Cc]ake).*', matching: 'regex'})
279
302
  *
280
303
  * // Click the text 'TERMINAL' that is left of the text 'Ports'
281
304
  * await aui.clickText({
282
305
  * text: 'TERMINAL',
283
- * type: "exact",
306
+ * matching: "exact",
284
307
  * relation: { type: 'leftOf', text: 'PORTS' }
285
308
  * })
286
309
  * ```
310
+ *
287
311
  * @param {Object} params - Object containing required `text` property and optional properties
288
312
  * for regular expression matching and relation.
289
313
  * @property {string} params.text - The text to be clicked.
290
- * @property {string} params.type - Whether the text is matched using similarity,
291
- * exact match or a regular expression.
292
- * @property {Object} params.relation - Object describing the relationship between the
293
- * clicked text and another element.
314
+ * @property {string} params.matching - Whether the text is matched using similarity,
315
+ * exact match or a regular expression.
316
+ * @property {Object} [params.relation] - Object describing the relationship between the
317
+ * clicked text and another element.
294
318
  * @property {RelationsForConvenienceMethods} params.relation.type - The type of relation.
295
319
  * @property {string} params.relation.text - The label or text associated with the
296
320
  * related element or state.
297
321
  */
298
322
  clickText(params: {
299
323
  text: string;
300
- type: 'similar' | 'exact' | 'regex';
324
+ matching: TextMatchingOption;
301
325
  relation?: {
302
326
  type: RelationsForConvenienceMethods;
303
327
  text: string;
304
328
  };
305
329
  }): Promise<void>;
330
+ private evaluateMatchingProperty;
331
+ /**
332
+ * Check if one or multiple elements are detected.
333
+ *
334
+ * **Examples:**
335
+ * ```typescript
336
+ * await aui.expectAllExist([
337
+ * {
338
+ * type: 'text',
339
+ * text: {
340
+ * value: 'Switch to Dark',
341
+ * matching: 'similar'
342
+ * }
343
+ * },
344
+ * ]);
345
+ *
346
+ * // Check for existence of multiple elements
347
+ * await aui.expectAllExist([
348
+ * {
349
+ * type: 'textfield',
350
+ * relation: {
351
+ * type: 'rightOf',
352
+ * text: 'Email:'
353
+ * }
354
+ * },
355
+ * {
356
+ * type: 'element',
357
+ * text: {
358
+ * value: 'Switch to Dark'
359
+ * }
360
+ * },
361
+ * ]);
362
+ *
363
+ * // Validate existence
364
+ * const exists = await aui.expectAllExist([...]);
365
+ * exists.allExist // true when every element exists
366
+ *
367
+ * // Check which elements do not exist
368
+ * // with the elements property
369
+ * const nonExistentElements = exists.elements.filter((e) => e.exists===false)
370
+ * ```
371
+ *
372
+ * @param {ElementExistsQuery[]} query - Objects containing the required property
373
+ * 'type' and the optional properties
374
+ * 'text' and 'relation'.
375
+ * @property {string} query.type - The type of the element: 'otherElement' | 'switch' |
376
+ * 'element' | 'container' | 'checkbox' | 'element' |
377
+ * 'button' | 'table' | 'text' | 'icon' | 'image' | 'textfield'
378
+ * @property {Object} [query.text] - Object containing value and matching strategy.
379
+ * @property {string} query.text.value - The text to match for.
380
+ * @property {string} [query.text.matching] - Whether the text is matched using similarity,
381
+ * exact match or a regular expression.
382
+ * @property {Object} [query.relation] - Object describing the relationship between the
383
+ * clicked text and another element.
384
+ * @property {RelationsForConvenienceMethods} query.relation.type - The type of relation.
385
+ * @property {string} query.relation.text - The label or text associated with the
386
+ * related element or state.
387
+ * @returns {ExpectAllExistResult.allExist} - If every element exists.
388
+ * @returns {ExpectAllExistResult.elements} - ExpectExistenceElement[].
389
+ */
390
+ expectAllExist(query: ElementExistsQuery[]): Promise<ExpectAllExistResult>;
306
391
  }
@@ -7,15 +7,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import ValidationError from 'yup/lib/ValidationError';
10
+ import { ValidationError } from 'yup';
11
11
  import { CustomElement } from '../core/model/custom-element';
12
12
  import { ApiCommands, Separators, } from './dsl';
13
13
  import { AnnotationWriter } from '../core/annotation/annotation-writer';
14
14
  import { logger } from '../lib/logger';
15
15
  import { UiControlClientDependencyBuilder } from './ui-control-client-dependency-builder';
16
+ import { AIElementCollection } from '../core/ai-element/ai-element-collection';
16
17
  export class UiControlClient extends ApiCommands {
17
- constructor(executionRuntime, stepReporter) {
18
+ constructor(workspaceId, executionRuntime, stepReporter) {
18
19
  super();
20
+ this.workspaceId = workspaceId;
19
21
  this.executionRuntime = executionRuntime;
20
22
  this.stepReporter = stepReporter;
21
23
  this.secretText = undefined;
@@ -24,8 +26,8 @@ export class UiControlClient extends ApiCommands {
24
26
  return __awaiter(this, void 0, void 0, function* () {
25
27
  const builder = UiControlClientDependencyBuilder;
26
28
  const clientArgsWithDefaults = yield builder.getClientArgsWithDefaults(clientArgs);
27
- const { executionRuntime, stepReporter } = yield builder.build(clientArgsWithDefaults);
28
- return new UiControlClient(executionRuntime, stepReporter);
29
+ const { workspaceId, executionRuntime, stepReporter, } = yield builder.build(clientArgsWithDefaults);
30
+ return new UiControlClient(workspaceId, executionRuntime, stepReporter);
29
31
  });
30
32
  }
31
33
  /**
@@ -119,9 +121,20 @@ export class UiControlClient extends ApiCommands {
119
121
  };
120
122
  });
121
123
  }
122
- fluentCommandExecutor(instructionString, customElementJson = []) {
124
+ getAIElementsByNames(names) {
123
125
  return __awaiter(this, void 0, void 0, function* () {
124
- const instruction = yield this.buildInstruction(instructionString, customElementJson);
126
+ // eslint-disable-next-line max-len
127
+ const workspaceAIElementCollection = yield AIElementCollection.collectForWorkspaceId(this.workspaceId);
128
+ return workspaceAIElementCollection.getByNames(names);
129
+ });
130
+ }
131
+ fluentCommandExecutor(instructionString, context = { customElementsJson: [], aiElementNames: [] }) {
132
+ return __awaiter(this, void 0, void 0, function* () {
133
+ const aiElements = yield this.getAIElementsByNames(context.aiElementNames);
134
+ const instruction = yield this.buildInstruction(instructionString, [
135
+ ...context.customElementsJson,
136
+ ...aiElements,
137
+ ]);
125
138
  logger.debug(instruction);
126
139
  try {
127
140
  yield this.stepReporter.resetStep(instruction);
@@ -135,12 +148,16 @@ export class UiControlClient extends ApiCommands {
135
148
  }
136
149
  });
137
150
  }
138
- getterExecutor(instruction, customElementJson = []) {
151
+ getterExecutor(instruction, context = { customElementsJson: [], aiElementNames: [] }) {
139
152
  return __awaiter(this, void 0, void 0, function* () {
140
- const customElements = yield CustomElement.fromJsonListWithImagePathOrImage(customElementJson);
153
+ const aiElements = yield this.getAIElementsByNames(context.aiElementNames);
154
+ const customElements = yield CustomElement.fromJsonListWithImagePathOrImage(context.customElementsJson);
141
155
  const stringWithoutSeparators = this.escapeSeparatorString(instruction);
142
156
  logger.debug(stringWithoutSeparators);
143
- return this.executionRuntime.getDetectedElements(instruction, customElements);
157
+ return this.executionRuntime.getDetectedElements(instruction, [
158
+ ...customElements,
159
+ ...aiElements,
160
+ ]);
144
161
  });
145
162
  }
146
163
  getAndResetSecretText() {
@@ -325,30 +342,22 @@ export class UiControlClient extends ApiCommands {
325
342
  }
326
343
  // eslint-disable-next-line class-methods-use-this
327
344
  evaluateRelation(command, relation, text) {
328
- let commando = command;
329
345
  switch (relation) {
330
- case 'nearestTo':
331
- commando = command.nearestTo().text(text);
332
- break;
333
346
  case 'leftOf':
334
- commando = command.leftOf().text(text);
335
- break;
347
+ return command.leftOf().text(text);
336
348
  case 'above':
337
- commando = command.above().text(text);
338
- break;
349
+ return command.above().text(text);
339
350
  case 'rightOf':
340
- commando = command.rightOf().text(text);
341
- break;
351
+ return command.rightOf().text(text);
342
352
  case 'below':
343
- commando = command.below().text(text);
344
- break;
353
+ return command.below().text(text);
345
354
  case 'contains':
346
- commando = command.contains().text(text);
347
- break;
355
+ return command.contains().text(text);
356
+ case 'nearestTo':
357
+ return command.nearestTo().text(text);
348
358
  default:
349
- throw new ValidationError('No valid Relation.Type was passed.');
359
+ throw new ValidationError(`'relation' has to be 'nearestTo', 'leftOf', 'above', 'rightOf', 'below' or 'contains' but was '${relation}'`);
350
360
  }
351
- return commando;
352
361
  }
353
362
  /**
354
363
  * Click a button with a specific label.
@@ -394,8 +403,8 @@ export class UiControlClient extends ApiCommands {
394
403
  * @param {Object} params - Object containing required `label` property and
395
404
  * optional `relation` property.
396
405
  * @property {string} params.label - The label for the checkbox.
397
- * @property {Object} params.relation - Object describing the relationship between
398
- * the clicked checkbox and another element.
406
+ * @property {Object} [params.relation] - Object describing the relationship between
407
+ * the clicked checkbox and another element.
399
408
  * @property {RelationsForConvenienceMethods} params.relation.type - The type of relation.
400
409
  */
401
410
  clickCheckbox(params) {
@@ -423,7 +432,7 @@ export class UiControlClient extends ApiCommands {
423
432
  * @param {Object} params - Object containing required `label` property and
424
433
  * optional `relation` property.
425
434
  * @property {string} params.label - The label for the checkbox.
426
- * @property {Object} params.relation - Object describing the relationship between
435
+ * @property {Object} [params.relation] - Object describing the relationship between
427
436
  * the clicked checkbox and another element.
428
437
  * @property {RelationsForConvenienceMethods} params.relation.type - The type of relation.
429
438
  */
@@ -487,31 +496,32 @@ export class UiControlClient extends ApiCommands {
487
496
  }
488
497
  /**
489
498
  * Click on a specific text.
490
- * You can also use a RegEx or match the text exactly by specifyicing the specific flag.
499
+ * You can also use a RegEx or match the text exactly by specifying the specific flag.
491
500
  * Use a relation to find the text in relation to a specific text.
492
501
  *
493
502
  * **Examples:**
494
503
  * ```typescript
495
504
  * // Click text that matches exactly
496
- * await aui.clickText({text: 'askui', type: 'similar'})
505
+ * await aui.clickText({text: 'askui', matching: 'similar'})
497
506
  *
498
507
  * // Click text that contains 'pie' or 'cake' or 'Pie' or 'Cake'
499
- * await aui.clickText({text: '.*([Pp]ie|[Cc]ake).*', type: 'regex'})
508
+ * await aui.clickText({text: '.*([Pp]ie|[Cc]ake).*', matching: 'regex'})
500
509
  *
501
510
  * // Click the text 'TERMINAL' that is left of the text 'Ports'
502
511
  * await aui.clickText({
503
512
  * text: 'TERMINAL',
504
- * type: "exact",
513
+ * matching: "exact",
505
514
  * relation: { type: 'leftOf', text: 'PORTS' }
506
515
  * })
507
516
  * ```
517
+ *
508
518
  * @param {Object} params - Object containing required `text` property and optional properties
509
519
  * for regular expression matching and relation.
510
520
  * @property {string} params.text - The text to be clicked.
511
- * @property {string} params.type - Whether the text is matched using similarity,
512
- * exact match or a regular expression.
513
- * @property {Object} params.relation - Object describing the relationship between the
514
- * clicked text and another element.
521
+ * @property {string} params.matching - Whether the text is matched using similarity,
522
+ * exact match or a regular expression.
523
+ * @property {Object} [params.relation] - Object describing the relationship between the
524
+ * clicked text and another element.
515
525
  * @property {RelationsForConvenienceMethods} params.relation.type - The type of relation.
516
526
  * @property {string} params.relation.text - The label or text associated with the
517
527
  * related element or state.
@@ -519,23 +529,106 @@ export class UiControlClient extends ApiCommands {
519
529
  clickText(params) {
520
530
  return __awaiter(this, void 0, void 0, function* () {
521
531
  let command = this.click().text();
522
- switch (params.type) {
523
- case 'similar':
524
- command = command.withText(params.text);
525
- break;
526
- case 'exact':
527
- command = command.withExactText(params.text);
528
- break;
529
- case 'regex':
530
- command = command.withTextRegex(params.text);
531
- break;
532
- default:
533
- throw new ValidationError('"type" must be "similar", "exact" or "regex"');
534
- }
532
+ command = this.evaluateMatchingProperty(command, { value: params.text, matching: params.matching });
535
533
  if (params.relation) {
536
534
  command = this.evaluateRelation(command, params.relation.type, params.relation.text);
537
535
  }
538
536
  yield command.exec();
539
537
  });
540
538
  }
539
+ // eslint-disable-next-line class-methods-use-this
540
+ evaluateMatchingProperty(command, text) {
541
+ var _a;
542
+ switch ((_a = text.matching) !== null && _a !== void 0 ? _a : 'similar') {
543
+ case 'exact':
544
+ return command.withExactText(text.value);
545
+ case 'regex':
546
+ return command.withTextRegex(text.value);
547
+ case 'similar':
548
+ return command.withText(text.value);
549
+ default:
550
+ throw new ValidationError(`'text.matching' property has to be 'similar', 'exact' or 'regex' but was '${text.matching}'`);
551
+ }
552
+ }
553
+ /**
554
+ * Check if one or multiple elements are detected.
555
+ *
556
+ * **Examples:**
557
+ * ```typescript
558
+ * await aui.expectAllExist([
559
+ * {
560
+ * type: 'text',
561
+ * text: {
562
+ * value: 'Switch to Dark',
563
+ * matching: 'similar'
564
+ * }
565
+ * },
566
+ * ]);
567
+ *
568
+ * // Check for existence of multiple elements
569
+ * await aui.expectAllExist([
570
+ * {
571
+ * type: 'textfield',
572
+ * relation: {
573
+ * type: 'rightOf',
574
+ * text: 'Email:'
575
+ * }
576
+ * },
577
+ * {
578
+ * type: 'element',
579
+ * text: {
580
+ * value: 'Switch to Dark'
581
+ * }
582
+ * },
583
+ * ]);
584
+ *
585
+ * // Validate existence
586
+ * const exists = await aui.expectAllExist([...]);
587
+ * exists.allExist // true when every element exists
588
+ *
589
+ * // Check which elements do not exist
590
+ * // with the elements property
591
+ * const nonExistentElements = exists.elements.filter((e) => e.exists===false)
592
+ * ```
593
+ *
594
+ * @param {ElementExistsQuery[]} query - Objects containing the required property
595
+ * 'type' and the optional properties
596
+ * 'text' and 'relation'.
597
+ * @property {string} query.type - The type of the element: 'otherElement' | 'switch' |
598
+ * 'element' | 'container' | 'checkbox' | 'element' |
599
+ * 'button' | 'table' | 'text' | 'icon' | 'image' | 'textfield'
600
+ * @property {Object} [query.text] - Object containing value and matching strategy.
601
+ * @property {string} query.text.value - The text to match for.
602
+ * @property {string} [query.text.matching] - Whether the text is matched using similarity,
603
+ * exact match or a regular expression.
604
+ * @property {Object} [query.relation] - Object describing the relationship between the
605
+ * clicked text and another element.
606
+ * @property {RelationsForConvenienceMethods} query.relation.type - The type of relation.
607
+ * @property {string} query.relation.text - The label or text associated with the
608
+ * related element or state.
609
+ * @returns {ExpectAllExistResult.allExist} - If every element exists.
610
+ * @returns {ExpectAllExistResult.elements} - ExpectExistenceElement[].
611
+ */
612
+ expectAllExist(query) {
613
+ return __awaiter(this, void 0, void 0, function* () {
614
+ const elements = yield query.reduce((accumulatorPromise, subquery) => __awaiter(this, void 0, void 0, function* () {
615
+ const acc = yield accumulatorPromise;
616
+ const command = this.get()[subquery.type]();
617
+ let finalCommand = subquery.text !== undefined
618
+ ? this.evaluateMatchingProperty(command, subquery.text)
619
+ : command;
620
+ if (subquery.relation) {
621
+ finalCommand = this.evaluateRelation(finalCommand, subquery.relation.type, subquery.relation.text);
622
+ }
623
+ return [
624
+ ...acc,
625
+ Object.assign(Object.assign({}, subquery), { exists: (yield finalCommand.exec()).length > 0 }),
626
+ ];
627
+ }), Promise.resolve([]));
628
+ return {
629
+ elements,
630
+ allExist: elements.every((el) => el.exists),
631
+ };
632
+ });
633
+ }
541
634
  }
@@ -53,9 +53,11 @@ export interface ClientArgs {
53
53
  readonly modelComposition?: ModelCompositionBranch[];
54
54
  readonly reporter?: Reporter | Reporter[] | undefined;
55
55
  readonly context?: ContextArgs | undefined;
56
+ readonly inferenceServerApiVersion?: string;
56
57
  }
57
58
  export interface ClientArgsWithDefaults extends ClientArgs {
58
59
  readonly uiControllerUrl: string;
59
60
  readonly inferenceServerUrl: string;
60
61
  readonly context: Context;
62
+ readonly inferenceServerApiVersion: string;
61
63
  }
@@ -18,6 +18,7 @@ export declare class UiControllerClient {
18
18
  private onMessage;
19
19
  connect(): Promise<UiControllerClientConnectionState>;
20
20
  disconnect(): void;
21
+ private checkConnection;
21
22
  private sendAndReceive;
22
23
  private send;
23
24
  requestScreenshot(): Promise<CaptureScreenshotResponse>;
@@ -4,6 +4,7 @@ import { logger } from '../lib/logger';
4
4
  import { UiControllerClientConnectionState } from './ui-controller-client-connection-state';
5
5
  import { ReadRecordingResponseStreamHandler } from './read-recording-response-stream-handler';
6
6
  import { UiControllerClientError } from './ui-controller-client-error';
7
+ import { UiControllerNotConnectedError } from './ui-controller-not-connected-error';
7
8
  export class UiControllerClient {
8
9
  constructor(url) {
9
10
  this.url = url;
@@ -34,7 +35,9 @@ export class UiControllerClient {
34
35
  return new Promise((resolve, reject) => {
35
36
  try {
36
37
  this.ws = new WebSocket(this.url);
37
- this.ws.on('message', (data) => { this.onMessage(data); });
38
+ this.ws.on('message', (data) => {
39
+ this.onMessage(data);
40
+ });
38
41
  this.ws.on('open', () => {
39
42
  this.connectionState = UiControllerClientConnectionState.CONNECTED;
40
43
  resolve(this.connectionState);
@@ -55,7 +58,13 @@ export class UiControllerClient {
55
58
  var _a;
56
59
  (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
57
60
  }
61
+ checkConnection() {
62
+ if (this.connectionState !== UiControllerClientConnectionState.CONNECTED) {
63
+ throw new UiControllerNotConnectedError();
64
+ }
65
+ }
58
66
  sendAndReceive(msg, requestTimeout = UiControllerClient.REQUEST_TIMEOUT_IN_MS) {
67
+ this.checkConnection();
59
68
  return new Promise((resolve, reject) => {
60
69
  this.currentResolve = resolve;
61
70
  this.currentReject = reject;
@@ -69,6 +78,7 @@ export class UiControllerClient {
69
78
  });
70
79
  }
71
80
  send(msg, _requestTimeout = UiControllerClient.REQUEST_TIMEOUT_IN_MS) {
81
+ this.checkConnection();
72
82
  if (!this.currentReject || !this.currentResolve) {
73
83
  throw new UiControllerClientError('Request is not finished! It is not possible to have multiple requests at the same time.');
74
84
  }
@@ -0,0 +1,4 @@
1
+ import { UiControllerClientError } from './ui-controller-client-error';
2
+ export declare class UiControllerNotConnectedError extends UiControllerClientError {
3
+ constructor();
4
+ }
@@ -0,0 +1,8 @@
1
+ import { UiControllerClientError } from './ui-controller-client-error';
2
+ export class UiControllerNotConnectedError extends UiControllerClientError {
3
+ constructor() {
4
+ super('UI Controller is not connected. Did you call `UiControlClient.connect()`'
5
+ + ' before trying to interact with the UI Controller / OS?');
6
+ this.name = 'UiControllerNotConnectedError';
7
+ }
8
+ }
@@ -1,5 +1,5 @@
1
1
  export { UiController } from './lib';
2
- export { UiControlClient, RelationsForConvenienceMethods } from './execution';
2
+ export * from './execution';
3
3
  export { Instruction, Reporter, ReporterConfig, Snapshot, SnapshotDetailLevel, Step, StepStatus, StepStatusEnd, } from './core/reporting';
4
4
  export { Annotation } from './core/annotation/annotation';
5
5
  export { DetectedElement } from './core/model/annotation-result/detected-element';
package/dist/esm/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { UiController } from './lib';
2
- export { UiControlClient } from './execution';
2
+ export * from './execution';
3
3
  export { Annotation } from './core/annotation/annotation';
4
4
  export { DetectedElement } from './core/model/annotation-result/detected-element';
5
5
  export { LogLevels } from './shared';