Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#370 - New keyboard step for advance keyboard entries and modifiers #371

Merged
merged 9 commits into from
Mar 29, 2019
100 changes: 95 additions & 5 deletions src/tagui.sikuli/tagui.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,72 @@ def x_coordinate ( input_locator ):
def y_coordinate ( input_locator ):
return int(input_locator[input_locator.find(',')+1:-1])

# function to map modifier keys to unicode for use in type()
def modifiers_map ( input_keys ):
modifier_keys = 0
if '[shift]' in input_keys or '[SHIFT]' in input_keys: modifier_keys = modifier_keys + KeyModifier.SHIFT
if '[ctrl]' in input_keys or '[CTRL]' in input_keys: modifier_keys = modifier_keys + KeyModifier.CTRL
if '[alt]' in input_keys or '[ALT]' in input_keys: modifier_keys = modifier_keys + KeyModifier.ALT
if '[meta]' in input_keys or '[META]' in input_keys: modifier_keys = modifier_keys + KeyModifier.META
if '[cmd]' in input_keys or '[CMD]' in input_keys: modifier_keys = modifier_keys + KeyModifier.CMD
if '[win]' in input_keys or '[WIN]' in input_keys: modifier_keys = modifier_keys + KeyModifier.WIN
return modifier_keys

# function to map special keys to unicode for use in type()
def keyboard_map ( input_keys ):
input_keys = input_keys.replace('[clear]','\b').replace('[CLEAR]','\b')
input_keys = input_keys.replace('[space]',' ').replace('[SPACE]',' ')
input_keys = input_keys.replace('[enter]','\n').replace('[ENTER]','\n')
input_keys = input_keys.replace('[backspace]','\b').replace('[BACKSPACE]','\b')
input_keys = input_keys.replace('[tab]','\t').replace('[TAB]','\t')
input_keys = input_keys.replace('[esc]',u'\u001b').replace('[ESC]',u'\u001b')
input_keys = input_keys.replace('[up]',u'\ue000').replace('[UP]',u'\ue000')
input_keys = input_keys.replace('[right]',u'\ue001').replace('[RIGHT]',u'\ue001')
input_keys = input_keys.replace('[down]',u'\ue002').replace('[DOWN]',u'\ue002')
input_keys = input_keys.replace('[left]',u'\ue003').replace('[LEFT]',u'\ue003')
input_keys = input_keys.replace('[pageup]',u'\ue004').replace('[PAGEUP]',u'\ue004')
input_keys = input_keys.replace('[pagedown]',u'\ue005').replace('[PAGEDOWN]',u'\ue005')
input_keys = input_keys.replace('[delete]',u'\ue006').replace('[DELETE]',u'\ue006')
input_keys = input_keys.replace('[end]',u'\ue007').replace('[END]',u'\ue007')
input_keys = input_keys.replace('[home]',u'\ue008').replace('[HOME]',u'\ue008')
input_keys = input_keys.replace('[insert]',u'\ue009').replace('[INSERT]',u'\ue009')
input_keys = input_keys.replace('[f1]',u'\ue011').replace('[F1]',u'\ue011')
input_keys = input_keys.replace('[f2]',u'\ue012').replace('[F2]',u'\ue012')
input_keys = input_keys.replace('[f3]',u'\ue013').replace('[F3]',u'\ue013')
input_keys = input_keys.replace('[f4]',u'\ue014').replace('[F4]',u'\ue014')
input_keys = input_keys.replace('[f5]',u'\ue015').replace('[F5]',u'\ue015')
input_keys = input_keys.replace('[f6]',u'\ue016').replace('[F6]',u'\ue016')
input_keys = input_keys.replace('[f7]',u'\ue017').replace('[F7]',u'\ue017')
input_keys = input_keys.replace('[f8]',u'\ue018').replace('[F8]',u'\ue018')
input_keys = input_keys.replace('[f9]',u'\ue019').replace('[F9]',u'\ue019')
input_keys = input_keys.replace('[f10]',u'\ue01A').replace('[F10]',u'\ue01A')
input_keys = input_keys.replace('[f11]',u'\ue01B').replace('[F11]',u'\ue01B')
input_keys = input_keys.replace('[f12]',u'\ue01C').replace('[F12]',u'\ue01C')
input_keys = input_keys.replace('[f13]',u'\ue01D').replace('[F13]',u'\ue01D')
input_keys = input_keys.replace('[f14]',u'\ue01E').replace('[F14]',u'\ue01E')
input_keys = input_keys.replace('[f15]',u'\ue01F').replace('[F15]',u'\ue01F')
input_keys = input_keys.replace('[printscreen]',u'\ue024').replace('[PRINTSCREEN]',u'\ue024')
input_keys = input_keys.replace('[scrolllock]',u'\ue025').replace('[SCROLLLOCK]',u'\ue025')
input_keys = input_keys.replace('[pause]',u'\ue026').replace('[PAUSE]',u'\ue026')
input_keys = input_keys.replace('[capslock]',u'\ue027').replace('[CAPSLOCK]',u'\ue027')
input_keys = input_keys.replace('[numlock]',u'\ue03B').replace('[NUMLOCK]',u'\ue03B')

