How to prevent cy.get from "snapshotting" a list before it's sorted - dom

Not the best title, but here's my challenge. I have a list on my page that can be sorted, and I want a Cypress test that checks that it works as expected. So imagine this test:
/* 1 */ cy.get('.list-item').eq(0).should('have.text', 'A');
/* 2 */ cy.get('.list-item').eq(-1).should('have.text', 'Z');
// Code that triggers sorting to change from asc to desc
/* 3 */ cy.get('.list-item').eq(0).should('have.text', 'Z');
/* 4 */ cy.get('.list-item').eq(-1).should('have.text', 'A');
Looks simple, but there's a slight delay when the sorting happens, so the UI isn't updated immediately.
I'm not sure if it's the cy.get or the eq function which causes it, but the problem is that line 3 "captures" the first element in the list, which is still 'A', and then tries to assert that the text is 'Z'. And when the list is reordered, this "captured" element doesn't actually change, it's just moved in the DOM, so the assertion still tries to assert that same element 'A', which in the DOM is actually the last element now, is 'Z', which it obviously isn't and shouldn't be.
If I insert a cy.wait(100) before 3, then it works as expected, but obviously I do not want to have a random wait in my test, so how do I solve this?
How do I check what the first and last elements are in a situation like this, when Cypress captures the DOM elements before they're reordered, without inserting an arbitrary wait? 😕
Actual case
Support app, showing a list of the 5 most recently viewed clients
Need to test that, when visiting for example number 3 in that list, it is moved to the top
The "code that triggers sorting" is actually a route navigation event:
There's a listener (React useEffect hook) on route changes.
When route changes, it updates the list of recently viewed client ids, which is stored in local storage.
When the list in local storage changes, the component showing the list first waits 750ms (so it's less confusing for user, but turned down to 10ms in Cypress tests), then updates (re-sorts) the list.
And since the list only contains ids, each tile will then async load the name and some more stuff to display on the list item.
So... the delay is actually more than just a UI update. There's routing, local storage and async requests involved too. 🎉

You need to merge cy.get(...) and .eq into a single selector to make sure it retries the assertion .should('have.text', 'Z') after resorting. Read about it https://on.cypress.io/retry-ability#Merging-queries - right now it grabs the list and then only retries .eq() command, which is too late. You could also rewrite your code to get the first and last elements using single .should(cb) https://on.cypress.io/should#Function - the example in the docs really fits your use case.

I am taking a wild shot here, but perhaps in the trigger to sort you could use then.
cy.get('.list-item').eq(0).should('have.text', 'A');
cy.get('.list-item').eq(-1).should('have.text', 'Z');
// Code that triggers sorting to change from asc to desc
cy.get("#id-to-sort").click().then(()=>{
cy.get('.list-item').eq(0).should('have.text', 'Z');
cy.get('.list-item').eq(-1).should('have.text', 'A');
})

Related

How can I get all Actions for a Board using Trello's Rest API?

I want to get all the actions for a board, but Trello limits the results to 1000. I understand the correct way to deal with this is to paginate. I've tried both before and page as keywords:
Basic Call:
https://api.trello.com/1/board/[boardID]/
?key=[key]&token=[token]
&actions=commentCard&actions_limit=1000
Alternatives:
Before:
https://api.trello.com/1/board/[boardID]/
?key=[key]&token=[token]
&actions=commentCard&actions_limit=1000&
before=[oldest_returned_action's_date]
Page:
https://api.trello.com/1/board/[boardID]/
?key=[key]&token=[token]
&actions=commentCard&actions_limit=1000&
page=[page_number]
The result never varies --- I always get back [limit] number of actions, and they're always the same no matter the call. I checked the dates in what was returned and they certainly don't respect the before parameter. I even tried lowering the limit to make sure I wasn't trying to return more than I possessed. The problem persists.
How can I correctly get all actions for a Trello board?
Actions are in reverse chronological order (newest-to-oldest), so to page through the actions on a board, you would use something like:
curl "https://api.trello.com/1/boards/${BOARD_ID}/actions/?key=${TRELLO_API_KEY}&token=${TRELLO_TOKEN}&limit=1000"
then, from the last element of the array returned by the the above, select the date or id and pass that as the before parameter in the next call, e.g.:
curl "https://api.trello.com/1/boards/${BOARD_ID}/actions/?key=${TRELLO_API_KEY}&token=${TRELLO_TOKEN}&limit=1000&before=${DATE_OR_ID_OF_LAST_ACTION}"
and repeat, passing in either the id or date of the last action as the subsequent before parameter.
References:
Paging
Board Actions
Actions Nested Resource

TYPO3 DataHandler: Copying record and relate it to itself --> running into loop

I'm developing an event extension with recurring dates. Therefor, I have a recurring date pattern and copy the record for each date. So the record has a relation to itself:
- Main Event
-- N Child Events
Currently, I use the DataHandler method copyRecord, which works perfect. But this just copies the record, without mapping the relation.
- Main Event --> should have the count of children in the database
-- N Child Events --> should habe the relation to its parent main event
The DB should look like:
Event 1 (Main Event) | uid: 1 | event: 0 | recurring_children: 3 (count)
Event copy 1 | uid: 2 |  event: 1 | recurring_children: 0
Event copy 2 | uid: 3 |  event: 1 | recurring_children: 0
Event copy 3 | uid: 4 |  event: 1 | recurring_children: 0
I tried several ways, but none without problems.
The following try sets the relation in the database, but creates more events (I guess, this loops and I have to build a condition to avoid filling the datamap with duplications):
public function processDatamap_afterDatabaseOperations(
$status,
$table,
$recordUid,
array $fields,
\TYPO3\CMS\Core\DataHandling\DataHandler $parentObject){
$event = BackendUtility::getRecord($table, $recordUid);
if ($status === 'update') {
/** #var \TYPO3\CMS\Core\DataHandling\DataHandler $tce */
$tce = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
$tce->start(array(), array());
$overrides = [
'is_recurring_parent' => 0,
'is_recurring_child' => 1,
'recurring_weekdays' => '',
'recurring_period_end' => '0000-00-00',
'event' => $recordUid,
'title' => 'FOOBAR'
];
if ($event['is_recurring_parent']) {
$foobar = $tce->copyRecord('tx_technoseumevents_domain_model_event', $event['uid'], $event['pid'], false, $overrides);
}
T3 version 8.7
Sorry, my first time I'm using the datahandler for complex tasks. Maybe someone has a hint for me...
The recursiveness happening in the first place, indicates a problem with your architecture that might be worth addressing before this problem as it would solve it at the root cause instead of treating symptoms. Normally, a record should not relate to itself - but that said, recursiveness can happen in other ways and it might not be possible to avoid in your use case.
Advise for addressing the problem:
Consider adding a condition that prevents your code from being called on tables other than the one table you need to operate on. This alone could actually be why your copies recurse so that's number one to handle.
If possible, and if you are not concerned with short-circuiting things like changing the title TCA field for a record to prepend "Copy of", or causing the copy to be hidden, you can switch to copyRecord_raw. It takes slightly different arguments (you may have to refactor to pass some arguments as part of the overrides array, compared to calling copyRecord). Doing that will prevent hooks from being called on the children you copy.
You are using a nested DataHandler instance - it might be preferable to call that method on the $dataHandler instance in your example. The DataHandler maintains a copy stack, but (parts of) it will only work if you do things in the same instance and only for some particular hooks (i.e. the pre-type and not-global hooks).
Lastly, there exists a runtime cache entry which contains an array of records which have been copied. Although the utility methods that access and store entries in this cache entry are not publicly accessible - so you cannot call them from your hook class - you can read the array of entries, manipulate it and put it back to prevent recursive processing of the same record. See the method isNestedElementCallRegistered in DataHandler (https://github.com/TYPO3/TYPO3.CMS/blob/v8.7.17/typo3/sysext/core/Classes/DataHandling/DataHandler.php#L9034).
Final note: the after-database-operations hook may be called at times when you do not expect it. For example, if you copy a record and also move it (as in: the copy comes from the UI, not programmatically) the array you receive in $fieldArray may not be the final one (for example, pid may be an unexpected value). Not being aware of this and the peculiarities above might also increase the danger of unintentionally causing recursive operations.
EDIT: If you are using bi-directional relations in TCA then removing one side may also improve the situation. For example, each "date" doesn't necessarily have to be aware of which event it is associated with. Something to consider.

MS Access - me.recordset not passing to sub

I have a form that loads a single record. The user does what they need to do on the form...in this case, they enter a date, and a button becomes available to click to advance the record to the next step in the process.
I have a public function that is logging the activity to tblActivity, and sets the record's new Status and Location. This Function takes 3 variables, and was working fine until today.
'I'm calling the function with this line from the button's Click event
LogActivity 15, Screen.ActiveForm, Me.Recordset
Public Function LogActivity(ByVal lSID As Long, Optional fForm As Form, Optional ByRef fRS As Recordset)
With fRS
Do Until .EOF
Debug.Print .Fields(5)
.MoveNext
Loop
End With
...
End Function
This should be printing the form's Status value, but fRS is passed in with no values. The form's recordset has values prior to being passed as the form has data. Some how it is getting lost in the pass. This was working fine, I have multiple buttons across 5 different forms that all call this same Function. Suddenly today, none of them can pass the recordset. I can think of nothing that was changed that would effect this. Most of the changes recently involved locking down fields and the appearance of buttons at the right time...nothing related to the recordset.
Naturally, this DB is supposed to go live on Monday.
Found the problem.
I had a backup from yesterday that was working fine.
One by one, I went through the changes I logged from yesterday and found that by changing some fields to .enabled = False and .locked = True is what was doing it. Apparently that was enough to clear all the values when passing.
Left the fields enabled, just locked them and it passes all values correctly.
Even though this was a failure on my part, I'll leave this up in case some one else makes the same mistake I made.
**** Update ****
I also found out that if I did a
fRS.movelast
fRS.movefirst
before anything else, it found the data. Not sure why it started happening, but these two things seem to have fixed it completely.

Cannot update one field at a time with VSTO for Word

When fields are nested, there is a problem.
foreach (Word.Field field in this.Application.ActiveDocument.Fields)
{
field.Update();
text = field.Result.Text;
}
The above code does not work.
The process starts, but winds up in an endless loop or some other process that hangs the system.
Thinking about it, I can surmise that when you update a field, it might have an effect on the fields collection - thus, the loop fails.
Does anyone have any ideas on implementing this?
P.S. I know there is a Document.UpdateFields() method to update ALL fields. However, there are reasons why I cannot use this and need to only update specific field types.
My apologies! I was going to give an example of a nested field but was trying to test some more before sending anyone (Jack) on a goose-chase.
I waited and waited and waited, and after a good 2 or 3 minutes, it finished. After the last field, it crashed with this message:
Object has been deleted.
The error was generated from the following line inside the loop:
string text = field.Code.Text;
The template is being tested on mergefields that are not being found because I am testing without database connectivity. It would be odd, but explainable, that it goes through all the fields and then, at the end of the day, the very OUTER IF field's result is "Error! Reference source not found." But I still don't get why this could happen.
Nor do I understand why looping takes 3 minutes while a call to document.Fields.Update() will do the same thing in about 1 second and NOT result in the error described above.
Again, my apologies. I never considered updating inside a loop would be vastly slower that a call to doc.fields.update().

Manipulating form input values after submission causes multiple instances

I'm building a form with Yii that updates two models at once.
The form takes the inputs for each model as $modelA and $modelB and then handles them separately as described here http://www.yiiframework.com/wiki/19/how-to-use-a-single-form-to-collect-data-for-two-or-more-models/
This is all good. The difference I have to the example is that $modelA (documents) has to be saved and its ID retrieved and then $modelB has to be saved including the ID from $model A as they are related.
There's an additional twist that $modelB has a file which needs to be saved.
My action code is as follows:
if(isset($_POST['Documents'], $_POST['DocumentVersions']))
{
$modelA->attributes=$_POST['Documents'];
$modelB->attributes=$_POST['DocumentVersions'];
$valid=$modelA->validate();
$valid=$modelB->validate() && $valid;
if($valid)
{
$modelA->save(false); // don't validate as we validated above.
$newdoc = $modelA->primaryKey; // get the ID of the document just created
$modelB->document_id = $newdoc; // set the Document_id of the DocumentVersions to be $newdoc
// todo: set the filename to some long hash
$modelB->file=CUploadedFile::getInstance($modelB,'file');
// finish set filename
$modelB->save(false);
if($modelB->save()) {
$modelB->file->saveAs(Yii::getPathOfAlias('webroot').'/uploads/'.$modelB->file);
}
$this->redirect(array('projects/myprojects','id'=>$_POST['project_id']));
}
}
ELSE {
$this->render('create',array(
'modelA'=>$modelA,
'modelB'=>$modelB,
'parent'=>$id,
'userid'=>$userid,
'categories'=>$categoriesList
));
}
You can see that I push the new values for 'file' and 'document_id' into $modelB. What this all works no problem, but... each time I push one of these values into $modelB I seem to get an new instance of $modelA. So the net result, I get 3 new documents, and 1 new version. The new version is all linked up correctly, but the other two documents are just straight duplicates.
I've tested removing the $modelB update steps, and sure enough, for each one removed a copy of $modelA is removed (or at least the resulting database entry).
I've no idea how to prevent this.
UPDATE....
As I put in a comment below, further testing shows the number of instances of $modelA depends on how many times the form has been submitted. Even if other pages/views are accessed in the meantime, if the form is resubmitted within a short period of time, each time I get an extra entry in the database. If this was due to some form of persistence, then I'd expect to get an extra copy of the PREVIOUS model, not multiples of the current one. So I suspect something in the way its saving, like there is some counter that's incrementing, but I've no idea where to look for this, or how to zero it each time.
Some help would be much appreciated.
thanks
JMB
OK, I had Ajax validation set to true. This was calling the create action and inserting entries. I don't fully get this, or how I could use ajax validation if I really wanted to without this effect, but... at least the two model insert with relationship works.
Thanks for the comments.
cheers
JMB