Cypress: How can one test a rapidly changing value (element text) - automated-tests

I am testing a renewals application which contains a button which will temporarily (perhaps 0.5-1second) contain the text "..." while an operation occurs (following clicking a different button in a confirmation dialog). After the operation completes, the element reverts to its standard value ("Renew selected").
I would like to test/verify that the temporary value is displayed, but cypress seems unable to read the value in time.
Application behaviour:
User clicks on the Renew confirmation dialog button Renew dialog is
hidden
Renew operation begins (api call)
The Renew Selected button (underneath the dialog) temporarily changes to contain the value "..."
Renew operation ends (api call returns 204)
The Renew Selected button changes to contain the standard value "Renew selected"
Test is:
cy.get("#loansConfirmRenewBtn") //the trigger button
.click()
cy.get(lib.loansRenewButton) //the element which changes as a response
.should("contains.text","...")
Expected: test passes, since the element does temporarily contain this value
Observed: test fails, with cypress reporting that the text within the element is the standard value
Is it possible to test rapidly changing values within cypress, or are some application behaviours too rapid to test?

Related

Robot Framework - Keyword to do in case the leadtime is not respected

I'm stuck on something in my Robot Framework project. When I hit a button from the 1st page after the login is done, another tab is opened, however, sometimes, the new window is not loaded and the code (robotframework) keep waiting for an answer.
To avoid getting an error when this situation happens, I want to solve it while the code still running, I want to know if there is any keyword that applies an action in case something is not done within a given lead time. In my case it would be to close the new window and repeat the previous step (hit the button from the first page again), therefore, it would be 2 actions in case the actions fails (lead time).
I have tried to use the keyword Run Keyword and Return Status, in my case the status would be false, however, since my code is kept waiting for an answer, the status is always True, therefore, it does not work for me.
I have read that there is a keyword named Run Keyword If Timeout Occurred however, it can be only used on Teardown`, therefore, I also don't know if it can applied.
I see you are trying to compare Boolean and String.
it's should be boolean and boolean run Keyword if ${status}==False.

Loading datastore based on boolean flag

I want to make a purchase order manager, where a queue is created from a database and then assembled into an accordion. Then, the user can look at requests, and then check the request when it is done. The task will then move to a "completed purchases" list.
I've been using a "notPurchased" datastore with the following server script:
query.filters.purchased._equals = false;
return query.run();
And then when the "submit" button is pressed, I call datastore.load();. However, this doesn't seem to refresh the purchase queue immediately. I have to completely refresh the page in order to see purchase request moved to 'completed'. How do I make this change instantaneous?
I figured out a solution that reduced any lag. Instead of filtering the database with a query, I bound the 'visibility' property to the proper boolean flag. Now items move instantly!

Why do my Firebase 'child_added' events show up out-of-order?

I have 2 "limit" queries on the same path. I first load a "limit(1)", and then later load a "limit(50)".
When I load the second query, the child_added events don't fire in-order. Instead, the last item in the list (the one returned by limit(1)) is fired first, and then all of the other items are fired in-order, like this:
**Limit 1:**
new Firebase(PATH).limit(1).on('child_added', ...)
Message 5
**Limit 2:**
new Firebase(PATH).limit(50).on('child_added', ...)
Message 5
Message 1
Message 2
Message 3
Message 4
I'm confused why "Message 5" is being called first in the second limit operation. Why is this happening, and how can I fix it?
I know this may seem strange, but this is actually the intended behavior.
In order to guarantee that local events can fire immediately without communicating with the server first, Firebase makes no guarantees that child_added events will always be called in sort order.
If this is confusing, think about it this way: If you had no internet connection at all, and you set up a limit(50), and then called push() on that reference, you would you expect an event to be fired immediately. When you reconnect to the server though, it may turn out that there were other items on the server before the one you pushed, which will then have their events triggered after the event for the one you added. In your example, the issue has to do with what data has been cached locally rather than something written by the local client, but the same principle applies.
For a more detailed example of why things need to work this way, imagine 2 clients, A and B:
While offline, Client A calls push() and sets some data
While online, Client B adds a child_added listener to read the messages
Client B then calls push(). The message it pushed triggers a child_added event right away locally.
Client A comes back online. Firebase syncs the data, and client B gets a child_added event fired for that data.
Now, note that even though the message Client A added comes first in the list (since it has an earlier timestamp), the event is fired second.
So as you see, you can't always rely on the order of events to reflect the correct sort order of Firebase children.
But don't worry, you can still get the behavior you want! If you want the data to show up in sort order rather than in the order the events arrived on your client, you have a couple of options:
1) (The naive approach) Use a "value" event instead of child_added, and redraw the entire list of items every time it fires using forEach. Value events only ever fire for complete sets of data, so forEach will always enumerate all of the events in order. There's a couple of downsides to this approach though: (1) value events won't fire until initial state is loaded from the server, so it won't work if the app is started in "offline mode" until a network connection can be established. (2) It inefficiently redraws everything for every change.
2) (The better approach) Use the prevChildName argument in the callback to on(). In addition to the snapshot, the callback for on() is passed the name of the previous child in in the query when items are placed in sort order. This allows you to render the children in the correct order, even if the events are fired out of order. See: https://www.firebase.com/docs/javascript/firebase/on.html
Note that prevChildName only gives the previous child in the query, not in the whole Firebase location. So the child at the beginning of the query will have a prevChildName of null, even if there is a child on the server that comes before it.
Our leaderboard example shows one way to manipulate the DOM to ensure things are rendered in the proper order. See:
https://www.firebase.com/tutorial/#example/leaderboard

Android Fragment state and setRetainInstance

Please excuse the long post. I was playing around with a simple app and wanted to save a custom object in a fragment across an orientation change. Previously within activities this used to be handled using the onRetainNonConfigurationInstance() / getLastNonConfigurationInstance() methods. Seeing as these methods are now deprecated, the documentation encourages the use of fragments and the setRetainInstance(boolean) method.
I went ahead and played around with this method and then noticed a strange difference in behaviour when it came to saving the state of the fragments across orientation change. First up, a very brief explanation of the app I was playing with:
Main Activity
Fragment A (First fragment shown on app launch)
This is a simple fragment with 3 EditText controls. Each one has an ID in the layout file. The fragment also includes a button which when selected replaces Fragment A with Fragment B and saves the transaction on the backstack.
Fragment B
This is a fragment with an empty layout. If back is pushed, Fragment A is restored from the backstack.
Scenarios
Scenario A - setRetainInstance(false):
App launches and fragment A is displayed.
I enter values into the EditText fields and select the button.
Fragment B is displayed. I change device orientation once and hit the back key.
Fragment A is displayed with the entered values (view state) intact.
Scenario A - setRetainInstance(true):
The same behaviour takes place as above
Scenario B - setRetainInstance(false):
App launches and fragment A is displayed.
I enter values into the EditText fields and select the button.
Fragment B is displayed. I change device orientation twice and hit the back key.
Fragment A is still displayed with the entered values (view state) intact.
Scenario B - setRetainInstance(true):
App launches and fragment A is displayed.
I enter values into the EditText fields and select the button.
Fragment B is displayed. I change device orientation twice and hit the back key.
Fragment A is displayed with empty EditText controls, i.e. none of the entered values (view state) still intact.
For some reason the use of setRetainInstance(true) interferes with the view state of fragment A (on the backstack) when the orientation changes more than once.
Possible Explanation
I started getting nervous about the use of setRetainInstance while not having a full understanding of what was going on, so I dug around in the support library source code to try figure it out. At a very high level, I think this may be what is going on with setRetainInstance(true):
Fragment A is displayed, button is pressed and Fragment A is replaced by Fragment B. As part of this process, the FragmentManager (FM) removes Fragment A and fragmentA.mRemoving flag is set to true.
Change orientation the first time. At this point the FM attempts to save all state of the fragments:
Parcelable saveAllState() {
...
if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
fs.mSavedFragmentState = saveFragmentBasicState(f);
Fragment A has a CREATED state and has a null saved state, so it qualifies to have its state saved.
The activity is destroyed as part of the orientation change. Long story short, Fragment A has its state changed to INITIALIZING.
The activity is recreated and an attempt is made to move the state of the fragments to CREATED. However, at this point there is a check in the FM moveToState() method:
if (f.mRemoving && newState > f.mState) {
// While removing a fragment, we can't change it to a higher state.
newState = f.mState;
}
Because fragmentA.mRemoving remains true from step 1 as the fragment was retained (not recreated), it does not have its state increased to CREATED but remains in the INITIALIZING state. Note that even if one presses the back key now, Fragment A will still have its state intact, as a result of its state being saved in step 2.
Change orientation for the 2nd time. Once again the FM attempts to save all state of the fragments:
Parcelable saveAllState() {
...
if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
fs.mSavedFragmentState = saveFragmentBasicState(f);
However, because Fragment A is in the INITIALIZING state it does not qualify to have its state saved. Hence, once orientation completes for the 2nd time, if the back key is pressed the state of Fragment A is no longer intact.
Questions
Is this behaviour expected? Perhaps this relates to the documentation discouraging the use of setRetainInstance and backstack fragments?
How should we deal with view state and the use of setRetainInstance? Perhaps my use case is incorrect, but I would be nervous using the setRetainInstance functionality with this difference in behaviour.
Once again, sorry for the long post. Feedback will be appreciated as always.

PyQt (or just QT). How to get QComboBox to fire a signal whenever it is set to a value (even if unchanged)

I am using PyQt4, but this is general enough that it could just apply to QT.
I have a series of QComboBoxes that I fill from left to right (i.e. selecting an item in the leftmost will populate the next one. Selecting an item in that one will populate the next, and so on)
I am having difficulty getting my signals to fire under all situations (i.e. regardless of whether the current index changes or not AND regardless of whether the item is set by the user or set programatically).
More detail:
I rely on the signals of the first QCombox to fire whenever an item is selected so that I can populate the next QCombobox in the gui. I then rely on THAT QCombobox to emit a signal so that I can populate the next one. And so on.
I want to pre-select an item in each QCombobox based on the user's last interaction with the gui.
I have a unique function per QCombobox that is responsible for populating and pre-selecting just that QCombobox. The code looks something like this:
comboBox1.blockSignals(True)
comboBox1.clear()
comboBox1.addItems(sorted(itemList))
comboBox1.blockSignals(False)
comboBox1.setCurrentIndex(intLastSavedState1)
where intLastSavedState1 is an integer that is derived from the text that was last selected by the user the last time they had used the app. I had hoped that the last line of this function would fire a signal that would cause the next combo box's function to load and pre-select an item (comboBox2). And that action would then cause the next comboBox's function to activate and it would cascade to the next and the next. But it is not working across all cases.
I have tried two versions of the signals:
self.connect(comboBox1, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.load_comboBox2)
and
self.connect(comboBox1, QtCore.SIGNAL("activated(const QString&)"), self.load_comboBox2)
In the first case, the signal will fire only if the intLastSavedState1 is different than whatever is currently selected in the combo box. This causes an issue if the user had last selected item 0 from that list. In this case QT does not recognize my script setting the the current index to 0 as being a change (since after loading the box it appears to think it is already on index 0), and so the signal does not fire.
In the second case, the signal will fire regardless of what is currently selected in the combo box... but only if activated by the user. It will not fire when my script tries to set the current index programatically.
These appear to be my only two options regarding the signals. So... is there another way of pre-selecting items in a QCombobox that will trigger a signal each and every time?
Well... sometimes just the act of asking a question can lead you to a (partial) answer.
I have a work-around but I am still interested in hearing if someone has a better idea.
I am now programatically setting the index of the QCombobox to -1 immediately after loading it up. Then, when I programatically set the actual index based on the user's history, it will always be considered a change (i.e. it will never be -1) and the signal will fire
using: currentIndexChanged(const QString&)
So my code looks like this now:
comboBox1.blockSignals(True)
comboBox1.clear()
comboBox1.addItems(sorted(itemList))
comboBox1.setCurrentIndex(-1)
comboBox1.blockSignals(False)
comboBox1.setCurrentIndex(intLastSavedState1)
and my signal looks like this:
self.connect(comboBox1, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.load_comboBox2)
This functions... does anyone have a better idea?
Thanks agian.
You can check current Index of your ComboBox and then either call your slot or do call setCurrentIndex().
Example:
if comboBox1.currentIndex() == index:
self.load_comboBox2(index)
else
comboBox1.setCurrentIndex(index)
This way you will not end up calling slot twice.

Resources