When I am testing a GUI, an element appears and disappears too quickly - coffeescript

I am testing the GUI using the tools of Webdriver.io and mocha. The tests themselves are written in CoffeeScript. Some interface elements are loaded for a long time, and a rotating loading indicator appears. In order to continue the testing process, it is necessary to wait for the data to be fully loaded (that is, to wait until the loading indicator disappears). This process was performed using function (1):
wait_for_page_load = () ->
$('... load indicator selector ...').waitForDisplayed(20000)
$('... load indicator selector ...').waitForDisplayed(20000, true)
In the first line, I expect the moment when the download indicator becomes visible. In the second term I expect the disappearance of the loading indicator.
However, in the process, I was faced with a situation in which the download indicator appears and disappears too quickly. At the same time, I simply do not have time to “catch” the loading indicator, because at that moment, when I expect it to appear, it already disappears. At the same time, an error message is displayed in the console:
element ("... load indicator selector ...") still not displayed after 20000ms
I found a way out of this situation. When a similar problem occurred, I fixed only the disappearance of the loading indicator. This process was performed using function (2):
wait_for_page_load = () ->
$('... load indicator selector ...').waitForDisplayed(20000, true)
It should be noted that with the fast disappearance of the loading indicator, it is also impossible to do without waiting at all - in this case, new data will not have time to load.
However, in some situations, I cannot determine in advance exactly how long the loading indicator will be visible: sometimes it disappears almost immediately, and I cannot track the moment it appears, in these cases I have to use function (2); sometimes it rotates for a long time, and it is possible to track the moment of its appearance using the function (1).
Is it possible to write a universal function that will fix the appearance and disappearance of a graphic element, even if the element appears and disappears very quickly?

So far I have found the following way to solve the problem. In situations where it is necessary to wait for the loading indicator to disappear, I first time out one second, and then wait for the loading indicator to disappear.
utilities.wait_for_page_load = () ->
browser.pause(1000)
$('... селектор индикатора загрузки ...').waitForDisplayed(20000, true)
Thus, if the download indicator appeared and disappeared too quickly, then at the end of the timeout, it will no longer be on the screen, respectively, we automatically waited for the download indicator to disappear. If the download indicator hangs on the screen for a long time, then at the end of the timeout, we simply continue to wait for it to disappear.
The method is not ideal. Its main drawback is an increase in the total test run time. However, the method is universal and allows you to handle both situations described in the question.

Related

Way to update user while code is executing?

I'm creating a SwiftUI multiplatform app in XCode and I have a section of my code that hangs. I want to update the user so they know what's happening and are willing to wait. I originally planned to have an alert with text that changed and then planned to have a Text element that updated. In both cases this isn't shown until after the code executes and as such only shows the final message (normally the success done message unless an error made it end sooner).
Is there anyway I can have a visible message to the user through an alert or SwiftUI element that is updated right away and thus will be helpful?
The fact that the alert isn't even shown until after the code executes is bad and incorrect. This suggests that you are doing something lengthy on the main thread, and that is an absolute no-no. You are freezing the interface and you risk the WatchDog process crashing your app before the user's very eyes. If something takes time, do it in the background.

Navigation lags as more remote data is loaded and added to the screen in SwiftUI

