The best place to ask programming/development questions, imo. UPDATE: stackoverflow is the *worst* place to *ask* questions (if your first question/comment doesn't get any up-rating/response, then u can't ask anymore questions--ridiculously unrealistic), but a great reference for finding answers.

My Music (Nickleus)

20130324

how to parse multiple cruisecontrol log files and put all svn commit comments into a single text file...

...to serve as a summary list of changes.

say you have a folder with cruisecontrol log files, like this:
...
log20130204150005Lbuild.1403.xml  log20130321154941Lbuild.1611.xml
log20130205085601Lbuild.1404.xml  log20130322094540Lbuild.1612.xml
log20130205101742Lbuild.1405.xml  log20130322132805Lbuild.1613.xml
log20130205102821Lbuild.1406.xml  log20130322133911Lbuild.1614.xml
log20130205113917Lbuild.1407.xml  log20130322152039Lbuild.1615.xml
log20130205120959Lbuild.1408.xml  log20130322153132Lbuild.1616.xml
...


and you want to make a summary of changes, based on comments you've written when you've committed code to svn.

a log file's xml looks like this:
<cruisecontrol>
 <modifications>
  <modification type="svn">
   <file action="modified">
    <revision>3481</revision>
    <filename>/trunk/build_scripts/build.properties</filename>
   </file>
   <date>2012-12-11T12:21:15</date>
   <user>nick</user>
   <comment>removing spring</comment>
   <revision>3481</revision>
  </modification>
  ...


just run the following code in a terminal, in the directory where your log files are:
xpath -q -e /cruisecontrol/modifications/modification/comment log* > comments.txt

NOTE: /cruisecontrol/modifications/modification/comment is the XPATH to the comment tag in the xml file.


comments.txt will look something like this:
<comment>converted iterator loops to enhanced for loops, added generics</comment>
<comment />
<comment />
<comment>forbid users to create users with usernames that start with "admin"</comment>
<comment>added new labels</comment>
<comment>removing spring</comment>

...


20130321

sahi - tip: you can use boolean operators in sahi functions

_assert(_isVisible(_button("Logg inn") || _button("Login")));

this checks that the login button on our login page is visible, whether it be in norwegian or english.

you can also use it for functions like _click():
_click(_button("Logg inn") || _button("Login"));

i wasn't previously aware that one could use boolean operators in sahi, since there seems to be no mention of it in the sahi apis.

20130317

sahi - how to parse the column(s) name(s) in an sql search

in our company's application we have a page in our admin backend where we can run sql queries. we display the results in a table, so i made a script that runs an sql query then checks to see if at least one of the select parameters is visible. here's the code:

// vars
//var $sql_query = "select count(*) from membershipapplications";    // 1 column

var $sql_query = "select id, partyname from membershipapplications where id=1";    // multiple columns

do_admin_sql_search($sql_query);

///////////////FUNCTION CODE/////////////
 

function do_admin_sql_search($sql_query) {
    ...
    var $regex = /(.*select\s+)(.*)(\s+from.*)/;
    var $columns = $sql_query.replace($regex, "$2");
    var $untrimmed_column1 = $columns.substring(0, $columns.indexOf(","));
    var $column1;
    if($untrimmed_column1.length > 0) {
        $column1 = $untrimmed_column1.replace(/^\s+|\s+$/g,"");    // .trim() doesn't work in sahi apparently
    } else {
        $column1 = $columns;
    }
   
    // assert that the query gave some kind of result
    _assert(_isVisible(_tableHeader($column1.toUpperCase())));
}

20130315

sahi - how to replace the domain of a url

in our company's application we send out an email asking users to confirm their new membership application by clicking on a link:
http://prod.mycompany.com/MembershipApplication/validateMembershipApplication?id=9354&email=me@example.com

but when i'm testing this usecase in sahi, i don't want to click on a production link, i want to use a link on my test server:
http://localhost/MembershipApplication/validateMembershipApplication?id=9354&email=me@example.com

so here's how i replace the prod link with localhost:
#######################

var $prod_url;

// ... more code ...

_set($prod_url, _link(/http:\/\/prod\.mycompany\.com\/MembershipApplication\/validateMembershipApplication\?id=.*/).href);
 

_call(window.open($prod_url.split("http://prod.mycompany.com").join("http://localhost")));

#######################

this code identifies a link that matches the regex below and returns the url ( .href ):

_link(/http:\/\/prod\.mycompany\.com\/MembershipApplication\/validateMembershipApplication\?id=.*/).href


thanks to the post Javascript - how to replace a sub-string? for the replace code :)

