Skip to content

Commit

Permalink
working framework for #24
Browse files Browse the repository at this point in the history
see issue #24 for more details
- working websocket integration with chrome (or headless chrome)
- concurrent communications using separate php thread
- supporting css and xpath selectors
- done following casperjs api - exists, getTitle, getCurrentURL, click
(simplified)
  • Loading branch information
kensoh committed Jun 30, 2017
1 parent eb44bad commit a4e1689
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 18 deletions.
71 changes: 59 additions & 12 deletions src/tagui_header.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ var p7 = casper.cli.raw.get(6); var p8 = casper.cli.raw.get(7); var p9 = casper.
var automation_start_time = Date.now(); casper.echo('\nSTART - automation started - ' + Date().toLocaleString());

// initialise default global variables
var quiet_mode = false; var save_text_count = 0; var snap_image_count = 0; var sikuli_count = 0;
var quiet_mode = false; var save_text_count = 0; var snap_image_count = 0; var sikuli_count = 0; var chrome_id = 0;

// variable for advance usage of api step
var api_config = {method:'GET', header:[], body:{}};
Expand Down Expand Up @@ -85,7 +85,7 @@ function sikuli_handshake() {techo('waiting for sikuli');
var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\';
var fs = require('fs'); fs.write('tagui.sikuli'+ds+'tagui_sikuli.in','','w'); var sikuli_handshake = '';
if (!fs.exists('tagui.sikuli'+ds+'tagui_sikuli.out')) fs.write('tagui.sikuli'+ds+'tagui_sikuli.out','','w');
do {sikuli_handshake = fs.read('tagui.sikuli'+ds+'tagui_sikuli.out').trim(); sleep(1000);}
do {sleep(1000); sikuli_handshake = fs.read('tagui.sikuli'+ds+'tagui_sikuli.out').trim();}
while (sikuli_handshake !== '[0] START');
techo('connected to sikuli');}

Expand All @@ -94,20 +94,69 @@ function sikuli_step(sikuli_intent) {sikuli_count++;
if (sikuli_count == 1) sikuli_handshake(); // handshake on first call
var ds; if (flow_path.indexOf('/') !== -1) ds = '/'; else ds = '\\';
var fs = require('fs'); fs.write('tagui.sikuli'+ds+'tagui_sikuli.in','['+sikuli_count.toString()+'] '+sikuli_intent,'w');
var sikuli_result = ''; do {sikuli_result = fs.read('tagui.sikuli'+ds+'tagui_sikuli.out').trim(); sleep(1000);}
var sikuli_result = ''; do {sleep(1000); sikuli_result = fs.read('tagui.sikuli'+ds+'tagui_sikuli.out').trim();}
while (sikuli_result.indexOf('['+sikuli_count.toString()+'] ') == -1);
if (sikuli_result.indexOf('SUCCESS') !== -1) return true; else return false;}

if (chrome_id > 0) { // super large if block to load chrome related functions if chrome or headless option is used
chrome_id = 0; // reset chrome_id from 1 back to 0 to prepare for initial call of chrome_step

// for initialising integration with chrome web browser
function chrome_handshake() {// techo('waiting for chrome');
var fs = require('fs'); fs.write('tagui_chrome.in','','w'); var chrome_handshake = '';
if (!fs.exists('tagui_chrome.out')) fs.write('tagui_chrome.out','','w');
do {sleep(50); chrome_handshake = fs.read('tagui_chrome.out').trim();}
while (chrome_handshake !== '[0] START'); //techo('connected to chrome');
}

// send websocket message to chrome browser using chrome debugging protocol
// php helper process tagui_chrome.php running to handle this concurrently
function chrome_step(method,params) {chrome_id++;
if (chrome_id == 1) chrome_handshake(); // handshake on first call
var chrome_intent = JSON.stringify({'id': chrome_id, 'method': method, 'params': params});
var fs = require('fs'); fs.write('tagui_chrome.in','['+chrome_id.toString()+'] '+chrome_intent,'w');
var chrome_result = ''; do {sleep(50); chrome_result = fs.read('tagui_chrome.out').trim();}
while (chrome_result.indexOf('['+chrome_id.toString()+'] ') == -1);
return chrome_result.substring(chrome_result.indexOf('] ')+2);}

// chrome object for handling integration with chrome or headless chrome
var chrome = new Object(); chrome.mouse = new Object();

// chrome methods as casper methods replacement for chrome integration
chrome.exists = function(selector) { // different handling for xpath and css
if ((selector.toString().length >= 16) && (selector.toString().substr(0,16) == 'xpath selector: '))
{if (selector.toString().length == 16) selector = ''; else selector = selector.toString().substring(16);
var ws_message = chrome_step('Runtime.evaluate',{expression: 'document.evaluate(\''+selector+'\',document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotLength'});
try {var ws_json = JSON.parse(ws_message); if (ws_json.result.result.value > 0) return true; else return false;}
catch(e) {return false;}}
else {var ws_message = chrome_step('Runtime.evaluate',{expression: 'document.querySelector(\''+selector+'\')'});
try {var ws_json = JSON.parse(ws_message); if (ws_json.result.result.subtype == 'node') return true; else return false;}
catch(e) {return false;}}};

chrome.click = function(selector) {
};
if ((selector.toString().length >= 16) && (selector.toString().substr(0,16) == 'xpath selector: '))
{if (selector.toString().length == 16) selector = ''; else selector = selector.toString().substring(16);
chrome_step('Runtime.evaluate',{expression: 'document.evaluate(\''+selector+'\',document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null).snapshotItem(0).click()'});}
else chrome_step('Runtime.evaluate',{expression: 'document.querySelector(\''+selector+'\').click()'});};

