I have some trouble with TestComplete because sometimes it won't find my objects, sometimes it just doesn't and I get an error because the object is null.
For instance in this small function
function SelectCountry(country){
var page = Sys.Browser("*").Page("*");
var panel = page.Form("ID1");
select = panel.FindChildByXPath("//select[#id='ID2']");
select.ClickItem(country);
link = page.FindChildByXPath("//a[#id='ID3']");
link.Click();
page.Wait();
}
I get an error for 4 out of 5 runs telling me that select has not been found, but then on the one lucky run, everything passes fine.
Can anyone tell me what I have to check for?
Try searching for your object in a loop. Use the Exists property of the object to determine if the object exists after each search of the page. Another option would be to use the Wait methods https://support.smartbear.com/viewarticle/73657/
I would suggest avoiding hard coded delays for the reasons you have discovered. They way I search for page objects in my project is to do the search in a loop and log an error if not found.
var stopTime = Win32API.GetTickCount() + 60000;
var currentUpTime = Win32API.GetTickCount();
while (currentUpTime < stopTime) { //repeat search for element for n milliseconds
currentUpTime = Win32API.GetTickCount();
for (i = 0; i < attributes.length; i++) {
var element = eval('Sys.Browser("iexplore").Page("*").' + tcMethod + '(' + '"' + attributes[i] + '"' + ',' + "'" + attributeValue + "'" + ',20000' + ')');
if (element.Exists) {
return element;
}
}
}
I found a working solution. It's evident that the source of the problem is that the page is not properly loaded. So I put some hard coded stops before every stap that loades a new page.
aqUtils.Delay(2000);
Sometimes I have even to go for 5 seconds.
This is still not very stable since for some reason delays could be higher sometimes.
Is there some way of telling TestComplete it should try to find an element during 30 seconds and only then raise an error?
you can always put up a delay in the test with the test complete code
aqUtils.Delay(2000);
as mentioned.But this can also occur in case you are doing something very fast in the tests because of which the test reaches the point before the object is visible. That is reason why we use the delay in test to wait for object to load.
Try putting the breakpoint at the object and check for after waiting for 10 seconds.if the test passes in all the cases in this method it should be due to delay in object load.Or use the wait process mentioned in
https://support.smartbear.com/testcomplete/docs/app-objects/common-tasks/waiting-process-or-window-activation.html
Related
I'm a beginning learner of InDesign scripting and would like to help myself with debugging, but my attempts seem to run into walls. Hope someone has some insights that will help me going forward.
I'm working on a little project that loops through some selected tables, puts the 3 tables into an array/variable (accomplished that) and then loops through the content of those tables to find a GREP match and store those in an array/variable (for further uses I won't get into now)
My main objective at this point: See exactly what text characters the .findGrep(); function is catching and display those in the Javascript Console of the ExtendScript Toolkit app.
So here's a bit of the journey up to this point, including codes tried and suggestions from others. (All of my attempted uses of these has failed...why I'm here now... and why this is long; my apologies)
Initial try.
var myTables = []; (in Data Browser this shows values of [object Table], [object Table], [object Table]
var myFinds = [];
var myTest = [];
var myCharacters = [];
app.findGrepPreferences = null;
app.findGrepPreferences.findWhat = "\"";
for (x = 0; x < myTables.length; x++) {
var myFinds = myTables[x].findGrep();
$.writeln(myFinds);
};
Notes on this code: Because not every table has the characters in the findWhat, sometimes in this loop myFinds has nothing, but when it does have something, it shows this in console [object Character],[object Character],[object Character]
So someone (firstHelp) gave me this: And it did not work... error thrown on .contents.toString(); *"undefined is not an object" which I thought, "ok, yes I see at times in the loop myFinds has nothing in it... more on this later"
var stringArray = [];
for( var n=0; n<myFinds.length; n++ ) {
stringArray[n] = myFinds[n].contents.toString();
};
$.writeln(myFinds.join("\r"));
Code revamp Gave up on the $.writeln(myFinds); within the loop and tried this in order to gather Grep finds in a variable/array that could be dealt with outside of loop.
for (x = 0; x < myTables.length; x++) {
$.writeln(myTables[x].cells.firstItem().texts[0].contents[0]);
myFinds.push(myTables[x].findGrep());
};
$.writeln(myFinds);
ExtendScript Toolkit console now showing this for myFinds:
*myFinds = [Array], [object Character], [object Character], [object...
+ (object symbol) 0 =
+ (object symbol) 1 = [object Character], [object Character], [object Character]
+ (object symbol) 2 =
+ (object symbol) _proto_ =*
*again tried the .contents.toString(); on the myFinds and still the same error, "undefined..." including targeting the array when it clearly had something in it.
**So then I get this tipoff...(but no helpful code to apply to what I already have)
"you are dealing with arrays of arrays mixed with texts.
So you have to check with each item of the result array if it is text
or another array of texts.
If it is an array loop that array."
And later this bit of code that is supposed to "flatten" my array... a = [].concat.apply([],a);
Replacing a with myFinds like this, myFinds = [].concat.apply([],myFinds); did absolutely nothing. The array and its contents showed no change in the console... and I have no idea how to loop through each item of this array within an array, find out if it's text or another array and then show its real contents to console.
Really...how many loops and if/thens etc do I need to run on one array to show its actual contents in the console? But I know I struggle with breaking down every little step I want, to its minute scripting granularity and so my ignorance regularly impedes me. I welcome any suggestions/tips to move me closer to my **main objective" as stated above. Thanks
Regarding the first help. The real reason why you get an error while accessing content property is that you don’t check the type of the object and presume it will be a Text object. As the findGrep may not find a Text occurrence, you actually get an empty array. And Array.prototype.contents doesn’t exist hence the error.
Then $.writeln is legacy of Adobe ExtendScript toolkit, the IDE for ExtendScript. This product is no longer de eloped and maintained by Adobe. You should consider using other logging techniques such as the Visual Studio ExtendScript plugin which will allow you to use breakpoints and everything you need.
I am new to Mocha. i am calling the it statement in a loop. I have a working script which i add here to ask if there is a better way to do this.
The following is the working script
var xl = require('./excel');
describe("Register User", function(){
var csv = xl.readExcel(); //gets multiple rows as csv.
var arrRows = csv.split("\n");
var arrRow = []; //will store the current row under test
var iRow = 0;
before(function() {
//can variables csv and arrRows be initialized here?
});
beforeEach(function(){
arrRow = xl.splitCsvToArray(arrRows[iRow++]);
});
for(var i = 0; i < arrRows.length - 1; i++){
it('test case X', function(){
console.log("current row is: " + iRow);
console.log("1st column is: " + arrRow[0][1]);
console.log("2nd column is: " + arrRow[0][2]);
});
}
});
result is
1st column is: col2row3
2nd column is: col3row3
√ test case X
current row is: 5
1st column is: col2row4
2nd column is: col3row4
√ test case X
current row is: 6
1st column is: col2row5
2nd column is: col3row5
√ test case X
current row is: 7
1st column is: col2row6
2nd column is: col3row6
√ test case X
7 passing (27ms)
Thanks in advance.
There's absolutely no problem calling it inside a synchronous loop like you are showing in your code. I do it whenever I have a finite set of conditions I need to test and the tests can be generated by looping.
If you have a loop that generates tests asynchronously then you have to use --delay and call mocha.run() to indicate when the test generation is done and Mocha can start running the tests.
Ideally, you should move your initialization of csv and arrRows into your before hook:
describe("Register User", function(){
var csv;
var arrRows;
var arrRow = []; //will store the current row under test
var iRow = 0;
before(function() {
csv = xl.readExcel(); //gets multiple rows as csv.
arrRows = csv.split("\n");
});
[...]
The only initializations you should feel free to do outside of the before and beforeEach hooks are those that are extremely cheap to do. The problem is that initializations performed outside the hooks are always done even if the suite does not need them. For instance, if you use --grep to select some tests that are outside the describe you show in your question, and your initializations are as you show in your question, then Mocha will load your Excel file and break it into rows even though it is not needed. By putting such initializations in before/beforeEach in the describe block that wraps your tests, you ensure that Mocha will run the initializations only when it needs to run a test that depends on them.
The problem though is that arrRows needs to be defined to run the loop. You can:
Abandon the ideal of not having initialization code outside of the hooks. This means keeping your initialization code as it is.
Move the loop inside it and have one test that checks the entire array. The granularity of your tests is up to you. It is a matter of preference and of how the code you test is structured. There's no hard and fast rule here.
If the structure you expect is meant to be regular and have the same set number of rows each time. Define a variable, e.g. TABLE_LENGTH = 10 and a) use it as the limit in your loop (for(var i = 0; i < TABLE_LENGTH; i++)) b) include in your before an assertion that verifies that the table you get has the length you expect (assert.equal(arrRows.length, TABLE_LENGTH)). This would allow you to perform the initialization as recommented, inside before/beforeEach, and still have a loop that creates multiple it.
I have the following snippet code hooked up to a FormIt email form:
$tv = "taken" . (int)$hook->getValue('datetime');
$docID = $modx->resource->get('id'); //get the page id
$page = $modx->getObject('modResource', $docID);
$current = (int)$page->getTVValue($tv);
if (!$page->setTVValue($tv, $current + 1)) {
$modx->log(xPDO::LOG_LEVEL_ERROR, 'There was a problem saving your TV...');
}
$modx->setPlaceholder('successMessage','<h2 class="success">'.$current.'</h2>');
return true;`
It increments a template variable every time it is run and outputs a success message (although right now I'm using that functionality to output a debug message instead). The problem is, it only increments the TV once after saving the snippet, thereby refreshing the cache. Normally I would call the snippet without cache by appending ! to its name, but that doesn't appear to work for FormIt hooks. How can I get this code to work? Right now I'm running the entire page as uncacheable, but that is obviously suboptimal. Perhaps, there's a way to hook a snippet in an uncached manner? Call a snippet from within a snippet as uncached?
I'm doing something similar - but to count page loads, it looks to me like you are missing the last little bit: $current->save();
<?php
$docID = $modx->resource->get('id');
$tvIdm = 32;
$tvm = $modx->getObject('modTemplateVar',$tvIdm );
$tvm->setValue($docID, $tvm->getValue($docID) + 1 );
$tvm->save();
Try add this before you save $tv object
$tv->_processed = false;
It's derived from modElement's property it extends.
I have this piece of code:
foreach(string pdfFile in Directory.EnumerateFiles(selectedFolderMulti_txt.Text,"*.pdf",SearchOption.AllDirectories))
{
//filePath = pdfFile.FullName;
//string abc = Path.GetFileName(pdfFile);
try
{
//pdfReader = new iTextSharp.text.pdf.PdfReader(filePath);
pdfReader = new iTextSharp.text.pdf.PdfReader(pdfFile);
rownum = pdfListMulti_gridview.Rows.Add();
pdfListMulti_gridview.Rows[rownum].Cells[0].Value = counter++;
//pdfListMulti_gridview.Rows[rownum].Cells[1].Value = pdfFile.Name;
pdfListMulti_gridview.Rows[rownum].Cells[1].Value = System.IO.Path.GetFileName(pdfFile);
pdfListMulti_gridview.Rows[rownum].Cells[2].Value = pdfReader.NumberOfPages;
//pdfListMulti_gridview.Rows[rownum].Cells[3].Value = filePath;
pdfListMulti_gridview.Rows[rownum].Cells[3].Value = pdfFile;
//totalpages += pdfReader.NumberOfPages;
}
catch
{
//MessageBox.Show("There was an error while opening '" + pdfFile.Name + "'", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
MessageBox.Show("There was an error while opening '" + System.IO.Path.GetFileName(pdfFile) + "'", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Problem is that when today I specified a folder having about 4000 pdf files, It took about 20 minutes to read all files and show me the results. Then, I thought what will this code do when I will input a folder having more than 20,000 files.
If I comment out this line:
pdfListMulti_gridview.Rows[rownum].Cells[2].Value = pdfReader.NumberOfPages;
Then, it seems if all of the processing burden is removed from the code.
So, what I want from you guys is a suggestion for making my approach efficient and less time should be taken to process all files. Or there is any alternative?
Definitely do what #ChrisBint said, that will get past Window's slowness with folders with many files.
But to get even more speed make sure to use the overload of PdfReader that takes a RandomAccessFileOrArray object instead. This object is way faster than regular streams in all of my testings. The constructor has a couple of overloads but you should mainly concern yourself with RandomAccessFileOrArray(string filename, bool forceRead). The second parameter is whether or not to load the entire file into memory (if I'm understanding the documentation correctly). For very large files this might be a performance hit but on modern machines it shouldn't matter much so I recommend that you pass true to this. If you pass false the disk will need to be hit several times as the parsing "cursor" walks through the file.
So with all of that you can do this in a very tight loop. For me, 4,000 files containing a total of over 42,000 pages takes about 2 seconds to run.
var files = Directory.EnumerateFiles(workingFolder, "*.pdf");
int totalPageCount = 0;
foreach (string f in files)
{
totalPageCount += new PdfReader(new RandomAccessFileOrArray(f, true), null).NumberOfPages;
}
MessageBox.Show(String.Format("Total Page Count : {0:N0}", totalPageCount));
Personally, I would change your code slightly to not call the Directory.EnumerateFiles in the foreach. For example;
var listOfFiles = Directory.EnumerateFiles(selectedFolderMulti_txt.Text,"*.pdf",SearchOption.AllDirectories);
foreach(string pdfFile in listOfFiles)
{
//Do something
}
I doubt this would impact the overall time by a massive amount, if any.
As far the speed to call the NumberOfPages property. It is unlikely that you will be able to optimise this due to be internal to the pdfReader object. If performance is a concern, then this may require additional hardware.
Personally, I would not factor this as an issue unless I have to continually run the scan (in which case I would start looking at caching/checking for existing files and only adding those that have changed/new).
I have read a number of posts about this but none with any solid answer. Here is my code:
// button creation
onew = document.createElement('input');
onew.setAttribute("type", "button");
onew.setAttribute("value", "hosts");
onew.onclick = function(){fnDisplay_Computers("'" + alines[i] + "'"); }; // ie
onew.setAttribute("onclick", "fnDisplay_Computers('" + alines[i] + "')"); // mozilla
odiv.appendChild(onew);
Now, the setAttribute() method (with the mozilla comment) works fine in mozilla but only if it comes AFTER the line above it. So in other words it seems to just default to whichever gets set last. The .onclick method (with the ie comment) does not work in either case, am I using it incorrectly?
Either way I can't find a way to make this work at all in IE, let alone in both. I did change the function call when using the .onclick method and it worked fine using just a simple call to an alert function which is why I believe my syntax is incorrect.
Long story short, I can't get the onclick parameter to work consistently between IE/Mozilla.
-- Nicholas
onew.setAttribute("type", "button");
Never use setAttribute on HTML documents. IE gets it badly wrong in many cases, and the DOM-HTML properties are shorter, faster and easier to read:
onew.type= 'button';
onew.onclick = function(){fnDisplay_Computers("'" + alines[i] + "'"); }; // ie
What is ‘alines’? Why are you converting it to a string and surrounding it with single quotes? It looks like you are trying to do something heinous involving evaluating code in a string (which is what you're doing below in the ‘onew.setAttribute’ version). Evaluating JavaScript code in strings is almost always the Wrong Thing; avoid it like the plague. In the above case, IE should do the same as Firefox: it shouldn't work.
If ‘alines[i]’ is a string, I guess what you're trying to do is make it remember that string by constructing a code string that will evaluate in JavaScript to the original string. But:
"'" + alines[i] + "'"
is insufficient. What happens if ‘alines[i]’ has an apostrophe in, or a backslash?
'O'Reilly'
you've got a syntax error and possible security hole. Now, you could do something laborious and annoying like:
"'" + alines[i].split('\\').join('\\\\').split("'").join("\\'") + "'"
to try to escape the string, but it's ugly and won't work for other datatypes. You could even ask JavaScript to do it for you:
uneval(alines[i])
But not all objects can even be converted to evaluatable JavaScript source strings; basically the entire approach is doomed to failure.
The normal thing to do if you just want to have the onclick callback call a function with a parameter is to write the code in the straightforward way:
onew.onclick= function() {
fnDisplay_Computers(alines[i]);
};
Generally this will work and is what you want. There is, however, a slight wrinkle which you may have hit here, which could be what is confusing you into considering the wacky approach with the strings.
Namely, if ‘i’ in this case is the variable of an enclosing ‘for’ loop, the reference to ‘alines[i]’ won't do what you think it does. The ‘i’ will be accessed by the callback function when the click happens — which is after the loop has finished. At this point the ‘i’ variable will be left with whatever value it had at the end of the loop, so ‘alines[i]’ will always be the last element of ‘alines’, regardless of which ‘onew’ was clicked.
(See eg. How to fix closure problem in ActionScript 3 (AS3) for some discussion of this. It's one of the biggest causes of confusion with closures in both JavaScript and Python, and should really be fixed at a language level some day.)
You can get around the loop problem by encapsulating the closure in its own function, like this:
function callbackWithArgs(f, args) {
return function() { f.apply(window, args); }
}
// ...
onew.onclick= callbackWithArgs(fnDisplay_Computers, [alines[i]]);
And in a later version of JavaScript, you'll be able to say simply:
onew.onclick= fnDisplay_Computers.bind(window, alines[i]);
If you would like to be able to use ‘Function.bind()’ in browsers today, you can get an implementation from the Prototype framework, or just use:
if (!('bind' in Function.prototype)) {
Function.prototype.bind= function(owner) {
var that= this;
var args= Array.prototype.slice.call(arguments, 1);
return function() {
return that.apply(owner,
args.length===0? arguments : arguments.length===0? args :
args.concat(Array.prototype.slice.call(arguments, 0))
);
};
};
}
I usually use something like:
onew.onclick = new Function("fnDisplay_Computers('" + alines[i] + "')");
this should work both in IE e Firefox.
Use the addEventListener() function with "click" for the type argument for Mozilla-based browsers, and attachEvent() function with "onclick" as the sEvent argument for IE; I find it best to use a try/catch statement, for example:
try {
onew.attachEvent("onclick", //For IE
function(){fnDisplay_Computers("'" + alines[i] + "'"); });
}
catch(e) {
onew.addEventListener("click", //For Mozilla-based browsers
function(){fnDisplay_Computers("'" + alines[i] + "'"); },
false);
}
I think #3 protesteth too much. In a lot of situations I'm building a table dynamically and need to pass parameters to the callback function. It isn't a typesafe issue since my variable parm is an integer index to the table row in question. Here's code with both a variable and fixed parameter that seems to be cross-browser compliant:
for (i = 0; i < arrTableData.length; i++) {
eleTR = objTable.insertRow(i + 1);
cell = eleTR.insertCell(0);
cell.width = "21";
var myElement = document.createElement('img');
myElement.setAttribute('src', 'images/button_down.gif');
myElement.setAttribute('alt', 'Move item down');
myElement.onclick = new Function('moveItem(' + i + ',0)');
cell.appendChild(myElement);
}