sahi - how to confirm a browser javascript dialogue/prompt

when i use sahi to click on a link in gmail, gmail opens a confirm dialogue asking me to confirm to leave the page ("Leave page" button). here's the code to automatically accept the "Leave page" option:

var $link_url = "http://example.com";
// ... more code ...
_call(window.open($link_url);
_expectConfirm(/.*/, true);

20130314

sahi - how to get a link element's url and store it in a variable

i have a link on a webpage like this:
example.com

here's how i get the href/url for the link and store it in a variable in sahi:
var $link_url;
_set($link_url, _link("example.com").href);


now $link_url has the following value:
http://example.com

thanks to this sahi forum post for pointing me in the right direction:
How do I get a list of links in the page

sahi - how to test https gmail in firefox

i'm trying to use sahi to login to my gmail (using firefox) and click on a registration link in an email as a party of my company's sahi test case, but i'm getting the following errors:

ERROR
Step >_sahi._call(window.open("https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue=https://mail.google.com/mail/&ss=1&scc=1<mpl=default<mplcache=2";), "gmailWindow");< did not complete in 150 seconds.

and

_popup("gmailWindow")._setValue(_textbox("Email"), "my_email_address@gmail.com");
Window/Domain not found: popupNameFromStep=gmailWindow; derivedName=; windowName=; windowTitle=; wasOpened=0; domain=



SOLUTION

1. click "firefox" in the sahi dashboard

2. click the link "SSL Manager"

3. copy each domain marked with a red dot, into a tab, prefixed by "https://", hit enter, accept security risk mumbo jumbo.

UPDATE 20130320: these are the domains that need to be accepted:
sahi.example.com
mail.google.com
ssl.google-analytics.com
accounts.google.com
sahi.example.com
accounts.youtube.com
gmail.com


4. go back to the default page: http://sahi.example.com/_s_/dyn/Driver_initialized (or close the ff window and reopen it) and open the sahi controller

5. choose my script, e.g. test_gmail_login.sah

my script code looks like this:
var $userMail = "my_email_address@gmail.com";
_setValue(_emailbox("Email"), $userMail);
_setValue(_password("Passwd"), _prompt("Enter gmail password:"));
_click(_submit("signIn"));

NOTE: the username field in gmail uses: _emailbox
NOT _textbox

6. set "https://gmail.com" as the "start url", click "set"

7. click "play"

so it DID seem to be an issue with SSL :)

---
NOTE:

this info is a repost from my original thread on the sahi forums, which i solved myself:
how to use sahi with gmail?


---
NOTE 2:

the sahi forum thread Problem with Automating gmail web page (HTTPS SSL certificate error)
suggests adding the following value:
xhr.wait_ready_states=2

at the end of the file:
sahi/userdata/config/userdata.properties

when using sahi in gmail. and the sahi documentation page troubleshooting sahi
suggests this value:
xhr.wait_ready_states=2,3

but in my case it seems to work without it.

---
NOTE 3:

sometimes when i first run the test, i get the following error message after logging into gmail:
Your browser's cookie functionality is turned off. Please turn it on.

all i have to do then is just click "set" in the sahi controller and run the test again and then it works.

20130313

sahi/javascript - function to generate a random 13-digit gs1 gln number

i am currently making sahi tests for registering member applications for my employer and i needed a way to automatically generate a GLN number with a correct control/check digit, so here's my solution:

///////// SAHI CODE //////////

// function call
var $gln_base_id_number = Math.floor(Math.random()*900000000000) + 100000000000;    // 12-digit random number
var $gln_check_digit = generate_gln_check_digit($gln_base_id_number);

var $gln = $gln_base_id_number + "" + $gln_check_digit;    // convert the number to a string


// function
function generate_gln_check_digit($gln_base_id_number) {
    $gln_base_id_number = (""+$gln_base_id_number);    // convert the number to a string
    var $factor = 3;
    var $sum = 0;
    var $index = $gln_base_id_number.length;

    for ($index; $index > 0; --$index) {
        $sum = $sum + $gln_base_id_number.substring($index-1, $index) * $factor;
        $factor = 4 - $factor;
    }
    return ((1000 - $sum) % 10);
}


//////////////////////////////////////////////

///////// JAVASCRIPT CODE //////////

// function call
var gln_base_id_number = Math.floor(Math.random()*900000000000) + 100000000000;    // 12-digit random number
var gln_check_digit = generate_gln_check_digit(gln_base_id_number);

var gln = gln_base_id_number + "" + gln_check_digit;    // convert the number to a string