chrome.mouse.move = function(selector) {
};

chrome.mouse.click = function(selector) {
};

chrome.mouse.doubleclick = function(selector) {
};

chrome.mouse.rightclick = function(selector) {
};

chrome.mouse.down = function(selector) {
};

chrome.mouse.up = function(selector) {
};

chrome.sendKeys = function(selector,value) {
};

Expand Down Expand Up @@ -135,21 +184,19 @@ return '';
};

chrome.getTitle = function() {
return '';
};
var ws_message = chrome_step('Runtime.evaluate',{expression: 'document.title'});
try {var ws_json = JSON.parse(ws_message); return ws_json.result.result.value;} catch(e) {return '';}};

chrome.getCurrentUrl = function() {
return '';
};

chrome.exists = function(selector) {
return false;
};
var ws_message = chrome_step('Runtime.evaluate',{expression: 'document.location.href'});
try {var ws_json = JSON.parse(ws_message); return ws_json.result.result.value;} catch(e) {return '';}};

chrome.echo = function(value) {casper.echo(value);};

chrome.on = function(value,statement) {casper.on(value,statement);};

} // end of super large if block to load chrome related functions if chrome or headless option is used

// for live mode simple parsing of tagui steps into js code
function tagui_parse(raw_input) {return parse_intent(raw_input);}

Expand Down
41 changes: 35 additions & 6 deletions src/tagui_parse.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
$repo_count++;} fclose($repo_file); $repo_count-=2;} //-1 for header, -1 for EOF

$tagui_web_browser = "this"; // set the web browser to be used base on tagui_web_browser environment variable
if ((getenv('tagui_web_browser')=='headless') or (getenv('tagui_web_browser')=='chrome')) $tagui_web_browser = "chrome";
if ((getenv('tagui_web_browser')=='headless') or (getenv('tagui_web_browser')=='chrome')) $tagui_web_browser = 'chrome';

$inside_code_block = 0; // track if step or code is inside user-defined code block
$inside_while_loop = 0; // track if step is in while loop and avoid async wait
Expand Down Expand Up @@ -52,8 +52,35 @@
chmod ($script . '.js',0600); if (!$url_provided) echo "ERROR - first line of " . $script . " not URL\n";

