Assertion on sorting a table in cypress - javascript

I am trying some UI tests on Cypress and would like to continue this discussion mentioned here in SO.
I have a table like this but would like to sort based on column say A or B.
I followed the solution mentioned in the link but getting assertion failure - expected [ Array(3) ] to deeply equal [ Array(3) ]
function getCellTextAsArray(){
let cellContents = []
return new Cypress.Promise(resolve => {
cy.get('#datatable-tabletools').find('tbody').children()
.each(($el, $index) => {
//some logic to select the elements you want
//like $index % 4 == 0
if($index>=0) {
cellContents.push($el.text())
}
}).debug()
.then(() => resolve(cellContents))
})
}
and then call this function as
getCellTextAsArray()
.then(cellContents => {
let actual = cellContents.slice()
cy.wrap(actual)
.should('deep.eq', cellContents.sort())})
Apologies, I am new to javascript.

cy.get('#example>tbody > tr > :nth-child(1)') // this is the table column 'A'
.then(function(A)
{
const age_values= A
.toArray()
.map($e1=> parseInt($e1.textContent))// to integer from constant
expect(age_values).to.be.sorted({descending:true})//use chai-assertion

Related

Filtering an array based on presence of string value

I know filter questions are covered extensivly on SO - but I'm struggling to implement my idea:
I want to filter through my panels & return an array filteredPanelTabItems for any that include the layouthint: "tab-view-item" and then the remainder of the array (without "tab-view-item") to another const so I can use it elsewhere, can anyone guide where I'm going wrong?
The screenshot above shows what's happening in the console when I log:
panel.panelLinks.links
const hasTabItemViewHint() => {
//check for string
}
const filteredPanelTabItems = panel.panelLinks.links.filter<Node>(
(panelLink) => panelLink.(call hasTabItemViewHint function?)
);
Consider something like this.
var filteredPanelTabItems = $.map(panel.panelLinks.links, function(l, i) {
if (l.LinkModal.layouthint._collections.indexOf("tab-view-item") >= 0) {
return l;
}
});
See more: https://api.jquery.com/jquery.map/

Javascript - Add Item in the middle of an array inside map function

I'm trying to add an item in a specific index inside an array inside a map function and it's been behaving unexpectedly. Here's the code for it
const addItemToLevelTwoArray= (uniqueID, arrayID )=> {
const reportObject = {
id:arrayID,
title:'',
}
data.map(section=>{
section.content.map((report, reportIndex)=>{
if(report.id===uniqueID){
section.content.splice(reportIndex, 0, reportObject);
}
return report;
})
return section;
})
}
Here's a working pen - https://codepen.io/raufabr/pen/vYZYgOV?editors=0011
Expected behaviour is that it would insert an object in the specific index, right above the object where the ID matches.
However, it's acting weirdly and sometimes I'm getting 2 items being added instead of one.
Any tip on what I'm doing would be massively appreciated! I know I'm close but I've been stuck on this for a while now and can't figure out what I'm doing wrong!
Preface: You're using map incorrectly. If you're not using the array that map builds and returns, there's no reason to use it; just use a loop or forEach. More in my post here. And one reason to use an old-fashioned for loop is that you're in control of iteration, which matters because...
However, it's acting weirdly and sometimes I'm getting 2 items being added instead of one.
That's because you're inserting into the array being looped by the map, so on the next pass, it picks up the entry you're adding.
If you do a simple loop, you can easily avoid that by incrementing the index when you insert, or by looping backward; here's the looping backward approach:
const addItemToLevelTwoArray = (uniqueID, arrayID) => {
const reportObject = {
id: arrayID,
title: "",
};
for (const section of data) {
for (let reportIndex = section.content.length - 1; reportIndex >= 0; --reportIndex) {
const report = section.content[reportIndex];
if (report.id === uniqueID) {
section.content.splice(reportIndex, 0, reportObject);
}
}
}
};
Because we're looping backward, we won't pick up the entry we just added on the next pass.
Since the outer loop doesn't have that problem, I used the more convenient for-of.
Since you asked about map, if you do use the array map returns, you can do this by returning an array with the two entries, and then calling flat on the array map builds. (This only works if the array doesn't already contain arrays, because they'll get flattened to.) This is common enough that it's combined in one function: flatMap. It's not what I'd do (I'd do a loop), but it's certainly feasible. Sticking with forEach and flatMap rather than using for-of and for:
const addItemToLevelTwoArray = (uniqueID, arrayID) => {
const reportObject = {
id: arrayID,
title: "",
}
data.forEach(section => {
section.content = section.content.flatMap(report => {
if (report.id === uniqueID) {
// Return the new one and the old one
return [reportObject, report];
}
// Return just the old one
return report;
});
});
};
That assumes it's okay to modify the section object. If it isn't, Alberto Sinigaglia's answer shows creating a new replacement object instead, which is handy in some sitautions.
You can just use flatMap:
const data = [
{
content: [
{
id: 1,
title: "a"
},{
id: 3,
title: "c"
},
]
}
]
const addItemToLevelTwoArray= (uniqueID, arrayID )=> {
const reportObject = {
id:arrayID,
title:'',
}
return data.map(section=> {
return {
...section,
content: section.content.flatMap( report =>
report.id === uniqueID
? [reportObject, report]
: report
)
}
}
)
}
console.log(addItemToLevelTwoArray(3, 2))
The following will extend the inner array .contentwithout modifying the original array data:
const data = [ {id: 0,title:'main',content:[{id:1,title:'Hello'},
{id:2,title:"World"}] } ];
const addItemToLevelTwoArray= (uniqueID, arrayID )=> {
const reportObject = {
id:arrayID,
title:'something new!',
}
return data.map(d=>(
{...d, content:d.content.reduce((acc, rep)=>{
if(rep.id===uniqueID) acc.push(reportObject);
acc.push(rep)
return acc;
},[]) // end of .reduce()
})); // end of .map()
}
const res=addItemToLevelTwoArray(1,123);
console.log(res);

How to sort array by object property with multiple conditions in Javascript

I've been reading similar solved problems but couldn't understand how to apply it in my case.
So, I have an unsorted array of objects:
const queues = [
{"floor":2,"direction":"REQUEST_FLOOR"},
{"floor":1,"direction":"REQUEST_FLOOR"},
{"floor":5,"direction":"CALL_DOWN"},
{"floor":8,"direction":"CALL_DOWN"},
{"floor":7,"direction":"REQUEST_FLOOR"},
{"floor":6,"direction":"CALL_DOWN"}
];
I'd like to sort it into:
const queues = [
{"floor":1,"direction":"REQUEST_FLOOR"},
{"floor":2,"direction":"REQUEST_FLOOR"},
{"floor":7,"direction":"REQUEST_FLOOR"},
{"floor":8,"direction":"CALL_DOWN"},
{"floor":6,"direction":"CALL_DOWN"},
{"floor":5,"direction":"CALL_DOWN"},
];
So, the priority rules I want to apply are :
Sort "REQUEST_FLOOR" first then only "CALL_DOWN".
Sort "REQUEST_FLOOR" in ascending and "CALL_DOWN" in descending.
I'm able to make it work by separating it into 2 different arrays, sort, then combine it at the end. Here is my code:
const requestFloors = queues.filter(queue => queue.direction === "REQUEST_FLOOR");
const callDownFloors = queues.filter(queue => queue.direction === "CALL_DOWN");
requestFloors.sort((a,b) => a.floor - b.floor);
callDownFloors.sort((a,b) => b.floor - a.floor);
const newQueues = [...requestFloors,...callDownFloors];
Question: Instead of filtering + sorting, I'd like to just use sort(), so maybe something like this:
queues.sort((a,b) => b.direction.localeCompare(a.direction) || a.floor - b.floor );
However, this code sort the the direction correctly, but the problem is it still sorts the floor in ascending for both direction.
How do I achieve the same result by only using sort()? Any helps will be greatly appreciated.
You can use the sort function:
queues.sort((el1, el2) => {
if(el1.direction == el2.direction){
if(el1.direction === "REQUEST_FLOOR")
return el1.floor - el2.floor
else
return el2.floor - el1.floor
}
else if(el1.direction === "REQUEST_FLOOR")
return -1;
else
return 1;
})
Output:
[
{
"floor":1,
"direction":"REQUEST_FLOOR"
},
{
"floor":2,
"direction":"REQUEST_FLOOR"
},
{
"floor":7,
"direction":"REQUEST_FLOOR"
},
{
"floor":8,
"direction":"CALL_DOWN"
},
{
"floor":6,
"direction":"CALL_DOWN"
},
{
"floor":5,
"direction":"CALL_DOWN"
}
]
If the direction is the same in both compared items, you can conditionally choose the evaluation of the other columns based on the direction:
queues.sort((a,b) =>
(a.direction=="CALL_DOWN") - (b.direction=="CALL_DOWN") ||
(a.direction == "CALL_DOWN"
? b.floor - a.floor
: a.floor - b.floor)
);

Javascript's method forEach() creates array with undefined keys

I am building a simple todo app, and I'm trying to get the assigned users for each task. But let's say that in my database, for some reason, the tasks id starts at 80, instead of starting at 1, and I have 5 tasks in total.
I wrote the following code to get the relationship between user and task, so I would expect that at the end it should return an array containing 5 keys, each key containing an array with the assigned users id to the specific task.
Problem is that I get an array with 85 keys in total, and the first 80 keys are undefined.
I've tried using .map() instead of .forEach() but I get the same result.
let assignedUsers = new Array();
this.taskLists.forEach(taskList => {
taskList.tasks.forEach(task => {
let taskId = task.id;
assignedUsers[taskId] = [];
task.users.forEach(user => {
if(taskId == user.pivot.task_id) {
assignedUsers[taskId].push(user.pivot.user_id);
}
});
});
});
return assignedUsers;
I assume the issue is at this line, but I don't understand why...
assignedUsers[taskId] = [];
I managed to filter and remove the empty keys from the array using the line below:
assignedUsers = assignedUsers.filter(e => e);
Still, I want to understand why this is happening and if there's any way I could avoid it from happening.
Looking forward to your comments!
If your taskId is not a Number or autoconvertable to a Number, you have to use a Object. assignedUsers = {};
This should work as you want it to. It also uses more of JS features for the sake of readability.
return this.taskLists.reduce((acc, taskList) => {
taskList.tasks.forEach(task => {
const taskId = task.id;
acc[taskId] = task.users.filter(user => taskId == user.pivot.task_id);
});
return acc;
}, []);
But you would probably want to use an object as the array would have "holes" between 0 and all unused indexes.
Your keys are task.id, so if there are undefined keys they must be from an undefined task id. Just skip if task id is falsey. If you expect the task id to possibly be 0, you can make a more specific check for typeof taskId === undefined
this.taskLists.forEach(taskList => {
taskList.tasks.forEach(task => {
let taskId = task.id;
// Skip this task if it doesn't have a defined id
if(!taskId) return;
assignedUsers[taskId] = [];
task.users.forEach(user => {
if(taskId == user.pivot.task_id) {
assignedUsers[taskId].push(user.pivot.user_id);
}
});
});
});

JS: How to make logic (using loops) to insert data into this table?

Fiddle here. But it does not show anything. I couldn't figure out why.
The structure of a 2D array is like:
Array ( [0] => Array ( [0] => Name 1 => Age [2] => CGPA ) 1 => Array ( [0] => Alex 1 => 23 [2] => 2.5 ) [2] => Array ( [0] => Bob 1 => 24 [2] => 3 ) [3] => Array ( [0] => Mike 1 => 22 [2] => 3.9 ) )
We need to load data from this array into a datatable created in a YUI module.
So from the understanding I have got from my efforts, I think we can not write our loops or code inside the YUI module block. (Tell me if I am wrong) Otherwise it was easy, I could simply use for loops.
I still know I need to use for loops here. But I am at a loss at how to create the logic since I can not insert my code into the block of code which is the YUI module.
The code is given as follows.
For ease of understanding, I have inserted the data by referring to indices of the given array. It works perfectly fine, but we can not use indices like 0 1 2 3 etc. because we don't know what will be the length of the 2D array and the 1D arrays it contains. So can anybody give me a hand on this?
M.mod_quiz.init_dataTable = function(Y, params) {
var $rowArray= params.key1;
alert($rowArray.length);
/*for (i=0; i<$rowArray.length; i++) {
for (j=0; j<$rowArray[0].length; j++) {
}
}*/
YUI().use("datatable-sort", function(Y) {
var cols = [
/*for (i=0; i<$rowArray.length; i++) {
{key:"Column "+i , label: $rowArray[0][i] , sortable=true}
}*/
{key:"Company", label:$rowArray[0][0], sortable:true},
{key:"Phone", label:$rowArray[0][1], sortable:true},
{key:"Contact", label:$rowArray[0][2], sortable:true}
],
data = [
{Company:$rowArray[1][0], Phone:$rowArray[1][1], Contact:$rowArray[1][2]},
{Company:$rowArray[2][0], Phone:$rowArray[2][1], Contact:$rowArray[2][2]},
{Company:$rowArray[3][0], Phone:$rowArray[3][1], Contact:$rowArray[3][2]}
],
table = new Y.DataTable({
columns: cols,
data : data,
summary: "Contacts list",
caption: "Table with simple column sorting"
}).render(params.key2);
});
};
It looks like you want to create a DataTable object, but your requirement is that the named columns property to the constructor must be dynamic, rather than static. Right?
Just because most every YUI example shows the columns property to be an in-line array, doesn't mean that it must be done that way. You can build it dynamically ...
YUI().use("datatable-sort", function(Y) {
// Build the column definitions dynamically, based on the names in $rowArray[0].
var cols = [];
for (i=0; i<$rowArray[0].length; i++) {
cols.push( { key:$rowArray[0][i], label:$rowArray[0][i], sortable:true } );
}
var data = [
...
As for the JSFiddle that you provided (Thank You!), there is a mix-up of references to Name/Age/CGPA and Company/Phone/Contact. You need to straighten that out. Even after that, the JSFiddle gets an exception, but I'm out of time. You can use your browser's built-in debugger to catch the exception and debug it.

Categories