How to take text from elements to use it later in cypress? - automated-tests

I need to take 2 lists of text values on page to use each of them in different cheking.
I do it by xpath
cy.xpath('//tbody/tr[position()<10]/td[6]/span').then(items => {
cy.wrap(items).as('multipleList')
})
cy.xpath('//tbody/tr[position()<21][position()>15]/td[6]/span').then(items => {
cy.wrap(items).as('commaList')
})
cy.get('#multipleList').each((qwer, index) => {..........})
cy.get('#commaList').each((qwer, index) => {..........})
But after using the first list (#multipleList), it shows error that the second list (#commaList) is empty.
If I swap them, then #commaList is being executed OK, but #multipleList shows the same error.

Try a combined selection and iteration only once
cy.xpath('//tbody/tr').as('list')
cy.get('#list').each(($rows, rowIndex) => {
if (rowIndex < 9) {
const $span = $rows[index].find('td:eq(6) span')
...
}
if (rowIndex > 14 && rowIndex < 20) {
const $span = $rows[index].find('td:eq(6) span')
...
}
})

With the test as you've shown, it's not possible.
If you have an error it's the processing in .each() causing page changes (2nd list becomes invalid).
Try to reorder the commands
cy.xpath('//tbody/tr[position()<10]/td[6]/span').as('multipleList')
cy.get('#multipleList').each((qwer, index) => {..........})
cy.xpath('//tbody/tr[position()<21][position()>15]/td[6]/span').as('commaList')
cy.get('#commaList').each((qwer, index) => {..........})

Related

Retrieve and compare the style attribute of an element periodically using using cypress

I have a time indicator that travels over a timescale, the indicator's style attribute value keeps on changing for every x milliseconds and I need to get, store and compare that the previously captured value is greater than the latest value.
Initial value:
Latest value:
The logic is, from one point (left 10), every second it moves to the left (left -0, -1, -2, -3 ...)
I tried few ways and one of them is to capture in the same 'cy.then', but in that case, the element will not have the recent value. So far, I tried this. it fetches the value and with some help of regex, I got a 'comparable' value but how I can store/compare those values? Additionally, what is the best way if we need to compare more than 2 values?
const BTN_CONTROL_TIMEINDICATOR = '#currentTimeIndicator'
static verifyTimeLapse() {
//wip
var initialVal, nextVal
initialVal = this.getAnyValueOfAnElement(BTN_CONTROL_TIMEINDICATOR)
cy.wait(500)
nextVal = this.getAnyValueOfAnElement(BTN_CONTROL_TIMEINDICATOR)
cy.log(initialVal > nextVal)
}
static getAnyValueOfAnElement(element) {
//wip
cy.get(element)
.then(($ele) => {
const val=$ele.attr('style').replace(/[^\d.-]/g, '')
cy.log(val)
// return does not work
})
}
cy.log:
Page objects don't work very well with the Cypress command queue, here's what you might do with custom commands.
/* Get the numeric value of CSS left in px */
Cypress.Commands.add('getTimescaleValue', () => {
cy.get('#currentTimeIndicator')
.then($el => +$el[0].style.left.replace('px',''))
})
/* Get a sequence of time scale values */
Cypress.Commands.add('getTimescaleValues', ({numValues, waitBetween}) => {
const values = [];
Cypress._.times(numValues, () => { // repeat inner commands n times
cy.getTimescaleValue()
.then(value => values.push(value)) // save value
.wait(waitBetween)
})
return cy.wrap(values);
})
/* Assert a sequence of values are in descending order */
Cypress.Commands.add('valuesAreDescending', { prevSubject: true }, (values) => {
values.reduce((prev, current) => {
if (prev) { // skip first (no prev to compare)
expect(prev).to.be.gt(current) // assert pairs of values
}
return current
});
})
it('check the timeline', () => {
cy.getTimescaleValues({ numValues: 10, waitBetween: 100 })
.valuesAreDescending()
})
Log
assert
expected 63 to be above 58
assert
expected 58 to be above 48
assert
expected 48 to be above 43
assert
expected 43 to be above 33
assert
expected 33 to be above 23
assert
expected 23 to be above 18
assert
expected 18 to be above 13
assert
expected 13 to be above 3
assert
expected 3 to be above -2
Tested with
<div id="currentTimeIndicator" style="left:63px">Target</div>
<script>
const timer = setInterval(() => {
const div = document.querySelector('#currentTimeIndicator')
const left = +div.style.left.replace('px', '');
if (left < 0) {
clearInterval(timer)
return
}
const next = (left - 5) + 'px';
div.style.left = next;
}, 100)
</script>
If your app uses setInterval() for timing, you should be able to use cy.clock() and cy.tick() instead of .wait(waitBetween) to get more precise sampling and faster test execution.
I don't know where the initial value comes from. But before it changes, maybe on page load, maybe as first job on click, etc you can do something like this:
let item = document.querySelector("#currentTimeIndicator");
item.dataset.left = parseFloat(item.style.left);
console.log(item);
<div id="currentTimeIndicator" style="left:-20px"></div>

refer doc id using where condition in snapshotChanges()

I am fetching data using snapshotChanges(). I want to add where condition to it and fetch data of only 1 doc from collection.
Below is my code:
this.firestore.collection('notifications' , ref => ref
.where(document id , "==" , "1") // giving error
)
.snapshotChanges()
.subscribe(response => {
if(!response.length){
console.log("no data available");
return false;
}
this.notifyarr = [];
for(let item of response){
this.notifyarr.push(item.payload.doc.data());
console.log(this.notifyarr);
this.notification_length = this.notifyarr.length;
console.log(this.notification_length);
}
})
This line of code is giving error .where(document , "==" , "1") // giving error
How do we refer a document using .snapshotChanges() ?
If you want to query to check a documentid is equal to the value then you should do the following:
where(firebase.firestore.FieldPath.documentId(), "==" , "1")
If you know you only want a single document using its ID, why don't you just build a AngularFirstoreDocument to the desired document and call snapshotChanges on that reference?
this.firestore.collection('notifications').doc(id).snapshotChanges()...
No need to bother building a query that only gives you a single document.

How to access final element in Handlebars.js array?

Given the following data structure:
{
"name" : "test",
"assetType" : "Home",
"value" : [
{
"value" : "999",
"date" : "10/03/2018"
},
{
"value" : "1234.56",
"date" : "10/04/2018"
}
]
}
How can I directly access the first and last element of value in Handlebars? My code is:
<td>{{value.[0].value}}</td>
<td>{{value.[value.length - 1].value}}</td>
The first statement works fine, but I cannot figure out how to access the last element. value.length inside the []'s does not return any value, but rendering value.length outside of the array works fine.
I tried using #first and #last in the array but those don't seem to work either.
As already pointed out, you can easily solve this with a template helper:
Template.myTemplate.helpers({
first (arr) {
return arr && arr[0]
},
last (arr) {
return arr && arr[arr.length - 1]
},
atIndex (arr, index) {
return arr && index >= 0 && index <= arr.length - 1 && arr[index]
}
})
You can then create a context using #with where the context object is the result of the helpers. Inside this context you can access properties using this.
Accessing values with the name value would look like the following:
<td>{{#with first value}}{{this.value}}{{/with}}</td>
<td>{{#with last value}}{{this.value}}{{/with}}</td>
<td>{{#with atIndex value 5}}{{this.value}}{{/with}}</td>

ES2015 - updating filtered items in a collection and combining updating the previous collection

So say i have a collection:
const collection = [0,3,2,4];
var r = collection.filter((item) => {
return item > 2;
}).map((item) => {
return item + 2;
});
// I could just `.map()` them back in but that seems ineffective
console.log(r); // 5,6
Is there a functional (fp) way to compose this code to update my previous collection to get the expected result (given the index of the result is important) ?
e.g. expected result: [0,5,2,6]
How about
var r = collection.map(item => item > 2 ? item + 2 : item)
instead?
I.e. .map all items but only modify the ones that match your condition.

Telerik Extensions for ASP.NET MVC - GRID - randomly sorted items inside group in Chrome when GridOperationMode.Client

Data source contains many records, but each 12 records represent 12 characteristic of 1 entity sorted in fixed order. Then rows are grouped by 3 columns (by 'AccountName', 'OportunityName' and 'OpportunityId'), and the group on deepest level contains those 12 characteristic. All worked OK when 'GridOperationMode.Server' was used:
But to increase performance we decided to change operation mode to client - 'GridOperationMode.Client'. After that performance became better, but those 12 characteristic lost they sorting in Chrome - for each group they are rendered in random order. I checked the issue in IE and FF - and found that they don't have such a problem. Any ideas how to fix the wrong order in chrome?
Wrong order in Chrome when GridOperationMode.Client is used
JS (shortened) - binds grid:
function populateForecastClosedGrid(controllerActionUrl) {
var gridForecastClosed = $("#gridFORECASTREPORT").data("tGrid");
var accountId = $('#accountsFilterCombo').data('tComboBox').value();
gridForecastClosed.ajax.selectUrl = controllerActionUrl + '?checkDate=' + new Date().formatMMDDYYYY() + '&accountId=' + accountId;
gridForecastClosed.showBusy();
$.post(gridForecastClosed.ajax.selectUrl, function (data) {
gridForecastClosed.dataSource.data([]);;
gridForecastClosed.dataBind(data);
});
}
Grid (shortened):
#(Html.Telerik().Grid()
.Name("gridFORECASTREPORT")
.Columns(columns => { ... }
.DataKeys(keys => keys.Add(c => c.OpportunityId))
.DataBinding(dataBinding => dataBinding.Ajax().OperationMode(GridOperationMode.Client))
.Groupable(grouping => grouping.Groups(groups =>
{
groups.Add(c => c.AccountName);
groups.Add(c => c.OpportunityName);
groups.Add(c => c.OpportunityId);
}).Visible(false))
.EnableCustomBinding(true)
.Pageable(p => p.PageSize(396)))
After a lot of researching I decided to implement sorting by myself using JS.
Works fast for page size equals 396 my grid uses, of course can be made faster. Each from those linked 12 items already has a field SortOrder with correct order inside this 12-items group. Quick and dirty, enjoy! If you know better solution please share. So far marked as answered. Really working solution, approved by my TL as no other ways found, can be adapted to any other grid.
function onGridForecastClosedDataBound() {
var grid = $(this).data('tGrid');
// Request came to increase Forecast (Closed) grid performance. The only way (w/o touching SQL)
// I found is to change grid operation mode from Server to GridOperationMode.Client (~50% increase).
// But Telerik Grid + Chrome (OK on IE, FF) has a problem - wrong sorted items inside group
// when grouping is performed on client side. This is a quick and dirty workaround for this
// particular grid - to perform "sorting" manually using JS.
// IMPORTANT! Pay attention, that if you change number of rows per
// opportunity (currently 12) then the grid will be broken w/o changing the code below.
if ('#Request.Browser.Browser' == 'Chrome') {
var numberOfRowsPerOpportunity = 12;
var rows = grid.$tbody.find('tr');
var rowsSorted = [];
while (rows.length > 0) {
var partGroups = rows.splice(0, rows.slice(0, grid.groups.length).filter('.t-grouping-row').length);
var partRows = rows.splice(0, numberOfRowsPerOpportunity);
partRows.sort(function (a, b) {
var sortOrderA = parseInt($(a).find('td.sort-order').text());
var sortOrderB = parseInt($(b).find('td.sort-order').text());
return sortOrderA - sortOrderB;
});
$.each(partRows, function (index, item) {
$(item).removeClass('t-alt');
if (index % 2 != 0) $(item).addClass('t-alt');
});
$.merge(rowsSorted, partGroups);
$.merge(rowsSorted, partRows);
}
rows.remove();
grid.$tbody.append(rowsSorted);
}
grid.hideBusy();
}
function populateForecastClosedGrid(controllerActionUrl) {
var gridForecastClosed = $("#gridFORECASTREPORT").data("tGrid");
var accountId = $('#accountsFilterCombo').data('tComboBox').value();
gridForecastClosed.ajax.selectUrl = controllerActionUrl + '?checkDate=' + new Date().formatMMDDYYYY() + '&accountId=' + accountId;
gridForecastClosed.showBusy();
gridForecastClosed.dataSource.data([]);
gridForecastClosed.ajaxRequest();
}
Grid (shortened):
#(Html.Telerik().Grid<mForecastReport>()
.Name("gridFORECASTREPORT")
.DataKeys(keys => keys.Add(c => c.OpportunityId))
.ClientEvents(e => e.OnDataBound("onGridForecastClosedDataBound"))
.DataBinding(dataBinding => dataBinding.Ajax().OperationMode(GridOperationMode.Client))
.Columns(columns => {
...
columns.Bound(c => c.SortOrder).Hidden(true).HtmlAttributes(new { #class = "sort-order" });
}
.Groupable(grouping => grouping.Groups(groups =>
{
groups.Add(c => c.AccountName);
groups.Add(c => c.OpportunityName);
groups.Add(c => c.OpportunityId);
}).Visible(false))
.Pageable(p => p.PageSize(396)))

Resources