// function
function generate_gln_check_digit(gln_base_id_number) {
    gln_base_id_number = (""+gln_base_id_number);    // convert the number to a string
    var factor = 3;
    var sum = 0;
    var index = gln_base_id_number.length;

    for (index; index > 0; --index) {
        sum = sum + gln_base_id_number.substring(index-1, index) * factor;
        factor = 4 - factor;
    }
    return ((1000 - sum) % 10);
}


//////////////////////////////////////////////


i made this based on:
* gs1's online tool, which includes this external form
* gs1's javascript

20130312

[SOLVED] sahi - ERROR missing ( before function parameters

i was sahi testing the following function:

function do_must_change_password($invalid_security_policy_password, $valid_security_policy_password) {
    _setValue(_password("changePwForm:currentpassword"), $invalid_security_policy_password);
    _setValue(_password("changePwForm:newpassword"), $valid_security_policy_password);

    _setValue(_password("changePwForm:confirmnewpassword"), $valid_security_policy_password);
    _click(_button("changePwForm:changePwBtn"));
}


but sahi was throwing this error on the line highlighted in red:
ERROR
missing ( before function parameters.


i found out that you can't end a sahi function name with _password, so i had to change the function name to:
function do_must_change_pwd(...

and then it worked :)

i've noticed that this also applies to function names ending in:
_list
_file

UPDATE: it looks like this might be the list of function name restrictions:
sahi/config/normal_functions.txt

20130310

sahi - how to verify elements in new window (popup)

in our company's application we have a link that opens a new google maps window showing the GPS coordinates location of a transport request party.


i want to assert that the output is correct, so here's how i did it:

/////////////////////////////////
function view_tnt_trip_event_loc($tripId, $trip_event_loc) {
    view_tnt_by_tripId($tripId);
    _click(_link($trip_event_loc));
    $google_maps_window_prefix = $trip_event_loc+" - Google Maps";
    _popup($google_maps_window_prefix)._assert(_containsText(_textbox("gbqfq"), $trip_event_loc));
}

/////////////////////////////////

$trip_event_loc is a text string GPS coordinate, e.g.: "59.7692516149341,10.25079422084966"

$google_maps_window_prefix is simply the window title, e.g.:
"59.7692516149341,10.25079422084966 - Google Maps"

gbqfq is google's id for the maps window search textbox.

NOTE: when you run a sahi script that opens a new window, a new Sahi Controller window opens up for that new window. there you can find the _popup(identifier) (window title) in the "Prefix" field in the "Record" tab of that new Sahi Controller window.

thanks to this page for pointing me in the right direction:
How to switch between child and parent windows?

20130307

sahi open source edition 3.5 - encrypted password workaround

this might not be what you were searching for, but apparently, only sahi pro offers encrypted passwords.

i am going to start on some admin scripts, testing our admin backend, but i don't want to put our admin user name and password in a sahi script file, so to get around this i'm going to make the scripts start after i've already manually logged in to the admin backend, i.e. no login function.

the limitation with this is that they can't be run in a suite. i have to start the sahi dashboard, choose my admin script, "Set", then log in with my admin credentials AND THEN "Run" the script.

UPDATE:
you can also semi-automate this by using sahi's "_prompt" functionality:
_setValue(_textbox("username"), _prompt("Enter your username:"));
_setValue(_password("password"), _prompt("Enter your password:"));

UPDATE 20130325:
you can also fully automate tests by reading admin login credentials (username, password) from a file, WITHOUT committing the contents of the file to version control.

here's how i implemented it:

//////////test_admin_login.sah/////////
_include("functions/common_functions.sah");
...
my_login_admin();
...
//////////////////////////////////////////////////

//////////common_functions.sah/////////
function my_login( $username, $password){
    _assert(_isVisible(_button("Login")));
    _setValue(_textbox("loginForm:username"), $username);
    _setValue(_password("loginForm:password"), $password);

    _click(_button("Login"));
    _assert(_isVisible(_label("Logged in as:")));
}
 

...
function my_login_admin(){
    var $ar = _readCSVFile("functions/admin_login_DO_NOT_COMMIT_THIS_FILE_WITH_ADMIN_USER_PASS_IN_IT.csv", "\t");
    my_login($ar[0][0], $ar[0][1]);
}

...
//////////////////////////////////////////////////

contents of the admin login credentials file committed to svn:
/////admin_login_DO_NOT_COMMIT_THIS_FILE_WITH_ADMIN_USER_PASS_IN_IT.csv///////

"" ""

//////////////////////////////////////////////////

contents of the admin login credentials file on your local machine:
/////admin_login_DO_NOT_COMMIT_THIS_FILE_WITH_ADMIN_USER_PASS_IN_IT.csv///////

"myAdminUser" "myAdminPassword"

 //////////////////////////////////////////////////

NOTE: those fields are tab-separated


20130305

sahi - automated web form submit using values from tab separated csv file, in linux

here's a simple sahi script that reads a tab separated csv file into a 2 dimensional array, logs into mywebsite.com, navigates to a user profile page, and updates user input text field values for "first name" and "last name", saves (submits the user profile form), then logs out of mywebsite.com:

########test_read_file_and_update_user_name.sah###############

// start URL: http://mywebsite.com/login.jsf

var $ar = _readCSVFile("/home/me/userprofile.csv", "\t");

_setValue(_textbox("username"), "myusername");

_setValue(_password("password"), "mypassword");

_click(_button("log in"));

_click(_link("User profile"));

_setValue(_textbox("firstname"), $ar[0][0]);

_setValue(_textbox("lastname"), $ar[0][1]);

_click(_button("Save"));

_click(_link("log out"));


##########################################################

start URL is a comment showing what you manually paste into the sahi controller > playback > start url field

userprofile.csv looks like this in a text editor:
"nick"    "humphrey"

see part 1 of this tutorial:
sahi - automated web data extraction, export to tab separated csv file, in linux

sahi - automated web data extraction, export to tab separated csv file, in linux

here's a simple web data extraction script that logs into mywebsite.com, navigates to a user profile page, extracts the input text field values for "first name" and "last name", saves the values in a tab separated csv file, then logs out of mywebsite.com:

########test_write_to_file.sah###############

// start URL: http://mywebsite.com/login.jsf

_setValue(_textbox("username"), "myusername");
 

_setValue(_password("password"), "mypassword");
 

_click(_button("log in"));
_click(_link("User profile"));

var $ar = new Array();
 

$ar[0] = [_getText(_textbox("firstname")), _getText(_textbox("lastname"))];
 

_writeCSVFile($ar, "/home/me/userprofile.csv", true, "\t");
 

_click(_link("log out"));

########################################


start URL is a comment showing what you manually paste into the sahi controller > playback > start url field

see also part 2 of this tutorial:
sahi - automated web form submit using values from tab separated csv file, in linux

great list of data mining/extraction command line utilities for linux

http://www.ibm.com/developerworks/linux/library/l-textutils/index.html

to do anything advanced, you first need to know how to use regular expressions (regex/regexp).

grep
fgrep
egrep
cut
paste
join
awk
head
tail

i would also add the following programs to the list:
gawk
sed
wget
curl

thank you IBM :)

20130301

[SOLVED] jsf/richfaces can't set id dynamically for use in sahi test: java.lang.IllegalArgumentException

i'm currently writing sahi scripts to test our web app. i ran into a problem with our dynamic trip table where there was no identifier for the delete image:




the delete button code looks like this:
<a4j:commandLink id="removeTRQlink" title="#{msg['label.trip.confirmRemoveTRQ']}" action="#{listRequestsDM.tripBean.setCurrentTripAndTRQ(listObj, trq)}" oncomplete="#{rich:component('removeTRQPanelTrip')}.show()" reRender="removeTRQPanelTrip">
  <h:graphicImage value="/images/icons/delete.gif" style="border:0" />
</a4j:commandLink>


when i tried setting the a4j:commandLink id dynamically (using the transport request receiver ref id, like this:
<a4j:commandLink id="#{trq.receiverRefId}" title="#{msg['label.trip.confirmRemoveTRQ']}" action="#{listRequestsDM.tripBean.setCurrentTripAndTRQ(listObj, trq)}" oncomplete="#{rich:component('removeTRQPanelTrip')}.show()" reRender="removeTRQPanelTrip">
  <h:graphicImage value="/images/icons/delete.gif" style="border:0" />
</a4j:commandLink>


i would get the following error:
java.lang.IllegalArgumentException

eventually, after reading the sahi documentation, i noticed that images can also be identified by title. and jsf/richfaces also allows setting the title value dynamically :)
<h:graphicImage value="/images/icons/delete.gif" title="#{trq.receiverRefId}" style="border:0" />

and here's the sahi script code:
_click(_image($tr_id));

then we don't need any proximity qualifiers and we can precisely match any transport request in the subtable! :)

NOTE: trq.receiverRefId == $tr_id

pages that helped me:
http://sahi.co.in/forums/discussion/3870/identifying-image-buttons-uniquely/p1
http://sahi.co.in/w/all-apis