// special handling if chrome or headless chrome is used as browser for automation
// replacement of this.method already happens in step intents, this is mostly to handle user inserted casperjs code
if ($tagui_web_browser == 'chrome') {$script_content = file_get_contents($script . '.js'); // read generated script
$script_content = str_replace("var chrome_id = 0;","var chrome_id = 1;",$script_content); // websocket message id
$script_content = str_replace("casper.exists","chrome.exists",$script_content); // change locator check to chrome
$script_content = str_replace("this.exists","chrome.exists",$script_content); // change this.exists call as well
$script_content = str_replace("casper.click","chrome.click",$script_content); // change click method to chrome
$script_content = str_replace("this.click","chrome.click",$script_content); // change this.click call as well
$script_content = str_replace("casper.mouse","chrome.mouse",$script_content); // change mouse object to chrome
$script_content = str_replace("this.mouse","chrome.mouse",$script_content); // change this.mouse call as well
$script_content = str_replace("casper.sendKeys","chrome.sendKeys",$script_content); // change sendKeys method to chrome
$script_content = str_replace("this.sendKeys","chrome.sendKeys",$script_content); // change this.sendKeys call as well
$script_content = str_replace("casper.selectOptionByValue","chrome.selectOptionByValue",$script_content); // select option
$script_content = str_replace("this.selectOptionByValue","chrome.selectOptionByValue",$script_content); // select option
$script_content = str_replace("casper.fetchText","chrome.fetchText",$script_content); // change fetchText method to chrome
$script_content = str_replace("this.fetchText","chrome.fetchText",$script_content); // change this.fetchText call as well
$script_content = str_replace("casper.capture","chrome.capture",$script_content); // change capture method to chrome
$script_content = str_replace("this.capture","chrome.capture",$script_content); // change this.capture call as well
$script_content = str_replace("casper.captureSelector","chrome.captureSelector",$script_content); // capture selector
$script_content = str_replace("this.captureSelector","chrome.captureSelector",$script_content); // capture selector
$script_content = str_replace("casper.download","chrome.download",$script_content); // change download method to chrome
$script_content = str_replace("this.download","chrome.download",$script_content); // change this.download call as well
$script_content = str_replace("casper.evaluate","chrome.evaluate",$script_content); // change evaluate method to chrome
$script_content = str_replace("this.evaluate","chrome.evaluate",$script_content); // change this.evaluate call as well
$script_content = str_replace("casper.getHTML","chrome.getHTML",$script_content); // change getHTML method to chrome
$script_content = str_replace("this.getHTML","chrome.getHTML",$script_content); // change this.getHTML call as well
$script_content = str_replace("casper.getTitle","chrome.getTitle",$script_content); // change getTitle method to chrome
$script_content = str_replace("this.getTitle","chrome.getTitle",$script_content); // change this.getTitle call as well
$script_content = str_replace("casper.getCurrentUrl","chrome.getCurrentUrl",$script_content); // get current url
$script_content = str_replace("this.getCurrentUrl","chrome.getCurrentUrl",$script_content); // get current url
file_put_contents($script . '.js',$script_content);}

// check quiet parameter to run flow quietly by only showing explicit output
Expand Down Expand Up @@ -248,13 +275,15 @@ function call_sikuli($input_intent,$input_params) { // helper function to use si
end_fi()."});\n\ncasper.then(function() {\n";}

// set of functions to interpret steps into corresponding casperjs code
function url_intent($raw_intent) {
function url_intent($raw_intent) {$twb = $GLOBALS['tagui_web_browser']; $casper_url = $raw_intent; $chrome_call = '';
if ($twb == 'chrome')
{$casper_url = 'about:blank'; $chrome_call = "chrome_step('Page.navigate',{url: '".$raw_intent."'}); sleep(1000);\n";}
if (filter_var($raw_intent, FILTER_VALIDATE_URL) == false)
echo "ERROR - " . current_line() . " invalid URL " . $raw_intent . "\n"; else
if ($GLOBALS['line_number'] == 1) {$GLOBALS['url_provided'] = true; return "casper.start('".$raw_intent.
"', function() {\ntecho('".$raw_intent."' + ' - ' + this.getTitle() + '\\n');});\n\ncasper.then(function() {\n";}
else return "});casper.thenOpen('".$raw_intent."', function() {\ntecho('".
$raw_intent."' + ' - ' + this.getTitle());});\n\ncasper.then(function() {\n";}
if ($GLOBALS['line_number'] == 1) {$GLOBALS['url_provided']=true; return "casper.start('".$casper_url."', function() {\n".
$chrome_call."techo('".$raw_intent."' + ' - ' + ".$twb.".getTitle() + '\\n');});\n\ncasper.then(function() {\n";}
else return "});casper.thenOpen('".$casper_url."', function() {\n".$chrome_call."techo('".
$raw_intent."' + ' - ' + ".$twb.".getTitle());});\n\ncasper.then(function() {\n";}

function tap_intent($raw_intent) {$twb = $GLOBALS['tagui_web_browser'];
$params = trim(substr($raw_intent." ",1+strpos($raw_intent." "," ")));
Expand Down

0 comments on commit a4e1689

Please sign in to comment.