I'm working on a screen in my app that allows users to leave comments. Additionally, they can view previous comments by tapping a button. The most recent comments are always at the end of the comments list, and the oldest at the top.
When the view more button is tapped, I fetch 10 previous comments from the database. My fetching code looks like this:
func fetchMoreComments(for gameId: Int, isLoadingMoreComments: Binding<Bool>) {
guard self.canLoadMoreComments else { return }
APIManager
.CommentsAPI
.getMoreComments(for: gameId, lastCommentId: self.lastCommentId!)
.sink(receiveCompletion: { completion in
print("load more comments completion:", completion)
},
receiveValue: {[weak self] response in
if response.statusCode == 200 {
// self?.comments.reserveCapacity((self?.comments.count)! + response.items.count)
let comments = response.items
if let lastCommentId = comments.map({ $0.id! }).min() {
self?.lastCommentId = lastCommentId
}
self?.comments.insert(contentsOf: comments.sorted(), at: 0)
isLoadingMoreComments.wrappedValue = false
self?.canLoadMoreComments = response.items.count >= 1
}
})
.store(in: &subscriptions)
}
Also, I should mention, when the screen is first loaded, there is another function I use to pull the 3 most recent comments to display on the screen the first time the user loads that particular screen.
The problem
As more comments are loaded, the navigation slows down. What I mean is, if I tap the back button to go back to the previous screen, there is a delay of about 4-5 seconds before I'm actually taken back to that screen. The reverse also has the same problem. It takes about 4-5 seconds to load the screen with the comments.
What I've tried
1)
I spent days sorting out memory leaks and cleaning up my code as I tried to find the problem. What I found was that, if I commented out the forloop that reads the array from my view model (the one function I pasted above lives in), then the laggy navigation issue disappears. I've pasted the forloop below. I've tried using .id from my commments object and also .self to improve the performance, but it didn't make a difference.
ForEach(self.liveGameViewModel.comments, id: \.self) { comment in
LiveGameCommentCell(comment: comment)
}
.id(UUID())
2)
I further narrowed things down to a NavigationLink inside the LiveGameCommentCell. That navigation link was a link to the commenter's profile screen. I noticed, once I commented out this navigation link, the navigation lag was partially cured. I could load up to 100 comments (10 at a time), then leave the screen (there would still be a few seconds of lag), however, once navigating back into the screen with the comments, the lag would be completely gone.
3)
Since I could no longer use the navigation, I decided to use a sheet to present a user's profile screen instead. Here's where I noticed another issue, or maybe just the same issue I've had all along. I would load the screen with the comments, and tap on a commenter's image, and the sheet would present itself with no lag. I'd then dismiss the sheet and load a few more comments and tap another commenter's profile image. I noticed, the more comments I loaded, the longer the sheet would take to present itself.
4)
Next, I decided to tweak how many comments are pulled from the database initially. I upped the limit to 100. Without loading more comments, I tapped on a commenter's profile image again, and the lag was there again. The problem is definitely related to the comments.
5)
I tried setting Swift array's reserveCapacity(_:) to 10 on the initial load of comments, then each time more comments were loaded, I'd set the capacity to the current number of comments in the array + the number received in the response. This didn't make much of a noticable difference.
I can't seem to track down this issue, but I know it's directly related to the number of comments on the screen. The more comments there are on the screen, the longer the sheet takes to present, or the longer it takes to navigate back to the previous screen.
I've refactored my code and fixed memory leaks I encountered. Most of the view models related to each screen only live for the life of that screen. For example, when a screen is dismissed, the view model is deinitialised.
Inside instruments, LiveGameCommentCell has the highest count and total duration. The count increases as I load more comments.
What could be causing this issue? I use the same type of functionality on other screens and don't have this issue. The only difference is, the other screens are entirely lists which contain foreach loops, whereas this problematic screen, is made up of the 2 images (host, challenger), some game info, such as, a short descriptive sentence, a deadline, comment count, the category, and then underneath all of this, a section for the list of comments and a text view to post new comments.
I even changed things around so that the API response for the comments would provide all of the info I would need for the comments, rather than utilising foreign keys and making multiple requests from the app. Everything that is needed is delivered in the getMostRecentComments and getMoreComments requests.
There were a few memory leaks I didn't get around to solving because they are most likely related to something else because they show regardless of visiting the screen that shows comments.
Also, I noticed when I first arrive the screen that shows the comments and tap a commenter's profile image, there are no errors. If I tap the next set of 10 comments that are loaded, there are no errors, but as the lagging increases, I notice an error starts to show when I tap on commenter's images. The error is:
[Common] _BSMachError: port 1c433; (os/kern) invalid capability (0x14) "Unable to insert COPY_SEND"
I'd really appreciate some clues that may help me solve this issue. At present, it's not at all obvious to me what is causing this issue.
Thanks in advance
I had a ForEach loop inside a ScrollView, and not inside a List. Replacing the ScrollView with a list, and wrapping my NavigationLinks inside a LazyView solved the issue.

Process page, or be notified that elements are present even if page not fully loaded

The page I'm using watir-webdriver on will often not load completely for maybe like a minute because of the many external scripts I'm loading/using. This of course means that I have to wait needlessly for a minute when I could have performed the checks I wanted to in 10 seconds after the browser I'm controlling (Firefox) started loading the page.
I noticed that even though my target elements become immediately available in the page, the browser.div(:id => 'some_id').present? method hangs until the page is fully loaded. I noticed that if I press on the stop button in the browser window, watir will immediately continue successfully with my tests (the present? method un-hangs). Is there a way to avoid this behavior of the present? method and be able to tell that my target elements are present without having to wait for the page to fully load?
I've experienced this issue before as well. The method I employed, which rides on the idea you mentioned about hitting the stop button, is as follows:
begin
browser.wait_until{browser.div(:id => 'some_id').present?}
rescue
browser.send_keys :escape
end
expect(browser.div(:id=> 'some_id').present?).to be true
This, by default, gives that div 30 seconds to appear (you can insert a smaller value then 30 if you prefer), otherwise it causes watir to hit 'escape' which will stop any remaining background page loading and resume the test.
.present?
As I understand it checks to see if an element exists AND is visible.
What I would suggest you try, is
.exists?
This will simply check that the element exists, and doesnt care if its visible or not.
Just for reference, there is also a
.visible?
Which just makes sure the item is visible, though I dont ever use that one.