# if modifier key is the only input, treat as a keystroke instead of a modifier
if input_keys == '[shift]' or input_keys == '[SHIFT]': input_keys = u'\ue020'
elif input_keys == '[ctrl]' or input_keys == '[CTRL]': input_keys = u'\ue021'
elif input_keys == '[alt]' or input_keys == '[ALT]': input_keys = u'\ue022'
elif input_keys == '[meta]' or input_keys == '[META]': input_keys = u'\ue023'
elif input_keys == '[cmd]' or input_keys == '[CMD]': input_keys = u'\ue023'
elif input_keys == '[win]' or input_keys == '[WIN]': input_keys = u'\ue042'

input_keys = input_keys.replace('[shift]','').replace('[SHIFT]','')
input_keys = input_keys.replace('[ctrl]','').replace('[CTRL]','')
input_keys = input_keys.replace('[alt]','').replace('[ALT]','')
input_keys = input_keys.replace('[meta]','').replace('[META]','')
input_keys = input_keys.replace('[cmd]','').replace('[CMD]','')
input_keys = input_keys.replace('[win]','').replace('[WIN]','')
return input_keys

# function to output sikuli text to tagui
def output_sikuli_text ( output_text ):
import codecs
Expand Down Expand Up @@ -89,14 +155,23 @@ def type_intent ( raw_intent ):
param1 = params[:params.find(' as ')].strip()
param2 = params[4+params.find(' as '):].strip()
print '[tagui] ACTION - type ' + param1 + ' as ' + param2
param2 = param2.replace('[enter]','\n')
param2 = param2.replace('[clear]','\b')
modifier_keys = modifiers_map(param2)
param2 = keyboard_map(param2)
if param1.endswith('page.png') or param1.endswith('page.bmp'):
return type(param2)
if modifier_keys == 0:
return type(param2)
else:
return type(param2,modifier_keys)
elif is_coordinates(param1):
return type(Location(x_coordinate(param1),y_coordinate(param1)),param2)
if modifier_keys == 0:
return type(Location(x_coordinate(param1),y_coordinate(param1)),param2)
else:
return type(Location(x_coordinate(param1),y_coordinate(param1)),param2,modifier_keys)
elif exists(param1):
return type(param1,param2)
if modifier_keys == 0:
return type(param1,param2)
else:
return type(param1,param2,modifier_keys)
else:
return 0

Expand Down Expand Up @@ -185,6 +260,17 @@ def snap_intent ( raw_intent ):
else:
return 0

# function for low-level keyboard control
def keyboard_intent ( raw_intent ):
params = (raw_intent + ' ')[1+(raw_intent + ' ').find(' '):].strip()
print '[tagui] ACTION - keyboard ' + params
modifier_keys = modifiers_map(params)
params = keyboard_map(params)
if modifier_keys == 0:
return type(params)
else:
return type(params,modifier_keys)

