Skip to content

Commit

Permalink
refactor: rename Relations to Anchor; Move evaluation of Anchor param…
Browse files Browse the repository at this point in the history
…eter to own method
  • Loading branch information
JohannesDienst-askui committed Apr 11, 2024
1 parent 140ba6c commit c791274
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 141 deletions.
2 changes: 0 additions & 2 deletions packages/askui-nodejs/src/execution/dsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ export enum Separators {
STRING = '<|string|>',
}

export type Relations = 'nearestTo' | 'leftOf' | 'above' | 'rightOf' | 'below' | 'contains';

// LITERALS
export type PC_KEY = 'backspace' | 'delete' | 'enter' | 'tab' | 'escape' | 'up' | 'down' | 'right' | 'left' | 'home' | 'end' | 'pageup' | 'pagedown' | 'f1' | 'f2' | 'f3' | 'f4' | 'f5' | 'f6' | 'f7' | 'f8' | 'f9' | 'f10' | 'f11' | 'f12' | 'space' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | '!' | '"' | '#' | '$' | '%' | '&' | "'" | '(' | ')' | '*' | '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | '=' | '>' | '?' | '@' | '[' | '\\' | ']' | '^' | '_' | '`' | '{' | '|' | '}' | '~ ';
export type ANDROID_KEY = 'home' | 'back' | 'call' | 'endcall' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'star' | 'pound' | 'dpad_up' | 'dpad_down' | 'dpad_left' | 'dpad_right' | 'dpad_center' | 'volume_up' | 'volume_down' | 'power' | 'camera' | 'clear' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | 'comma' | 'period' | 'alt_left' | 'alt_right' | 'shift_left' | 'shift_right' | 'tab' | 'space' | 'sym' | 'explorer' | 'envelope' | 'enter' | 'del' | 'grave' | 'minus' | 'equals' | 'left_bracket' | 'right_bracket' | 'backslash' | 'semicolon' | 'apostrophe' | 'slash' | 'at' | 'num' | 'headsethook' | 'focus' | 'plus' | 'menu' | 'notification' | 'search' | 'media_play_pause' | 'media_stop' | 'media_next' | 'media_previous' | 'media_rewind' | 'media_fast_forward' | 'mute' | 'page_up' | 'page_down' | 'switch_charset' | 'escape' | 'forward_del' | 'ctrl_left' | 'ctrl_right' | 'caps_lock' | 'scroll_lock' | 'function' | 'break' | 'move_home' | 'move_end' | 'insert' | 'forward' | 'media_play' | 'media_pause' | 'media_close' | 'media_eject' | 'media_record' | 'f1' | 'f2' | 'f3' | 'f4' | 'f5' | 'f6' | 'f7' | 'f8' | 'f9' | 'f10' | 'f11' | 'f12' | 'num_lock' | 'numpad_0' | 'numpad_1' | 'numpad_2' | 'numpad_3' | 'numpad_4' | 'numpad_5' | 'numpad_6' | 'numpad_7' | 'numpad_8' | 'numpad_9' | 'numpad_divide' | 'numpad_multiply' | 'numpad_subtract' | 'numpad_add' | 'numpad_dot' | 'numpad_comma' | 'numpad_enter' | 'numpad_equals' | 'numpad_left_paren' | 'numpad_right_paren' | 'volume_mute' | 'info' | 'channel_up' | 'channel_down' | 'zoom_in' | 'zoom_out' | 'window' | 'guide' | 'bookmark' | 'captions' | 'settings' | 'app_switch' | 'language_switch' | 'contacts' | 'calendar' | 'music' | 'calculator' | 'assist' | 'brightness_down' | 'brightness_up' | 'media_audio_track' | 'sleep' | 'wakeup' | 'pairing' | 'media_top_menu' | 'last_channel' | 'tv_data_service' | 'voice_assist' | 'help' | 'navigate_previous' | 'navigate_next' | 'navigate_in' | 'navigate_out' | 'dpad_up_left' | 'dpad_down_left' | 'dpad_up_right' | 'dpad_down_right' | 'media_skip_forward' | 'media_skip_backward' | 'media_step_forward' | 'media_step_backward' | 'soft_sleep' | 'cut' | 'copy' | 'paste' | 'all_apps' | 'refresh';
Expand Down
2 changes: 1 addition & 1 deletion packages/askui-nodejs/src/execution/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { UiControlClient } from './ui-control-client';
export { UiControlClient, Anchor as Anchors } from './ui-control-client';
244 changes: 107 additions & 137 deletions packages/askui-nodejs/src/execution/ui-control-client.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import ValidationError from 'yup/lib/ValidationError';
import { CustomElement } from '../core/model/custom-element';
import { CustomElementJson } from '../core/model/custom-element-json';
import {
Exec, Executable, FluentFilters, ApiCommands, Separators,
PC_AND_MODIFIER_KEY, Relations,
PC_AND_MODIFIER_KEY,
FluentFiltersOrRelations,
} from './dsl';
import { UiControllerClientConnectionState } from './ui-controller-client-connection-state';
import { ExecutionRuntime } from './execution-runtime';
Expand All @@ -15,6 +17,8 @@ import { ClientArgs } from './ui-controller-client-interface';
import { UiControlClientDependencyBuilder } from './ui-control-client-dependency-builder';
import { Instruction, StepReporter } from '../core/reporting';

export type Anchor = 'nearestTo' | 'leftOf' | 'above' | 'rightOf' | 'below' | 'contains';

export class UiControlClient extends ApiCommands {
private constructor(
private executionRuntime: ExecutionRuntime,
Expand Down Expand Up @@ -344,6 +348,38 @@ export class UiControlClient extends ApiCommands {
}
}

// eslint-disable-next-line class-methods-use-this
private evaluateRelation(
command: FluentFiltersOrRelations,
position: Anchor,
text: string,
) {
let commando = command;
switch (position) {
case 'nearestTo':
commando = command.nearestTo().text(text);
break;
case 'leftOf':
commando = command.leftOf().text(text);
break;
case 'above':
commando = command.above().text(text);
break;
case 'rightOf':
commando = command.rightOf().text(text);
break;
case 'below':
commando = command.below().text(text);
break;
case 'contains':
commando = command.contains().text(text);
break;
default:
throw new ValidationError('No valid Position.Type was passed.');
}
return commando;
}

/**
* Click a button with a specific label.
* Optional relation identifies the button in relation to another element.
Expand All @@ -360,14 +396,14 @@ export class UiControlClient extends ApiCommands {
* @property {string} [params.label] - The text label of the button. Defaults to an empty string.
* @property {Object} [params.relation] - Object describing the relationship between
* the clicked button and another element.
* @property {Relations} params.relation.type - The type of relation.
* @property {Anchor} params.relation.type - The type of relation.
* @property {string} params.relation.text - The text element the relation is based on.
*/
async clickButton(
params: {
label?: string,
relation?: {
type: Relations,
type: Anchor,
text: string
}
},
Expand All @@ -379,26 +415,7 @@ export class UiControlClient extends ApiCommands {
}

if (params.relation) {
switch (params.relation.type) {
case 'nearestTo':
command = command.nearestTo().text(params.relation.text);
break;
case 'leftOf':
command = command.leftOf().text(params.relation.text);
break;
case 'above':
command = command.above().text(params.relation.text);
break;
case 'rightOf':
command = command.rightOf().text(params.relation.text);
break;
case 'below':
command = command.below().text(params.relation.text);
break;
default:
command = command.nearestTo().text(params.relation.text);
break;
}
command = this.evaluateRelation(command, params.relation.type, params.relation.text);
}

await command.exec();
Expand All @@ -419,78 +436,61 @@ export class UiControlClient extends ApiCommands {
* @property {string} params.label - The label for the checkbox.
* @property {Object} [params.relation] - Object describing the relationship between
* the clicked checkbox and another element.
* @property {Relations} params.relation.type - The type of relation.
* @property {Anchor} params.relation.type - The type of relation.
*/
async clickCheckbox(
params: {
label: string,
relation?: {
type: Relations
type: Anchor
}
},
) {
const command = this.click().checkbox();
let command = this.click().checkbox();

if (params.relation) {
switch (params.relation.type) {
case 'nearestTo':
await command.nearestTo().text(params.label).exec();
break;
case 'leftOf':
await command.leftOf().text(params.label).exec();
break;
case 'above':
await command.above().text(params.label).exec();
break;
case 'rightOf':
await command.rightOf().text(params.label).exec();
break;
case 'below':
await command.below().text(params.label).exec();
break;
default:
await command.nearestTo().text(params.label).exec();
break;
}
if (!params.relation) {
command = command.nearestTo().text(params.label);
} else {
await command.nearestTo().text(params.label).exec();
command = this.evaluateRelation(command, params.relation.type, params.label);
}

await command.exec();
}

/**
* Click a switch with a specific label.
* You can also specify where the label is placed relationally.
*
* **Examples:**
* ```typescript
* await aui.clickSwitch({label: 'Toggle'})
* await aui.clickSwitch({label: 'Toggle', relation: {type: 'leftOf'}})
* ```
*
* @param {Object} params - Object containing required `label` property and
* optional `relation` property.
* @property {string} params.label - The label for the checkbox.
* @property {Object} [params.relation] - Object describing the relationship between
* the clicked checkbox and another element.
* @property {Anchor} params.relation.type - The type of relation.
*/
async clickSwitch(
params: {
label: string,
relation?: {
type: Relations
type: Anchor
}
},
) {
const command = this.click().switch();
let command = this.click().switch();

if (params.relation) {
switch (params.relation.type) {
case 'nearestTo':
await command.nearestTo().text(params.label).exec();
break;
case 'leftOf':
await command.leftOf().text(params.label).exec();
break;
case 'above':
await command.above().text(params.label).exec();
break;
case 'rightOf':
await command.rightOf().text(params.label).exec();
break;
case 'below':
await command.below().text(params.label).exec();
break;
default:
await command.nearestTo().text(params.label).exec();
break;
}
if (!params.relation) {
command = command.nearestTo().text(params.label);
} else {
await command.nearestTo().text(params.label).exec();
command = this.evaluateRelation(command, params.relation.type, params.label);
}

await command.exec();
}

/**
Expand Down Expand Up @@ -520,50 +520,30 @@ export class UiControlClient extends ApiCommands {
* @param {Object} params - Object containing required `textToWrite` property and
* optional `relation` property.
* @property {string} params.textToWrite - The text to be typed into the textfield.
* @property {Object} params.relation - Object describing the relationship between
* the textfield being interacted with and another element.
* @property {Relations} [params.relation.type] - The type of relation, optional.
* @property {Object} params.relation - Object describing the relationship between the
* textfield being interacted with and another element.
* @property {Anchor} [params.relation.type] - The type of relation, optional.
* @property {string} params.relation.label - The label associated with the related
* element, optional.
*/
async typeIntoTextfield(
params: {
textToWrite: string,
relation: {
type?: Relations,
type?: Anchor,
label: string
}
},
) {
const command = this.typeIn(params.textToWrite).textfield();

if (params.relation.type) {
switch (params.relation.type) {
case 'nearestTo':
await command.nearestTo().text(params.relation.label).exec();
break;
case 'contains':
await command.contains().text(params.relation.label).exec();
break;
case 'leftOf':
await command.leftOf().text(params.relation.label).exec();
break;
case 'above':
await command.above().text(params.relation.label).exec();
break;
case 'rightOf':
await command.rightOf().text(params.relation.label).exec();
break;
case 'below':
await command.below().text(params.relation.label).exec();
break;
default:
await command.nearestTo().text(params.relation.label).exec();
break;
}
let command = this.typeIn(params.textToWrite).textfield();

if (!params.relation.type) {
command = command.nearestTo().text(params.relation.label);
} else {
await command.nearestTo().text(params.relation.label).exec();
command = this.evaluateRelation(command, params.relation.type, params.relation.label);
}

await command.exec();
}

/**
Expand All @@ -574,15 +554,17 @@ export class UiControlClient extends ApiCommands {
* **Examples:**
* ```typescript
* // Click text that matches exactly
* await aui.clickText({text: 'askui', exact: true})
* await aui.clickText({text: 'askui', type: 'similar'})
*
* // Click text that contains 'pie' or 'cake' or 'Pie' or 'Cake'
* await aui.clickText({text: '.*([Pp]ie|[Cc]ake).*', regex: true})
* await aui.clickText({text: '.*([Pp]ie|[Cc]ake).*', type: 'regex'})
*
* // Click the text 'TERMINAL' that is left of the text 'Ports'
* await aui.clickText({text: 'TERMINAL', relation: {type: 'leftOf', text: 'PORTS'}})
* await aui.clickText({
* text: 'TERMINAL',
* type: "exact",
* relation: {type: 'leftOf', text: 'PORTS'}})
* ```
*
* @param {Object} params - Object containing required `text` property and optional properties
* for regular expression matching and relation.
* @property {string} params.text - The text to be clicked.
Expand All @@ -592,49 +574,37 @@ export class UiControlClient extends ApiCommands {
* when using regex, default is false.
* @property {Object} [params.relation] - Object describing the relationship between the
* clicked text and another element.
* @property {Relations} params.relation.type - The type of relation.
* @property {Anchor} params.relation.type - The type of relation.
* @property {string} params.relation.text - The label or text associated with the
* related element or state.
*/
async clickText(
params: {
text: string,
regex?: boolean,
exact?: boolean,
relation?: { type: Relations, text: string }
type: 'similar' | 'exact' | 'regex',
relation?: {
type: Anchor,
text: string }
},
) {
let command = this.click().text();

if (params.regex) {
command = command.withTextRegex(params.text);
} else if (params.exact) {
command = command.withExactText(params.text);
} else if (params.text) {
command = command.withText(params.text);
switch (params.type) {
case 'similar':
command = command.withText(params.text);
break;
case 'exact':
command = command.withExactText(params.text);
break;
case 'regex':
command = command.withTextRegex(params.text);
break;
default:
throw new ValidationError('"type" must be "similar", "exact" or "regex"');
}

if (params.relation) {
switch (params.relation.type) {
case 'nearestTo':
command = command.nearestTo().text(params.relation.text);
break;
case 'leftOf':
command = command.leftOf().text(params.relation.text);
break;
case 'above':
command = command.above().text(params.relation.text);
break;
case 'rightOf':
command = command.rightOf().text(params.relation.text);
break;
case 'below':
command = command.below().text(params.relation.text);
break;
default:
command = command.nearestTo().text(params.relation.text);
break;
}
command = this.evaluateRelation(command, params.relation.type, params.relation.text);
}

await command.exec();
Expand Down
2 changes: 1 addition & 1 deletion packages/askui-nodejs/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { UiController } from './lib';
export { UiControlClient } from './execution';
export { UiControlClient, Anchors } from './execution';
export {
Instruction,
Reporter,
Expand Down

0 comments on commit c791274

Please sign in to comment.