Test Complete Automation - Issue with waiting for an object to load - vbscript

Background:
I have a JAVA application on which we run our Test Complete scripts(We have recently moved from UFT to TestComplete, so TC is a bit new for us). The scripting language used is VBScript.
Issue:
In order to handle the slow application behavior, I have created a function which waits for an object to get loaded and become visible on the screen before any operation is performed on that object. But, at times, the function does not work. By this, I mean that even though the object is loaded and is visible on the screen, the function still keeps on waiting for the object i.e., uiObject.exists keeps on returning false due to which it keeps on waiting until the timeout value is reached. Has someone here faced this issue before?
Paramter values passed:
uiObject = Aliases.ParentObj.Login_Window
intMaxTimeOut = 120
Code
'============================================================================================================
'Function Name: fn_waitForObject
'Purpose: To wait for an object to exist and become visible on screen
'Creation Date: 04-06-2018
'Return type: true, if the object exists and is visible; false, if the object doesn't exist
'Parameters: uiObject - The object for which the script waits to get visible on screen
' intMaxTimeOut - Maximum timeout in seconds
'============================================================================================================
function fn_waitForObject(uiObject,intMaxTimeOut)
Dim intCounter : intCounter = 0
Do While (intCounter < intMaxTimeOut)
If uiObject.exists then
Exit Do
Else
intCounter = intCounter + 1
delay 1000
End If
Loop
'If the object exists, make sure that it is visible on screen
If uiObject.exists then
Do While (intCounter < intMaxTimeOut)
If uiObject.visibleonscreen then
Log.Message "The object """&uiObject.toString&""" exists and is visible on screen"
Exit Do
Else
intCounter = intCounter + 1
delay 1000
End If
Loop
End If
fn_waitForObject = uiObject.visibleonscreen
End Function
Object Spy

Maybe the information on this link can help you!
whats is the difference between UIObject and UIObject2 other than UIAutomator 2.0 version name?

What is the actual error saying in TC?
Have you seen this link? https://support.smartbear.com/testcomplete/docs/app-objects/common-tasks/waiting-process-or-window-activation.html
I would also suggest trying to use the record keyword test, then convert it to script
For now you could increase your max timeout value, but your while loop will still have a hard upper limit. I recommend using one of the methods listed in the article above, as they will make TestComplete wait until your process/objects have fully loaded, no matter how much time has passed. That way, you won't run into your current problem anymore.

It is the Name Mapping creating a 2nd version of the same object.
Go into the Name Mapping and edit the properties to only use static properties, so new versions of the same UIObject are not being created.

Related

Last index can't be located

I have a very different problem here.
I have this code
xpath = "//div[#class='z-listheader-content'][normalize-space()='Name']/ancestor::table/../following-sibling::div/table"
array = #browser.table(xpath: xpath, visible: true).rows.map { |row| [row.cells[0], row.cells[0].text] }
col = array.filter_map { |x| x if x[1].eql?(result) }
When I am executing the aforementioned code, it throws the following error
timed out after 10 seconds, waiting for #<Watir::Row: located: false; {:xpath=>\"//div[#class='z-listheader-content'][normalize-space()='Name']/ancestor::table/../following-sibling::div/table\", :visible=>true, :tag_name=>\"table\"} --> {:index=>10}> to be located\""
10 is actually the last index of the table.
But it works fine if I put sleep 5 before my code segment. However, I was expecting WATIR to be automatically waited, but this is not the case, May I know why?
Here is more clear explanation
I am printing this line
p #browser.table(xpath: xpath, visible: true).rows.count
With sleep this is printing 10
without sleep, this is throwing the following error ends with {:index=>11}> to be located\, you could see the full error below
"timed out after 10 seconds, waiting for #<Watir::Row: located: false; {:xpath=>\"//div[#class='z-listheader-content'][normalize-space()='Name']/ancestor::table/../following-sibling::div/table\", :visible=>true, :tag_name=>\"table\"} --> {:index=>11}> to be located\""
Per the Documentation on waiting there are a few key points that should be noted:
The idea behind implicit waits is good, but there are two main issues with this form of implementation, so Watir does not recommend and does not provide direct access for setting them.
The wait happens during the locate instead of when trying to act on the element. This makes it impossible to immediately query the state of an element before it is there.
Implicit waits by themselves will not be sufficient to handle all of the synchronization issues in your code. The combination of delegating waiting responsibilities to the driver and leveraging polling in the code (explicit waits) can cause weirdness that is difficult to debug...
...The second and recommended approach to waiting in Selenium is to use explicit waits...
...Note that Watir does its automatic waiting when taking actions, not when attempting to locate...
In your case the wait is needed for location and thus the "automatic" wait you were expecting is not actually how watir works.
The Watir library does however provide mechanisms for explicit waits:
wait_until which waits until a specific condition is true
wait Waits until readyState of document is complete.
It appears that based on your posted issue that a waiting timeout has expired for your location before the element could be found.
It is possible that wait_until would resolve this e.g.
table = #browser.table(xpath: xpath).wait_until(&:visible?)
puts table.rows.count

Data Loss Prevention policy making OpenMsgStore fail (0x80040312)

When DLP policy is enabled, Redemption fails with the error:
"All business e-mail messages are protected based on a policy set in your organization. There was an error opening the protected e-mail message."
ulLowLevelError: 2147746578 (i.e. 0x80040312)
ulContext: 805701633 (0x30060801)
Is there any way around this?
The error occurs when trying to access the IPMRootFolder property of a Store object:
// A previous version of the code was multi-threaded, it is no longer.
Session = OutlookRpcLoader.new_RDOSession();
Session.Logon(ProfileName: profile, ShowDialog: false, NewSession: true);
var stores = Session.Stores;
var store = stores["{STORE-NAME}"];
var root = store.IPMRootFolder;
The call stack shows that Redemption.IRDOStore.get_IPMRootFolder() threw the exception.
Edit
This is seen when using Redemption version 5.22.0.5498 loaded via the RedemptionLoader class in .NET (registry-free COM).
When testing with Redemption version 5.19.0.5238 from VBScript using CreateObject(), the error doesn't occur.
Could anything have changed between v5.19 and v5.22?
First of all, you need to detect where your code is running - whether it is the foreground or background thread. I'd suggest checking the ThreadID of the process. The foreground thread has the value set to 1. All background threads will values greater than one. If it is a secondary thread you need to create a new Redemption session on a secondary thread where you are going to use and set the MAPIOBJECT property to the object retrieved from the main thread. For example, a raw sketch in VB.NET:
Dim PrimaryRDOSession As New Redemption.RDOSession()
PrimaryRDOSession.Login([...])
Dim WorkerThread as New System.Threading.Thread(AddressOf ThreadProc)
WorkerThread.Start(PrimaryRDOSession.MAPIOBJECT)
Sub ThreadProc(ByVal param as Object)
Dim ThdRDOSession As New Redemption.RDOSession()
ThdRDOSession.MAPIOBJECT = param
' do other stuff
End Sub
Don't use objects created on the main thread if you are on a secondary one. Ensure you are consistent when using the objects.
I believe this was caused by AppLocker rules blocking unsigned binaries. The resolution was to either code-sign the files or add the program to the AppLocker allow-list.

With rbx.lua, how do you edit GUI properties?

Changing UI_ELEMENT.Visible to true, and then false shows and hides the UI element, however when I switch it to true again it doesn't reappear. I believe this may be an issue of how I'm doing it rather than what I'm doing.
Hi,
I'm new to Roblox Lua (But I have Javascript and C# experience). I am working on making a 'Garage' or 'Parts' GUI. I'm am trying to make a click detector on a text object set the UI_ELEMENT.Visible of a UI element to true. And a text button (Part of the previously mentions UI element) set that UI_ELEMENT.Visible back to false.
This process works fine until I run through it multiple times (e.g setting to true, then false, and then true again). The UI_ELEMENT.Visible is 'locked' at true (as in setting it to false just results in it being set back to true next frame) but the UI doesn't show.
Code:
click_detector1.MouseClick:connect(function(player) -- When clicked
_G.PlayerInfo[player.Name].status = "In Garage" -- set player status to in garage (works fine no issues)
_G.PlayerInfo[player.Name].topbar = "" -- reset topbar (works)
print("this is only supposed to happen once") -- a check to see if this is running more than once
game.Players[tostring(player.Name)].PlayerGui.Garage.menu.Visible = true -- one way that should work
--.Enabled = true -- another way that should work
--.menu.Position = UDim2.new(0.5, 0, 0,0) -- another way that should work (setting position to center of screen)
end)
The above is in a server script (let's call it script #1).
button = script.Parent
local function onButtonActivated()
local Players = game:GetService("Players")
local player = Players.LocalPlayer -- get the local player
print("I am only running once") -- test to see if this is running more than once
game.Players[tostring(player.Name)].PlayerGui.Garage.menu.Visible = false -- one way that should work
--.Enabled = false -- another way that should work
--.menu.Position = UDim2.new(10, 0, 0,0) -- another way that should work (change x scale to off screen)
end
button.Activated:Connect(onButtonActivated)
The above is in a local script (let's call this script #2).
The interesting thing is that none of the methods I proposed in the 'another way that should work' actually function more than the initial first cycle of the loop (e.g setting to true, then false, and then true again).
Also logging the tests to see if they run multiple times only runs once each time it is cycled through (Like it should). However, this means the code for setting it to visible is also running, but not logging an error or doing what it should do.
Thanks, Daniel Morgan
I believe the issue lies with your use of a server script and local script. LocalScripts only change what's on the player's client. Non-local scripts change whats on the server. In other words, non-local scripts affect everyone while LocalScripts only affect individual players.
Setting the visibility of the GUI to false through a LocalScript will only change the GUI on the player's client. However, the server will still see the player's GUI visibility as true. This discrepancy may be causing you issues.
I would suggest an alternative method using RemoteEvents. Instead of changing the GUI visibility to false in a LocalScript as you do in script #2, I would use a RemoteEvent to do that instead. In your case it would look something like the following:
The following is what would be in a non-local script:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = Instance.new("RemoteEvent",ReplicatedStorage)
remoteEvent.Name = "MyRemoteEventName"
-- player is the player object, visibility is a boolean (true or false)
local function ChangeGuiVisibility(player, visibility)
player.PlayerGui.Garage.menu.Visible = visibility
end
-- Call "onCreatePart()" when the client fires the remote event
remoteEvent.OnServerEvent:Connect(onCreatePart)
You can Fire this remote event from a local script like this:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local visiblity = false
local remoteEvent = ReplicatedStorage:WaitForChild("MyRemoteEventName")
-- Fire the remote event.
remoteEvent:FireServer(visibility)-- NOTE: the player object is automatically passed in as the first argument to FireServer()
Here is a very good guide on RemoteEvents: https://developer.roblox.com/en-us/articles/Remote-Functions-and-Events
Basically, RemoteEvents allow you to bridge the gap between the client and server. The client can fire an event to which the server will respond to. A while ago, this was not the case with Roblox. Changes from the client were visible to the server, which made exploiting games very easy.
Also, I'd like to suggest some changes to your first script, which may be helpful for you in the future.
Instead of:
game.Players[tostring(player.Name)].PlayerGui.Garage.menu.Visible = true
try
if player.PlayerGui:FindFirstChild("Garage") then
player.PlayerGui.Garage.menu.Visible = true
end
There is no need to access game.Players since the MouseClick event already returns the player object that clicked the button. I use FindFirstChild which checks if the Garage GUI exists. This can prevent potential errors from occurring and is often good practice.
Hopefully this helps, please follow up if you're still having issues.

What does "DoEvents" do in vb6?

What does "DoEvents" do in vb6 ?
Why do I get the error message "Out of stack space" ? What does it mean ?
DoEvents() allows other Windows messages to be processed.
The reason you get an out of stack space error is probably because DoEvents() is allowing events to occur that call your code again, which again calls DoEvents(), and so on until the stack space, which tracks the return addresses for all these calls, has run out.
In general, I do not recommend using DoEvents() due to problems like these and the fact that it violates the overall event-driven design of Windows.
A slightly different way of looking at DoEvents is that it flushes the events in the event queue. If your sub or function triggers an event, that event handler becomes a sub that is in line to run as soon as your sub/function is finished. DoEvents says to run that event handler sub now, instead of waiting till the end of your sub.
While I agree in spirit with Jonathon about not using DoEvents, I would temper his statement by saying I only recommend using it if you know exactly why, and know all of the repercussions of changing the order of the event queue this way. Most often, DoEvents is indicated when you want to update your screen in some way from within the context of a subroutine, before the subroutine is finished executing.
An example of this is when you are using the ProgressBar control. Suppose you are iterating through several thousand records, and want to provide feedback to the user as to how far along you are by updating a progress bar. You might interrupt your loop every hundred records and change the value on the progressbar control. However (unless you do something about it) you won't see the change on the screen until after the progressbar's change event handler runs, and that handler won't run until your sub is done executing. It will just get put in the event queue. The way to force the change event to run immediately, suspending your sub, is to call DoEvents. This will flush all existing events from the queue--in this case your progressbar's change event--and will update the progressbar control on the screen.
Now, "out of stack space" basically means that you've been caught in an endless loop of function calls. The most basic way to cause that is this:
Public sub MySub()
MySub
End Sub
And then call MySub from somewhere. You'll get an out of stack space error. If you look at the Call Stack, you'll see a very long line of calls to MySub.
A well-known real-world example of this would happen in older versions of VB:
Public Sub TextBoxArray_LostFocus(index as Integer)
If TextBoxArray(index) = "" Then
TextBoxArray(index).SetFocus
MsgBox "Please enter a value"
End If
End Sub
This situation assumes two members of a TextBox control array called TextBoxArray. Now, if the user starts with the first one (index 0) and moves to the second one (index 1) then index 0's LostFocus event will fire. However, VB would also internally set the focus to the index 1 box. Then the code would set the focus back to index 0, firing index 1's LostFocus event! You're caught in a loop. They fixed that in VB5 or 6 by waiting to set the focus until the LostFocus event was done executing.
I would clarify Johnathon's answer in that it pumps that VB message loop and allows the VB Runtime to process windows messages, which is the opposite of Sleep which allows for Windows to process its events (not necessary in the world of Multicore CPUs and true multitasking OS's but when VB6 was written Windows 9x was the dominant OS and a hard loop that only had DoEvents in it would spike the CPU usage to 100%). So seeing things like
While fDoneFile = False
DoEvents
Sleep 55
Wend
was a common pattern throughout the VB6 world.
As stated else where, DoEvents allows other events in your application to fire. Here's an example of how you can use DoEvents without the "Out of stack space" issue. This makes sure you don't run through the code multiple times by using a Boolean to indicate the code is running.
Sub Example()
'Create static variable to indicate the sub is running.
Static isRunning As Boolean
'Exit the sub if isRunning
If isRunning Then Exit Sub
'Indicate sub is running
isRunning = True
'Sub does stuff
DoEvents
'Ends up calling sub again
Example 'Added just to prove via testing.
'Indicate sub is no longer runningrunning
isRunning = False
End Sub

Dialog not available within 60 seconds

I am trying to use Dialog handler twice in a function. For first time it executes well but on second time it hangs the system with dialog box open and showing Ok and Cancel butoon but never able to click it. Also it times out with an error "Dialog not available within 60 seconds"
Dim cdhPopup As ConfirmDialogHandler
cdhPopup = New ConfirmDialogHandler()
If (ie.Button(Find.ById("btnDelete")).Exists) Then
'Cancel the booking '
ie.AddDialogHandler(cdhPopup)
ie.Button(Find.ById("btnDelete")).ClickNoWait()
cdhPopup.WaitUntilExists()
cdhPopup.OKButton.Click()
ie.WaitForComplete() 'Wait for page to finish loading '
Else
Assert.Fail("Could not found the Cancel Button")
End If
Using this at 2 places in my code, First time it executes fine and second time within same function it gives dialog not available whereas it is available error.
My best guess is that in the second pass you are again calling
ie.AddDialogHandler(cdhPopup), thereby registering it a second time, which is somehow crashing the program when the handlers are invoked (cross thread access to internal variables maybe?)
You should perform a check if the handler is registered, and only register it if it isn't.

Resources