# function for low-level mouse control
def mouse_intent ( raw_intent ):
params = (raw_intent + ' ')[1+(raw_intent + ' ').find(' '):].strip()
Expand Down Expand Up @@ -240,6 +326,8 @@ def get_intent ( raw_intent ):
return 'save'
if raw_intent[:5].lower() == 'snap ':
return 'snap'
if raw_intent[:9].lower() == 'keyboard ':
return 'keyboard'
if raw_intent[:6].lower() == 'mouse ':
return 'mouse'
if raw_intent[:7].lower() == 'vision ':
Expand Down Expand Up @@ -271,6 +359,8 @@ def parse_intent ( script_line ):
return save_intent(script_line)
elif intent_type == 'snap':
return snap_intent(script_line)
elif intent_type == 'keyboard':
return keyboard_intent(script_line)
elif intent_type == 'mouse':
return mouse_intent(script_line)
elif intent_type == 'vision':
Expand Down
8 changes: 8 additions & 0 deletions src/tagui_header.js
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,7 @@ case 'table': return table_intent(live_line); break;
case 'wait': return wait_intent(live_line); break;
case 'live': return live_intent(live_line); break;
case 'ask': return ask_intent(live_line); break;
case 'keyboard': return keyboard_intent(live_line); break;
case 'mouse': return mouse_intent(live_line); break;
case 'check': return check_intent(live_line); break;
case 'test': return test_intent(live_line); break;
Expand Down Expand Up @@ -759,6 +760,7 @@ if (lc_raw_intent.substr(0,6) == 'table ') return 'table';
if (lc_raw_intent.substr(0,5) == 'wait ') return 'wait';
if (lc_raw_intent.substr(0,5) == 'live ') return 'live';
if (lc_raw_intent.substr(0,4) == 'ask ') return 'ask';
if (lc_raw_intent.substr(0,9) == 'keyboard ') return 'keyboard';
if (lc_raw_intent.substr(0,6) == 'mouse ') return 'mouse';
if (lc_raw_intent.substr(0,6) == 'check ') return 'check';
if (lc_raw_intent.substr(0,5) == 'test ') return 'test';
Expand Down Expand Up @@ -795,6 +797,7 @@ if (lc_raw_intent == 'table') return 'table';
if (lc_raw_intent == 'wait') return 'wait';
if (lc_raw_intent == 'live') return 'live';
if (lc_raw_intent == 'ask') return 'ask';
if (lc_raw_intent == 'keyboard') return 'keyboard';
if (lc_raw_intent == 'mouse') return 'mouse';
if (lc_raw_intent == 'check') return 'check';
if (lc_raw_intent == 'test') return 'test';
Expand Down Expand Up @@ -1091,6 +1094,11 @@ return "this.echo('ERROR - you are already in live mode, type done to quit live
function ask_intent(raw_intent) {raw_intent = eval("'" + raw_intent + "'"); // support dynamic variables
return "this.echo('ERROR - step is not relevant in live mode, set ask_result directly')";}

function keyboard_intent(raw_intent) {raw_intent = eval("'" + raw_intent + "'"); // support dynamic variables
var params = ((raw_intent + ' ').substr(1+(raw_intent + ' ').indexOf(' '))).trim();
if (params == '') return "this.echo('ERROR - keys to type missing for " + raw_intent + "')";
else return call_sikuli(raw_intent,params);}