Finish activity onPause but still be in backstack?

I'm trying to minimize memory usage in my app, and one of the things I'm doing is calling finish() in the onPause method (which I know is not the best way to do things). For the most part, it seems to be working well, but when the user clicks the back button from the next activity, it logically skips over the finished activity and goes back further. Is it possible to have that activity in the back stack and just get recreated if the user presses back?
No. This conclusion comes from the task and backstack documentation as well as the activity documentation and a general understanding of how a stack data structure works.
A stack data strucure only has 2 possible operations push/put, which adds something to the collection, and pop, which removes it. Stacks folow a last in first out model, or LIFO, where by last thing added - in your case an activity - is the first thing removed when pop is called.
Within the android lifecycle activities are generally popped from the stack when the back button is pressed. At that point onDestroy() is called and the activity is removed (you can verify this by overriding the onDestroy() method and logging the results if you want to check). Alternativly you can force onDestroy() to be called by calling finish() as you are. Finishing an activity effectivly does the same thing as pressing back. The activity is destroyed and must be recreated before it can be added to the stack.
For what you're trying to do the stack would have to incorporate some intermediate state in which an activity does not exist but rather something akin to a reference is held that, when moved to the top, would indicate that the corresponding activity should be recreated. Since this is not how the sack works - it only holds activities - that state cannont exist and so the result you are talking about is not possible.
Your Goal is to minimize memory usage,Just make use of activity life cycle, You can do this alternative(if you need)
-Just leave onCreate() method blank.(only do setContentView(layout))
-Override onResume();
-whatever you were doing in onCreate just copy paste to onResume().
-and In onPause(), Recycle your all bitmaps and set them to null(I think you are using Bitmaps thats why you are very cautious about it ). and remove your views.
Now what will happen, when you launch your new activity, onPause() would be called. that will remove your all bitmap and views. and when you come back, onResume() will be call.(onCreate will not be called). and that will again initialize your view and bitmaps.
No, i don't think that is possible. Once you finish the Activity it's gone. You could, however, implement and handle your own stack. On back pressed, you would just start the closed Activity again.

How do I implement "Cancel" button inside the method building data source in ModelController.m for Page View Controller

I have a Page View Controller that's implemented in 3 files. One of them, the ModelController.m implements "generateData" method, which basically just builds and formats all the pages for this Page View Controller. Inside this method, the first thing I do is I create a Progress Bar popup with "Cancel" button. Then, inside the while() loop, I keep building the pages and at the same time, every 10th page, I update the progress bar for the user to see that the application is still generating the output.
Once the "generateData" method completes, i.e. builds all the pages (and there may be over 1,000 or even 10,000 pages depending on user input), it returns "self" to a method inside RootViewController.m, which in turn passes that generated data in "dataObject" to "viewWillAppear" in DataViewController.m, which finally displays pages in that object to the user.
It all works great, except the "Cancel" button. Because "generateData" method runs on the main thread, it blocks and the "Cancel" button becomes totally unresponsive. By "blocks" I of course mean it takes all CPU cycles, not allowing anyting else to execute, and it may take over a minute to generate 10,000 pages so I really want to allow the user to cancel the operation.
I tried to create a separate thread for the Progress Bar popup which contains Cancel button, but that won't work because all UI operations need to be performed on the application's main thread.
What almost seems to work, is when I put the "generateData" method on a separate thread and keep the Progress Bar on the main thread. This would work just fine, except that now "generateData" is put on another thread to execute in the background and returns immediately, hence returning empty "self" or rather empty "dataObject", causing a crash because there is nothing in that object yet to display.
So how can I check if the "generateData" thread is done, and "return self" ONLY when it's done? I can't really have some BOOL true/false variable and do it in a loop, because that loop on the main thread would be again blocking the "Cancel" button.
Or, alternatively, what is the correct way to implement the "Cancel" button on a lengthy method in iOS? Maybe I'm doing it all wrong, but for the life of me I can't find the right recipe for this.
Thank you in advance for the help, the code is rather extensive and that's why I didn't include any, but I hope my description gives you a good idea of what's going on. I can provide code if that would help.
The correct way to do this is to load the data asynchronously (on a background thread) and update the main (UI) thread to update the progress bar, etc... But you probably don't need a progress bar since you can probably load those pages faster than a user can flip them, so load 100 and let the user play while you continue to load the next 99,900... Except those may not be the exact numbers you should use....
If you continue to use the progress view and cancel button, then you would cancel the background thread when the user presses "Cancel", which would respond since your data generation is on a background thread...
Look into either Grand Central Dispatch or NSInvocationOperations
Here's a good tutorial by Ray Wenderlich to get you started.