When I perform an action on my page, a spinner is displayed which disappears once the action is completed. I want to wait for the spinner to disappear so as to execute the assert statements.
I read the documentation which tells me how to wait for an element to appear but does not give info on how to wait for the element to disappear
I don't know how to implement this in Cucumber, Geb, Groovy project.
I'll edit/explain this in a bit, when i have more time:
In your page object:
static content = {
loadingSpinner(wait:3, required:false) { $("mat-spinner") }
//this wait:3 is redundant (i think) if we also give the waitFor() a timeout
//required:false allows our wait !displayed to pass even if the element isnt there
}
def "Handle the loader"() {
try {
waitFor(2) { loadingSpinner.isDisplayed() }
} catch (WaitTimeoutException wte) {
//do nothing, if spinner doesnt load then thats ok
//most likely the spinner has come and gone before we finished page load
//if this is not the case, up our waitFor timeout
return true;
}
waitFor(10) { !loadingSpinner.isDisplayed() }
}
As described in the documentation, the waitFor block uses Groovy Truth to know when it waited long enough. When you put a Navigator in it and the element is currently not present, it will wait for it to appear or until the maximum waiting time it elapsed.
So if want to wait for an element to disappear, you can simply put it in a waitFor like this:
// go to the page
waitFor(2) { $(".loadingspinner").displayed }
waitFor(10) { !$(".loadingspinner").displayed }
// do your assertions
In case the loading spinner already disappeared, waitFor will return immediately. In case it never disappears, it will throw a WaitTimeoutException after 10 seconds, which will make your test fail.
Related
This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 1 year ago.
Currently I am creating a background STA thread to keep the UI responsive but it slows down my function calls on the main thread.
Based on this thread How to update progress bar while working in the UI thread I tried the following but the UI only gets updated after all of the work has finished. I tried playing around with the Dispatcher priorities but none of them seem to work.
What I also tried is adding _frmPrg.Refresh() to my Progress callback but this does not seem to change anything.
Dim oProgress = New Progress(Of PrgObject)(Sub(runNumber)
_frmPrg.Invoke((Sub()
_frmPrg.Status = runNumber
End Sub))
End Sub)
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(Sub()
DoLongRunningWork(oProgress, _cancellationToken)
End Sub, System.Windows.Threading.DispatcherPriority.Background)
I can't really help you with your problem, but I'll try to clarify what happens in your posted code.
DoLongRunningWork will be invoked through Dispatcher on the UI thread, when the UI thread is not busy. But once started, it will block the UI thread until it completes. So you can't show a progress this way. Your single chance is, to let DoLongRunningWork run on a background thread. That brings you nothing, if the long-running methods come from office objects, which must be accessed from the UI thread...
The Progress class (see the remarks section) invokes your event handler on the UI thread automatically, so you don't need _frmPrg.Invoke in your event handler.
Maybe you can start a STAthread for your progress form and show it from there. The instance of your Progress class must be created in this thread too, but not before your form is shown to ensure, that the thread becomes a WindowsFormsSynchronisationContext (or you set one explicitly after starting the thread). A plain SynchronisationContext won't work!
At least you get updates in your form this way, but the UI thread of the office app will still be blocked. And of course, any action you make with your progress form must be invoked on the UI thread, if accessing office objects.
After reading some other posts, I decided to suggest another solution. My previous answer still contains usable information, so I'll leave it there. I'm not familiar with VB.NET syntax, so the samples are in C#. I have tested the code in a VSTO plugin for PowerPoint, but it should run in any office application.
Forget the Progress class and background threads. Run everything on the UI thread!
Now use some async code. To stay on the UI thread, we need a "good" SynchronizationContext.
private static void EnsureWinFormsSyncContext()
{
// Ensure that we have a "good" SynchronisationContext
// See https://stackoverflow.com/a/32866156/10318835
if (SynchronizationContext.Current is not WindowsFormsSynchronizationContext)
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
}
This is the event handler of a button. Note the manually added async keyword. The SynchronizationContext.Current gets resetted again and again, so ensure the good one in the EventHandler:
private async void OnButtonClick(object sender, EventArgs e)
{
EnsureWinFormsSyncContext();
// Return from event handler, ensure that we are really async
// See https://stackoverflow.com/a/22645114/10318835
await Task.Yield();
await RunLongOnUIThread();
}
This will be the worker method, also running on the UI thread.
private async Task RunLongOnUIThread()
{
//Dummy code, replace it with your code
var pres = addIn.Application.Presentations.Add();
for (int i = 0; i < 100; i++)
{
Debug.Print("Creating slide {0} on thread {1}", i, Thread.CurrentThread.ManagedThreadId);
// If you have some workloads that can be run on a background
// thread, execute them with await Task.Run(...).
try
{
var layout = pres.Designs[1].SlideMaster.CustomLayouts[1];
var slide = pres.Slides.AddSlide(i + 1, layout);
var shape = slide.Shapes.AddLabel(Microsoft.Office.Core.MsoTextOrientation.msoTextOrientationHorizontal, 0, 15 * i, 100, 15);
shape.TextFrame.TextRange.Text = $"Text on slide {i + 1}";
}
catch (Exception ex)
{
Debug.Print("I don't know what am I doing here, I'm not familiar with PowerPoint... {0}", ex);
}
// Update UI
statusLabel.Text = $"Slide {i + 1} done";
progressBar1.Value = i + 1;
// This is the magic! It gives the main thread the opportunity to update the UI.
// It also processes input messages so you need to disable unwanted buttons etc.
await IdleYield();
}
}
The following method is for Windows Forms Applications where it does the job perfect. I've tried it also in PowerPoint. If you are facing problems, try the WPF flavour with await Dispatcher.Yield(DispatcherPriority.ApplicationIdle) instead of await IdleYield().
private static Task IdleYield()
{
var idleTcs = new TaskCompletionSource<bool>();
void handler(object s, EventArgs e)
{
Application.Idle -= handler;
idleTcs.SetResult(true);
}
Application.Idle += handler;
return idleTcs.Task;
}
Here are the (clickable) links to the answers that I used (I can't put them in the code-blocks...).
Incorrect async/await working, Excel events in Excel Application Level Add-in
When would I use Task.Yield()?
Task.Yield - real usages?
If in your real code something runs not as expected, check the thread you are running on and SynchronizationContext.Current.
I was trying to update the recycler view content from a background thread in Kotlin. I am not using AsyncTask.
Here is my code, i want to know if there is any better way than this:
In my MainActivity, i have progressThread as a member variable.
var progressThread = Thread()
Then in my method where i want to run the thread first i am defining it...like
progressThread = Thread (
Runnable {
kotlin.run {
try {
while (i <= 100 && !progressThread.isInterrupted) {
Thread.sleep(200)
//Some Logic
runOnUiThread {
//this runs in ui thread
}
i++
}
}catch (e:InterruptedException){
progressThread.interrupt()
}
}
})
after that i am starting it in the same method as
progressThread.start()
and for stopping it, i have a listener to cancel the progress and in the callback of that listener, i have written:
progressThread.interrupt()
Updated
Coroutines are stable now,: https://kotlinlang.org/docs/reference/coroutines-overview.html
Old Answer
Yes, you can do this using doAsync from kotlin anko library that is fairly simple and easy to use.
add following line in module level gradle file:
compile "org.jetbrains.anko:anko-commons:0.10.0"
Code example:
val future = doAsync {
// do your background thread task
result = someTask()
uiThread {
// use result here if you want to update ui
updateUI(result)
}
}
code block written in uiThread will only be executed if your Activity or Fragment is in foreground mode (It is lifecycle aware). So if you are trying to stop thread because you don't want your ui code to execute when Activity is in background, then this is an ideal case for you.
As you can check doAsync returns a Future object so you can cancel the background task, by cancel() function:
future.cancel(true)
pass true if you want to stop the thread even when it has started executing.
If you have more specialised case to handle stopping case then you can do the same thing as in your example.
You can use Kotlin Coroutines also but its in Experimental phase, still you can try it out: https://kotlinlang.org/docs/reference/coroutines.html
Is it possible to stop a Thread by its associated QFuture Object ?
Currently i've been starting a video capturing process like this.
this->cameraThreadRepresentation = QtConcurrent::run(this,&MainWindow::startLiveCapturing);
Inside the startLiveCapturing-Method an infinite loop is running that captures images and displays them. So if the user wants to stop that process he should simply press a button and that operation stops.
But it seems that i can not stop this thread by calling the cancel method like this ?
this->cameraThreadRepresentation.cancel();
What i am doing wrong and how can i stop that thread or operation.
From the documentation of QtConcurrent::run:
Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting. The QFuture returned can only be used to query for the running/finished status and the return value of the function.
What you could do is have a button press set a boolean flag in your main window and build your infinite loop like this:
_aborted = false;
forever // Qt syntax for "while( 1 )"
{
if( _aborted ) return;
// do your actual work here
}
Why don't you create a boolean flag that you can test inside your capturing loop and when it is set, it jumps out and the thread exits?
Something like:
MainWindow::onCancelClick() // a slot
{
QMutexLocker locker(&cancelMutex);
stopCapturing = true;
}
And then for your threaded function:
MainWindow::startLiveCapturing()
{
forever
{
...
QMutexLocker locker(&cancelMutex);
if (stopCapturing) break;
}
}
as Qt Document said, you can not use cancel() function fir QConcurrent::run() but you can cancel tasks by this answer :
https://stackoverflow.com/a/16729619/14906306
I have created a chrome extension which does something after its button is clicked.
However I dont want it be abused so I need the its code to be executed after some time.
How can I surround this code with a timeout in order to achieve this?
Thank you for reading me!
chrome.tabs.getSelected(null,function(tab) {
var Mp=tab.url.substring(0,23);
if(Mp=='https://www.example.com')
{
onWindowLoad();
chrome.extension.onMessage.addListener(function(request, sender) {
if (request.action == "getSource")
{
...Working here
}
});
}
else
{
message.innerHTML='<span style="color: #f00">This is not a valid page</span>';
}
});
function onWindowLoad()
{
var message = document.querySelector('#message');
chrome.tabs.executeScript(null, {file: "getPagesSource.js"});
}
I had to make a compromise so just after the getSelected I added the following line:
chrome.browserAction.disable(tab.Id);
It disables the action button thus it cant be clicked while the script sends the data to the server, as with this extension I grab the tab url in order to store it in my database.
After the ajax response and adding the following
if(xhr.readyState==4)
{
message.innerHTML=xhr.responseText;
chrome.browserAction.enable(tab.Id); /////<---THAT LINE
}
the button gets ready again to be clicked.
I couldnt find the way to add a specific delay in seconds, this way seems stupid but its working, as the response's delay from my server is enough for switching the 2 states of the action button.
With this, however another problem came up, which Ill write in different question.
I am using LWUIT ResrouceEditor(latest SVN code revision 1513) to generate a UI State machine.
I want to show a wait screen when a long running command is invoked by a user using a button on the current form. I believe I can use the asynchronous option when linking the command on the button. I have setup a form in which I have a button which should invoke the asynchronous command. In command selection for that button, I have set the action to show the wait screen form and have marked the command as asynchronous. However when I use the asynchronous option, the code shows the wait screen, but after that it throws a NullPointerException.
As per my understanding, once you mark a command as asynchronous, it will call the following methods from a different thread where you can handle its processing.
protected void asyncCommandProcess(Command cmd, ActionEvent sourceEvent);
protected void postAsyncCommand(Command cmd, ActionEvent sourceEvent);
However this methods are not getting called and it throws a NullPointerException.
When I looked at the LWUIT code, in UIBuilder.java(lineno. 2278), I see that it constructs the new thread for an asynchronous command as follows:
new Thread(new FormListener(currentAction, currentActionEvent, f)).start();
But when running it through Debugger I see that currentAction and currentActionEvent are always null. And hence when the FormListener thread starts running, it never calls the above two async command processing methods. Please see the listing of the run() method in the UIBuilder.java(line no. 2178)
public void run() {
if(currentAction != null) {
if(Display.getInstance().isEdt()) {
postAsyncCommand(currentAction, currentActionEvent);
} else {
asyncCommandProcess(currentAction, currentActionEvent);
// wait for the destination form to appear before moving back into the LWUIT thread
waitForForm(destForm);
}
} else {
if(Display.getInstance().isEdt()) {
if(Display.getInstance().getCurrent() != null) {
exitForm(Display.getInstance().getCurrent());
}
Form f = (Form)createContainer(fetchResourceFile(), nextForm);
beforeShow(f);
f.show();
postShow(f);
} else {
if(processBackground(destForm)) {
waitForForm(destForm);
}
}
}
}
In the above method, since the currentAction is null, it always goes into the else statement and since the nextForm is also null, it causes the NullPointerException.
On further look at the UIBuilder.java code, I noticed what is causing the NullPointer exception. It seems when the FormListner is created, it is passed currentAction and currentActionEvent, however they are null at that time. Instead the code should be changed as follows(starting at line 2264):
if(action.startsWith("#")) {
action = action.substring(1);
Form currentForm = Display.getInstance().getCurrent();
if(currentForm != null) {
exitForm(currentForm);
}
Form f = (Form)createContainer(fetchResourceFile(), action);
beforeShow(f);
/* Replace following with next lines for fixing asynchronous command
if(Display.getInstance().getCurrent().getBackCommand() == cmd) {
f.showBack();
} else {
f.show();
}
postShow(f);
new Thread(new FormListener(currentAction, currentActionEvent, f)).start();
*/
new Thread(new FormListener(cmd, evt, f)).start();
return;
}
Can lwuit development team take a look at the above code, review and fix it. After I made the above change, the asynchronous command processing methods were invoked.
Thank you.
Thanks for the information, its probably better to use the issue tracker for things like this (at http://lwuit.java.net).
I will make a similar change although I don't understand why you commented out the form navigation portion.
To solve your use case of a wait screen we have a much simpler solution: Next Form. Just show the wait screen and in it define the "Next Form" property.
This will trigger a background thread to be invoked (processBackground callback) and only when the background thread completes the next form will be shown.