function mouse_intent(raw_intent) {raw_intent = eval("'" + raw_intent + "'"); // support dynamic variables
var params = ((raw_intent + ' ').substr(1+(raw_intent + ' ').indexOf(' '))).trim();
if (params == '') return "this.echo('ERROR - up / down missing for " + raw_intent + "')";
Expand Down
8 changes: 8 additions & 0 deletions src/tagui_parse.php
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ function process_intent($intent_type, $script_line) {
case "wait": return wait_intent($script_line); break;
case "live": return live_intent($script_line); break;
case "ask": return ask_intent($script_line); break;
case "keyboard": return keyboard_intent($script_line); break;
case "mouse": return mouse_intent($script_line); break;
case "check": return check_intent($script_line); break;
case "test": return test_intent($script_line); break;
Expand Down Expand Up @@ -440,6 +441,7 @@ function get_intent($raw_intent) {$lc_raw_intent = strtolower($raw_intent);
if (substr($lc_raw_intent,0,5)=="wait ") return "wait";
if (substr($lc_raw_intent,0,5)=="live ") return "live";
if (substr($lc_raw_intent,0,4)=="ask ") return "ask";
if (substr($lc_raw_intent,0,9)=="keyboard ") return "keyboard";
if (substr($lc_raw_intent,0,6)=="mouse ") return "mouse";
if (substr($lc_raw_intent,0,6)=="check ") {$GLOBALS['test_automation']++; return "check";}
if (substr($lc_raw_intent,0,5)=="test ") return "test";
Expand Down Expand Up @@ -476,6 +478,7 @@ function get_intent($raw_intent) {$lc_raw_intent = strtolower($raw_intent);
if ($lc_raw_intent=="wait") return "wait";
if ($lc_raw_intent=="live") return "live";
if ($lc_raw_intent=="ask") return "ask";
if ($lc_raw_intent=="keyboard") return "keyboard";
if ($lc_raw_intent=="mouse") return "mouse";
if ($lc_raw_intent=="check") {$GLOBALS['test_automation']++; return "check";}
if ($lc_raw_intent=="test") return "test";
Expand Down Expand Up @@ -842,6 +845,11 @@ function ask_intent($raw_intent) { // ask user for input during automation and s
"{ask_result = ''; var sys = require('system');\nthis.echo('".$params." '); ".
"ask_result = sys.stdin.readLine();}".end_fi()."});"."\n\n";}

function keyboard_intent($raw_intent) {
$params = trim(substr($raw_intent." ",1+strpos($raw_intent." "," ")));
if ($params == "") echo "ERROR - " . current_line() . " keys to type missing for " . $raw_intent . "\n";
return "casper.then(function() {".call_sikuli($raw_intent,$params);}

function mouse_intent($raw_intent) {
$params = trim(substr($raw_intent." ",1+strpos($raw_intent." "," ")));
if ($params == "") echo "ERROR - " . current_line() . " up / down missing for " . $raw_intent . "\n";
Expand Down
15 changes: 15 additions & 0 deletions src/test/positive_test
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,21 @@ wait 7.5 seconds
// test live
live

// test keyboard
keyboard ls -lrt[enter]
keyboard ls -lrt[ENTER]
keyboard [pageup]
keyboard [PAGEDOWN]
keyboard 123[enter]456[ENTER]
keyboard [home]
keyboard [end]
keyboard [ctrl][home]
keyboard [ctrl][end]
keyboard [win]
keyboard e[win]
keyboard [win]e
keyboard [cmd][space]

// test mouse
mouse down
mouse up
Expand Down
74 changes: 74 additions & 0 deletions src/test/positive_test.signature
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ case 'table': return table_intent(live_line); break;
case 'wait': return wait_intent(live_line); break;
case 'live': return live_intent(live_line); break;
case 'ask': return ask_intent(live_line); break;
case 'keyboard': return keyboard_intent(live_line); break;
case 'mouse': return mouse_intent(live_line); break;
case 'check': return check_intent(live_line); break;
case 'test': return test_intent(live_line); break;
Expand Down Expand Up @@ -786,6 +787,7 @@ if (lc_raw_intent.substr(0,6) == 'table ') return 'table';
if (lc_raw_intent.substr(0,5) == 'wait ') return 'wait';
if (lc_raw_intent.substr(0,5) == 'live ') return 'live';
if (lc_raw_intent.substr(0,4) == 'ask ') return 'ask';
if (lc_raw_intent.substr(0,9) == 'keyboard ') return 'keyboard';
if (lc_raw_intent.substr(0,6) == 'mouse ') return 'mouse';
if (lc_raw_intent.substr(0,6) == 'check ') return 'check';
if (lc_raw_intent.substr(0,5) == 'test ') return 'test';
Expand Down Expand Up @@ -822,6 +824,7 @@ if (lc_raw_intent == 'table') return 'table';
if (lc_raw_intent == 'wait') return 'wait';
if (lc_raw_intent == 'live') return 'live';
if (lc_raw_intent == 'ask') return 'ask';
if (lc_raw_intent == 'keyboard') return 'keyboard';
if (lc_raw_intent == 'mouse') return 'mouse';
if (lc_raw_intent == 'check') return 'check';
if (lc_raw_intent == 'test') return 'test';
Expand Down Expand Up @@ -1118,6 +1121,11 @@ return "this.echo('ERROR - you are already in live mode, type done to quit live
function ask_intent(raw_intent) {raw_intent = eval("'" + raw_intent + "'"); // support dynamic variables
return "this.echo('ERROR - step is not relevant in live mode, set ask_result directly')";}

function keyboard_intent(raw_intent) {raw_intent = eval("'" + raw_intent + "'"); // support dynamic variables
var params = ((raw_intent + ' ').substr(1+(raw_intent + ' ').indexOf(' '))).trim();
if (params == '') return "this.echo('ERROR - keys to type missing for " + raw_intent + "')";
else return call_sikuli(raw_intent,params);}

function mouse_intent(raw_intent) {raw_intent = eval("'" + raw_intent + "'"); // support dynamic variables
var params = ((raw_intent + ' ').substr(1+(raw_intent + ' ').indexOf(' '))).trim();
if (params == '') return "this.echo('ERROR - up / down missing for " + raw_intent + "')";
Expand Down Expand Up @@ -2621,6 +2629,72 @@ while (true) {live_input = sys.stdin.readLine(); // evaluate input in casperjs c
if (live_input.indexOf('done') == 0) break; try {eval(tagui_parse(live_input));}
catch(e) {this.echo('ERROR - ' + e.message.charAt(0).toLowerCase() + e.message.slice(1));}}}});

// test keyboard
casper.then(function() {{techo('keyboard ls -lrt[enter]'); var fs = require('fs');
if (!sikuli_step('keyboard ls -lrt[enter]')) if (!fs.exists('ls -lrt[enter]'))
this.echo('ERROR - cannot find image file ls -lrt[enter]').exit(); else
this.echo('ERROR - cannot find ls -lrt[enter] on screen').exit(); this.wait(0);}});

casper.then(function() {{techo('keyboard ls -lrt[ENTER]'); var fs = require('fs');
if (!sikuli_step('keyboard ls -lrt[ENTER]')) if (!fs.exists('ls -lrt[ENTER]'))
this.echo('ERROR - cannot find image file ls -lrt[ENTER]').exit(); else
this.echo('ERROR - cannot find ls -lrt[ENTER] on screen').exit(); this.wait(0);}});

casper.then(function() {{techo('keyboard [pageup]'); var fs = require('fs');
if (!sikuli_step('keyboard [pageup]')) if (!fs.exists('[pageup]'))
this.echo('ERROR - cannot find image file [pageup]').exit(); else
this.echo('ERROR - cannot find [pageup] on screen').exit(); this.wait(0);}});

casper.then(function() {{techo('keyboard [PAGEDOWN]'); var fs = require('fs');
if (!sikuli_step('keyboard [PAGEDOWN]')) if (!fs.exists('[PAGEDOWN]'))
this.echo('ERROR - cannot find image file [PAGEDOWN]').exit(); else
this.echo('ERROR - cannot find [PAGEDOWN] on screen').exit(); this.wait(0);}});

casper.then(function() {{techo('keyboard 123[enter]456[ENTER]'); var fs = require('fs');
if (!sikuli_step('keyboard 123[enter]456[ENTER]')) if (!fs.exists('123[enter]456[ENTER]'))
this.echo('ERROR - cannot find image file 123[enter]456[ENTER]').exit(); else
this.echo('ERROR - cannot find 123[enter]456[ENTER] on screen').exit(); this.wait(0);}});

casper.then(function() {{techo('keyboard [home]'); var fs = require('fs');
if (!sikuli_step('keyboard [home]')) if (!fs.exists('[home]'))
this.echo('ERROR - cannot find image file [home]').exit(); else
this.echo('ERROR - cannot find [home] on screen').exit(); this.wait(0);}});

casper.then(function() {{techo('keyboard [end]'); var fs = require('fs');
if (!sikuli_step('keyboard [end]')) if (!fs.exists('[end]'))
this.echo('ERROR - cannot find image file [end]').exit(); else
this.echo('ERROR - cannot find [end] on screen').exit(); this.wait(0);}});

casper.then(function() {{techo('keyboard [ctrl][home]'); var fs = require('fs');
if (!sikuli_step('keyboard [ctrl][home]')) if (!fs.exists('[ctrl][home]'))
this.echo('ERROR - cannot find image file [ctrl][home]').exit(); else
this.echo('ERROR - cannot find [ctrl][home] on screen').exit(); this.wait(0);}});

casper.then(function() {{techo('keyboard [ctrl][end]'); var fs = require('fs');
if (!sikuli_step('keyboard [ctrl][end]')) if (!fs.exists('[ctrl][end]'))
this.echo('ERROR - cannot find image file [ctrl][end]').exit(); else
this.echo('ERROR - cannot find [ctrl][end] on screen').exit(); this.wait(0);}});

casper.then(function() {{techo('keyboard [win]'); var fs = require('fs');
if (!sikuli_step('keyboard [win]')) if (!fs.exists('[win]'))
this.echo('ERROR - cannot find image file [win]').exit(); else
this.echo('ERROR - cannot find [win] on screen').exit(); this.wait(0);}});

casper.then(function() {{techo('keyboard e[win]'); var fs = require('fs');
if (!sikuli_step('keyboard e[win]')) if (!fs.exists('e[win]'))
this.echo('ERROR - cannot find image file e[win]').exit(); else
this.echo('ERROR - cannot find e[win] on screen').exit(); this.wait(0);}});

casper.then(function() {{techo('keyboard [win]e'); var fs = require('fs');
if (!sikuli_step('keyboard [win]e')) if (!fs.exists('[win]e'))
this.echo('ERROR - cannot find image file [win]e').exit(); else
this.echo('ERROR - cannot find [win]e on screen').exit(); this.wait(0);}});

casper.then(function() {{techo('keyboard [cmd][space]'); var fs = require('fs');
if (!sikuli_step('keyboard [cmd][space]')) if (!fs.exists('[cmd][space]'))
this.echo('ERROR - cannot find image file [cmd][space]').exit(); else
this.echo('ERROR - cannot find [cmd][space] on screen').exit(); this.wait(0);}});

// test mouse
casper.then(function() {{techo('mouse down'); var fs = require('fs');
if (!sikuli_step('mouse down')) if (!fs.exists('down'))
Expand Down